Почему Java 6 Arrays#sort(Object[]) меняется с сортировки слиянием на сортировку вставками для небольших массивов?

Реализация сортировки слиянием Java 6 в Arrays.java использует сортировку вставками, если длина массива меньше некоторого порога. Это значение жестко запрограммировано на 7. Поскольку алгоритм является рекурсивным, в конечном итоге это происходит много раз для большого массива. Канонический алгоритм сортировки слиянием этого не делает, он просто использует сортировку слиянием. вниз, пока в списке не останется только 1 элемент.

Это оптимизация? Если да, то как это должно помочь? А почему 7? Сортировка вставками (даже <=7 вещей) значительно увеличивает количество сравнений, необходимых для сортировки большого массива, поэтому увеличивает стоимость сортировки, где compareTo() вызовы выполняются медленно.

размер массива и количество сравнений для разных значений INSERTIONSORT_THRESHOLD

(ось X — size of array, ось Y — # of comparisons, для разных значений INSERTIONSORT_THRESHOLD)


person Matthew Gilliard    schedule 11.07.2011    source источник
comment
Каков источник этого графика? Вы, кажется, представляете это без каких-либо комментариев   -  person matt b    schedule 11.07.2011
comment
Я построил этот график, отсортировав массив объектов, которые подсчитывают, сколько раз вызывается compareTo, и варьировали INSERTIONSORT_THRESHOLD.   -  person Matthew Gilliard    schedule 11.07.2011
comment
Стоит отметить, что в Java7 также есть Timsort, представляющий собой гибрид слияния и вставки, разработанный Тимом Питерсом для Python. download.java. сеть/jdk7/docs/api/java/util/   -  person Tremmors    schedule 11.07.2011


Ответы (3)


Да это намеренно. Хотя Big-O сортировки слиянием меньше, чем у квадратичных сортировок, таких как сортировка вставками, операции, которые она выполняет, более сложны и, следовательно, медленнее.

Рассмотрим сортировку массива длины 8. Сортировка слиянием делает примерно 14 рекурсивных вызовов самой себе в дополнение к 7 операциям слияния. Каждый рекурсивный вызов вносит нетривиальные накладные расходы во время выполнения. Каждая операция слияния включает в себя цикл, в котором переменные индекса должны быть инициализированы, увеличены и сравнены, временные массивы должны быть скопированы и т. д. Всего можно ожидать более 300 «простых» операций.

С другой стороны, сортировка вставками по своей сути проста и использует около 8 ^ 2 = 64 операций, что намного быстрее.

Подумайте об этом таким образом. Когда вы сортируете список из 10 чисел вручную, используете ли вы сортировку слиянием? Нет, потому что ваш мозг гораздо лучше справляется с такими простыми задачами, как сортировка вставками. Однако, если бы я дал вам год на сортировку списка из 100 000 чисел, вы могли бы быть более склонны к сортировке слиянием.

Что касается магического числа 7, то оно эмпирически выведено как оптимальное.

РЕДАКТИРОВАТЬ: в стандартной сортировке вставки из 8 элементов наихудший сценарий приводит к ~ 36 сравнениям. В канонической сортировке слиянием у вас есть ~ 24 сравнения. С учетом накладных расходов на вызовы методов и сложности операций сортировка вставками должна выполняться быстрее. Кроме того, если вы посмотрите на средний случай, сортировка вставками будет производить гораздо меньше сравнений, чем 36.

person tskuzzy    schedule 11.07.2011
comment
Это объяснение сложности имеет большой смысл интуитивно — хотя я не смог конкретно доказать какое-либо преимущество 7, ›25 имело значение. - person Matthew Gilliard; 11.07.2011
comment
Отредактировал мой ответ. Я не уверен на 100%, что показывают ваши тесты, поскольку ваши оси на самом деле не помечены. - person tskuzzy; 11.07.2011
comment
+1 это имеет большое значение, если, например, вам нужно отсортировать множество небольших массивов. Я ощутил это на своей шкуре. - person Gabi Purcaru; 11.07.2011

Сортировка вставками — n(n-1)/2, а сортировка слиянием — n*(log n с основанием 2).

Учитывая это -

  1. Для массива длины 5 => сортировка вставками = 10, а сортировка слиянием — 11,609.
  2. Для массива длины 6 => сортировка вставками = 15, а сортировка слиянием — 15,509.
  3. Для массива длины 7 => сортировка вставками = 21, а сортировка слиянием — 19,651.
  4. Для массива длины 8 => сортировка вставкой = 28 и сортировка слиянием 24

Из приведенных выше данных видно, что до длины 6 сортировка вставкой выполняется быстрее, а после 7 эффективна сортировка слиянием.

Это объясняет, почему используется 7.

person user1289117    schedule 21.02.2013

Насколько я понимаю, это эмпирически полученное значение, где время, необходимое для сортировки вставками, на самом деле меньше, несмотря на (возможно) большее количество требуемых сравнений. Это связано с тем, что ближе к концу сортировки слиянием данные, скорее всего, будут почти отсортированы, что делает сортировку вставками эффективной.

person dlev    schedule 11.07.2011
comment
Я тоже так догадался. Но когда я провел несколько тестов, я обнаружил, что это не так. Для дешевых операций сравнения любое число, меньшее 20, было примерно эквивалентно, а для дорогостоящих операций сравнения доминирует время сравнения. - person Matthew Gilliard; 11.07.2011
comment
Мэтью: Обратите внимание, что дорогие реализации compareTo, вероятно, не самый частый случай (помните, что библиотека базовых классов Java довольно универсальна и не предназначена конкретно для вашего варианта использования), и с помощью сортировки вставками в небольших подсписках вы также можете сэкономить накладные расходы. для многократного применения алгоритма D&C или сортировки слиянием. - person Joey; 11.07.2011
comment
@Matthew Joey прав в отношении общей цели Java BCL. Еще один момент, который следует отметить, это то, что действительно дорогие методы compareTo(), вероятно, должны быть исправлены, поскольку сравнение двух объектов не должно занимать много времени. Если нет способа избежать этого (возможно, потому, что объекты действительно настолько сложны), возможно, стоит отсортировать набор прокси-объектов по соответствующим критериям (поскольку редко бывает так, что каждый аспект объекта будет учитываются при сортировке.) - person dlev; 11.07.2011
comment
Понятно - у меня нет реальной проблемы, которую я пытаюсь решить ;) Но этот алгоритм используется только для сортировки общих объектов - примитивные массивы обрабатываются по-разному - поэтому неизвестная сложность сравнений должна для рассмотрения, не так ли? - person Matthew Gilliard; 11.07.2011
comment
Как данные почти отсортированы при вызове сортировки вставками? Сортировка слиянием выполняется только на этапе слияния, а вызов рекурсивной сортировки вставками выполняется до этого. - person Paŭlo Ebermann; 11.07.2011