11 ответов:
http://www.cs.auckland.ac.nz / ~jmor159/PLDS210/qsort3.html имеет некоторый анализ.
кроме того, из Википедии:
самый прямой конкурент quicksort - это куча. Heapsort is как правило, несколько медленнее, чем quicksort, но в худшем случае работает время всегда Θ(nlogn). Быстрой сортировки обычно быстрее, хотя там остается вероятность худшего случая производительности за исключением варианта introsort, которая переключается на heapsort, когда a тяжелый случай обнаружен. Если это известно заранее что heapsort как будет необходимо, используя его непосредственно будет быстрее, чем ждать introsort в переключитесь на него.
Heapsort является o (n log N) гарантированным, что намного лучше, чем в худшем случае в Quicksort. Heapsort не нужно больше памяти для другого массива для размещения упорядоченных данных, как это необходимо для Mergesort. Так почему же коммерческие приложения придерживаются Quicksort? Что у Quicksort есть такое особенное по сравнению с другими реализациями?
Я проверил алгоритмы сам, и я видел, что Quicksort имеет что-то особенное действительно. Он работает быстро, намного быстрее, чем куча и слияние алгоритмы.
секрет Quicksort заключается в том, что он почти не делает ненужных свопов элементов. Своп занимает много времени.
С помощью Heapsort, даже если все ваши данные уже упорядочены, вы собираетесь поменять местами 100% элементов, чтобы упорядочить массив.
С Mergesort, это еще хуже. Вы собираетесь записать 100% элементов в другой массив и записать его обратно в исходный, даже если данные уже упорядочены.
С Quicksort вы не меняете то, что есть уже заказал. Если ваши данные полностью упорядочены,вы почти ничего не меняете! Хотя есть много суеты о худшем случае, небольшое улучшение в выборе pivot, любое другое, чем получение первого или последнего элемента массива, может избежать этого. Если вы получаете поворот от промежуточного элемента между первым, последним и средним элементом, этого достаточно, чтобы избежать наихудшего случая.
то, что превосходит в Quicksort-это не худший случай, а лучший случай! В лучшем случае вы делаете то же самое количество сравнений, хорошо,но вы почти ничего не меняете. В среднем случае вы меняете местами часть элементов, но не все элементы, как в Heapsort и Mergesort. Это то, что дает Quicksort лучшее время. Меньше подкачки, больше скорости.
реализация ниже в C# на моем компьютере, работающем в режиме выпуска, бьет массив.Сортировка на 3 секунды со средним стержнем и на 2 секунды с улучшенным стержнем (да, есть накладные расходы, чтобы получить хороший стержень).
static void Main(string[] args) { int[] arrToSort = new int[100000000]; var r = new Random(); for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length); Console.WriteLine("Press q to quick sort, s to Array.Sort"); while (true) { var k = Console.ReadKey(true); if (k.KeyChar == 'q') { // quick sort Console.WriteLine("Beg quick sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff")); QuickSort(arrToSort, 0, arrToSort.Length - 1); Console.WriteLine("End quick sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff")); for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length); } else if (k.KeyChar == 's') { Console.WriteLine("Beg Array.Sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff")); Array.Sort(arrToSort); Console.WriteLine("End Array.Sort at " + DateTime.Now.ToString("HH:mm:ss.ffffff")); for (int i = 0; i < arrToSort.Length; i++) arrToSort[i] = r.Next(1, arrToSort.Length); } } } static public void QuickSort(int[] arr, int left, int right) { int begin = left , end = right , pivot // get middle element pivot //= arr[(left + right) / 2] ; //improved pivot int middle = (left + right) / 2; int LM = arr[left].CompareTo(arr[middle]) , MR = arr[middle].CompareTo(arr[right]) , LR = arr[left].CompareTo(arr[right]) ; if (-1 * LM == LR) pivot = arr[left]; else if (MR == -1 * LR) pivot = arr[right]; else pivot = arr[middle]; do { while (arr[left] < pivot) left++; while (arr[right] > pivot) right--; if(left <= right) { int temp = arr[right]; arr[right] = arr[left]; arr[left] = temp; left++; right--; } } while (left <= right); if (left < end) QuickSort(arr, left, end); if (begin < right) QuickSort(arr, begin, right); }
для большинства ситуаций, имея быстрый против немного быстрее не имеет значения... вы просто никогда не хотите, чтобы он иногда становился waayyy медленным. Хотя вы можете настроить QuickSort, чтобы избежать медленных ситуаций, вы теряете элегантность основного QuickSort. Так что, для большинства вещей, я на самом деле предпочитаю HeapSort... вы можете реализовать его в полной простой элегантности, и никогда не получите медленный вид.
для ситуаций, когда вы хотите максимальную скорость в большинстве случаев, QuickSort может быть предпочтительнее HeapSort, но ни один из них не может быть правильным ответом. Для критических по скорости ситуаций стоит внимательно изучить детали ситуации. Например, в некоторых из моих критических для скорости кода очень часто данные уже отсортированы или почти отсортированы (это индексирование нескольких связанных полей, которые часто либо перемещаются вверх и вниз вместе, либо перемещаются вверх и вниз друг против друга, поэтому, как только вы сортируете по одному, другие сортируются или сортируются в обратном порядке или закрываются... любой из которых может убить QuickSort). В этом случае я не реализовал ни того, ни другого... вместо этого я реализовал SmoothSort Дейкстры... вариант HeapSort, который является O (N), когда он уже отсортирован или почти отсортирован... это не так элегантно, не слишком легко понять, но быстрый... читайте http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD796a.PDF Если вы хотите что-то более сложное для кода.
быстрая сортировка-heapsort как на месте гибриды очень интересные, слишком, поскольку большинство из них только Н*лог N сравнений в худшем случае (они оптимальны в отношении первого термина асимптотики, поэтому они избежать худших сценариев быстрая сортировка), o(зарегистрируйте N) дополнительное пространство, и они сохраняют по крайней мере "половины" хорошее поведение алгоритма быстрой сортировки по отношению к уже-упорядоченный набор данных. Чрезвычайно интересный алгоритм представлен Дикертом и Вайсом в http://arxiv.org/pdf/1209.4214v1.pdf:
- выберите pivot P в качестве медианы случайной выборки элементов sqrt(n) (это можно сделать не более чем в 24 сравнениях sqrt(n) с помощью алгоритма Tarjan&co или 5 сравнений sqrt(n) с помощью гораздо более запутанного алгоритма spider-factory Schonhage);
- разделите массив на две части, как в первом шаге Quicksort;
- сложите самую маленькую часть и используйте o (log n) дополнительные биты, чтобы кодировать кучу, в которой каждый левый ребенок имеет значение больше, чем его брат;
- рекурсивно извлеките корень из кучи, просейте лакуну, оставленную корнем, пока она не достигнет листа кучи, а затем заполните лакуну соответствующим элементом, взятым из другой части массива;
- повторите над оставшейся неупорядоченной частью массива (если p выбрано в качестве точной медианы, рекурсии вообще нет).
Comp. между
quick sortиmerge sortпоскольку оба типа сортировки на месте, существует разница между временем выполнения wrost case времени выполнения wrost case для быстрой сортировкиO(n^2)а для сортировки кучи это ещеO(n*log(n))и для среднего количества данных быстрая сортировка будет более полезной. Поскольку это рандомизированный алгоритм, поэтому вероятность получения правильного ans. в меньшее время будет зависеть от положения элемента поворота, который вы выберете.так а
хороший призыв: размеры л и г каждое чем 3С / 4
ошибка: один из L и G имеет размер больше, чем 3s/4
для небольшого количества мы можем пойти на сортировку вставки и для очень большого количества данных пойти на сортировку кучи.
хорошо, если вы перейдете на уровень архитектуры...мы используем структуру данных очереди в кэше memory.so то, что когда-либо доступно в очереди, получит sorted.As в быстрой сортировке у нас нет проблем с разделением массива на любую длину...но в сортировке кучи (с помощью массива) может случиться так, что родитель может не присутствовать в под массиве, доступном в кэше, а затем он должен принести его в кэш-память ...что отнимает много времени. Это quicksort лучше всего!!
Heapsort создает кучу, а затем повторно извлекает максимальный элемент. Его худший случай-O (N log n).
но если бы вы увидели худший случай быстрая сортировка, который является O (n2), вы бы поняли, что быстрая сортировка будет не очень хорошим выбором для больших данных.
таким образом, это делает сортировку интересной вещью; я считаю, что причина, по которой так много алгоритмов сортировки живут сегодня, заключается в том, что все они "лучшие" в своих лучших местах. Для например, пузырьковая сортировка может выполнять быструю сортировку, если данные отсортированы. Или если мы знаем что-то о деталях, котор нужно сортировать после этого вероятно мы можем сделать более лучше.
Это может не ответить на ваш вопрос напрямую, думал, что я добавлю свои два цента.
Heapsort имеет преимущество иметь худший запущенный случай O(n * log (n)) поэтому в тех случаях, когда quicksort, вероятно, будет работать плохо (в основном сортируются наборы данных в целом) heapsort является гораздо предпочтительнее.
сортировка кучи-это безопасная ставка при работе с очень большими входными данными. Асимптотический анализ показывает порядок роста кучи в худшем случае
Big-O(n logn), Что лучше, чем QuicksortBig-O(n^2)в худшем случае. Однако,Heapsort несколько медленнее, на практике на большинстве машин, чем хорошо реализована быстрая сортировка. Heapsort также не является стабильным алгоритмом сортировки.причина heapsort медленнее на практике, чем quicksort из-за лучшей локализации Ссылка ( " https://en.wikipedia.org/wiki/Locality_of_reference") в quicksort, где элементы данных находятся в относительно близких местах хранения. Системы, которые демонстрируют сильную локальность ссылки, являются отличными кандидатами для оптимизации производительности. Сортировка кучи, однако, имеет дело с большими скачками. Это делает quicksort более благоприятным для более малых входных сигналов.
для меня существует очень фундаментальное различие между heapsort и quicksort: последний использует рекурсию. В рекурсивных алгоритмах куча растет с числом рекурсий. Это не имеет значения, если n мало, но прямо сейчас я сортирую две матрицы с n=10^9 !!. Программа занимает почти 10 ГБ оперативной памяти, и любая дополнительная память заставит мой компьютер начать замену на виртуальную дисковую память. Мой диск-это RAM-диск, но все же замена на него делает огромная разница в скорости. Поэтому в statpack, закодированном на C++, который включает в себя регулируемые размерные матрицы, с размером, заранее неизвестным программисту, и непараметрическим статистическим видом сортировки я предпочитаю heapsort, чтобы избежать задержек для использования с очень большими матрицами данных.
чтобы ответить на исходный вопрос и обратиться к некоторым другим комментариям здесь:
Я только что сравнил реализации выбора, быстрого, слияния и сортировки кучи, чтобы увидеть, как они будут складываться друг против друга. Ответ заключается в том, что все они имеют свои недостатки.
TL; DR: Быстрый-это лучший вид общего назначения (достаточно быстрый, стабильный и в основном на месте) Лично я предпочитаю сортировку кучи, хотя, если мне не нужна стабильная сортировка.
выбор-N^2-это действительно только для менее чем 20 элементов, потом она превзошла. Если только ваши данные уже не отсортированы, или очень, очень близко к этому. Н^2 становится действительно очень медленно, быстро.
быстрый, по моему опыту, на самом деле не это быстро все время. Бонусы за использование быстрой сортировки в качестве общей сортировки, хотя это достаточно быстро и стабильно. Это также алгоритм на месте, но поскольку он обычно реализуется рекурсивно, он займет дополнительное пространство стека. Это также падает где-то между O(N log n) и O(n^2). Время на некоторых видах, похоже, подтверждает это, особенно когда значения попадают в узкий диапазон. Это намного быстрее, чем сортировка выбора на 10 000 000 элементов, но медленнее, чем слияние или куча.
сортировка слиянием гарантируется O (N log n), поскольку ее сортировка не зависит от данных. Он просто делает то, что он делает, независимо от того, какие ценности вы ему дали. Он также стабилен, но очень большие сорта могут выдуть ваш стек, если вы не будете осторожны реализация. Существуют некоторые сложные реализации сортировки слиянием на месте, но обычно вам нужен другой массив на каждом уровне для слияния ваших значений. Если эти массивы живут в стеке, вы можете столкнуться с проблемами.
сортировка кучи-это max O (N log n), но во многих случаях быстрее, в зависимости от того, как далеко вы должны переместить свои значения в глубокую кучу log n. Куча может быть легко реализована на месте в исходном массиве, поэтому ей не нужна дополнительная память, и она итеративна, поэтому нет беспокоится о переполнении стека при рекурсии. Элемент огромный недостатком сортировки кучи является то, что она не является стабильной, что означает, что это правильно, если вам это нужно.
Comments