Чому Collections.sort використовує сортування злиття замість швидкого сортування?


101

Ми знаємо, що швидке сортування - це найшвидший алгоритм сортування.

JDK6 collections.sortвикористовує алгоритм сортування злиття замість швидкого сортування. Але Arrays.sort використовує алгоритм швидкого сортування.

З якої причини Collections.sort використовує сортування злиттями замість швидкого сортування?


3
Якщо ви не можете отримати автора JDK відповісти, все, що ви отримаєте, - це здогадки. Не справжнє запитання.
Маркіз Лорнський

4
@EJP Хороший момент, але, безумовно, "Не конструктивний" - це правильна причина закриття. Мені зрозуміло, у чому тут питання.
Duncan Jones

2
Тому що хлопці з Java вирішили це зробити так. Запитайте їх. Тут я не можу отримати законну відповідь. І швидке сортування - не найкраще. Він є найкращим для загального використання .
Адам Арольд

4
Одна здогадка: Quicksort не стабільний, Mergesort є. Для примітивів стабільне / нестабільне сортування не має значення, для об’єктів це може бути (або, принаймні, ви можете отримати помилки, подані проти нестабільного сортування).
parsifal 01.03.13

2
@EJP, ніщо не заважає намірам авторів JDK бути публічними. Після того, як це стане загальнодоступним, нам не потрібно буде відповідати самому автору. Насправді можна отримати відповідь, яка є більш загадковою, навіть без відповіді автора JDK.
Pacerier

Відповіді:


188

Дуже ймовірно від Джоша Блоха § :

Я писав ці методи, тому, мабуть, я кваліфікований для відповіді. Це правда, що не існує єдиного найкращого алгоритму сортування. QuickSort має два основні недоліки у порівнянні зі злиттям:

  1. Він не є стабільним (як зазначив parsifal).

  2. Це не гарантує n n n продуктивності; він може погіршити до квадратичної ефективності на патологічних входах.

Стабільність не є проблемою для примітивних типів, оскільки не існує поняття ідентичності на відміну від (ціннісної) рівності. І можливість квадратичної поведінки було визнано не проблемою на практиці для реалізації Бентелі та Макілроя (або згодом для подвійного поворотного швидкого сортування) ), саме тому ці варіанти QuickSort були використані для примітивних сортів.

Стабільність - велика справа при сортуванні довільних об'єктів. Наприклад, припустимо, у вас є об’єкти, що представляють повідомлення електронної пошти, і ви сортуєте їх спочатку за датою, а потім за відправником. Ви очікуєте, що вони будуть відсортовані за датою в межах кожного відправника, але це буде правдою лише в тому випадку, якщо сортування стабільне. Ось чому ми вирішили надати стабільний сортування (Об’єднання Сортування) для сортування посилань на об'єкти. (Технічно кажучи, кілька послідовних стабільних сортувань призводять до лексикографічного упорядкування ключів у зворотному порядку сортувань: остаточне сортування визначає найбільш значущий підрозділ.)

Приємною побічною перевагою є те, що Merge Sort гарантує n log n (час) продуктивність незалежно від того, який вхід. Звичайно, є і зворотна сторона: швидке сортування - це сортування за місцем: воно вимагає лише журналу n зовнішнього простору (для підтримки стека викликів). Злиття, сортування, з іншого боку, вимагає O (n) зовнішнього простору. Варіант TimSort (представлений в Java SE 6) вимагає значно менше місця (O (k)), якщо вхідний масив майже відсортований.

Також актуальним є наступне :

Алгоритм, який використовується java.util.Arrays.sort та (побічно) java.util.Collections.sort для сортування посилань на об'єкти, є "модифікованим об'єднанням злиття (в якому злиття опускається, якщо найвищий елемент у нижньому підсписку менше, ніж найнижчий елемент у верхньому підсписку). " Це досить швидкий стабільний сорт, який гарантує продуктивність O (n log n) і вимагає O (n) додаткового місця. У свої часи (це було написано в 1997 році Джошуа Блохом) це був чудовий вибір, але сьогодні ми можемо зробити набагато краще.

З 2003 р. У списку Python використовується алгоритм, відомий як timsort (за Тимом Пітерсом, який його написав). Це стабільний, адаптивний, ітераційний метод злиття, який вимагає набагато менше порівнянь з n log (n) при роботі на частково відсортованих масивах, пропонуючи продуктивність, порівнянну з традиційним злиттям, коли працює на випадкових масивах. Як і всі належні злиття, тимсорт стабільний і працює за час O (n log n) (найгірший випадок). У гіршому випадку, timsort вимагає тимчасового місця для зберігання для n / 2 посилань на об'єкти; в кращому випадку для цього потрібно лише невелика постійна кількість місця. Порівняйте це з поточною реалізацією, яка завжди вимагає додаткового місця для n посилань на об'єкти та побиває n log n лише у майже відсортованих списках.

Timsort докладно описаний тут: http://svn.python.org/projects/python/trunk/Objects/listsort.txt .

Оригінальна реалізація Тіма Пітерса написана на C. Джошуа Блох переніс її з C на Java і закінчив тестувати, тестував та широко налаштовував отриманий код. Отриманий код є випадаючою заміною java.util.Arrays.sort. На високовпорядкованих даних цей код може працювати в 25 разів швидше, ніж поточна реалізація (на віртуальній машині сервера HotSpot). За випадковими даними швидкість старої та нової реалізації порівнянна. Для дуже коротких списків нова реалізація значно швидша, ніж стара, навіть для випадкових даних (оскільки це дозволяє уникнути непотрібного копіювання даних).

Також див. Чи використовує Java 7 метод Tim Sort для методів Arrays.Sort?.

Не існує жодного "найкращого" вибору. Як і в багатьох інших речах, йдеться про компроміси.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.