Быстрое превосходство над кучей сортировки - PullRequest
37 голосов
/ 05 декабря 2009

Сортировка кучи имеет наихудший уровень сложности O(nlogn), тогда как для быстрой сортировки O(n^2). Но эмпирические свидетельства говорят, что быстрая сортировка лучше. Почему это так?

Ответы [ 5 ]

49 голосов
/ 05 декабря 2009

Одним из основных факторов является то, что быстрая сортировка имеет лучшую локальность ссылки - следующая вещь, к которой нужно обращаться, обычно близка по памяти к тому, на что вы только что смотрели. В отличие от heapsort прыгает значительно больше Так как вещи, которые находятся близко друг к другу, вероятно, будут кэшироваться вместе, быстрая сортировка будет быстрее.

Однако быстродействие в худшем случае для быстрой сортировки значительно хуже, чем для Heapsort. Поскольку для некоторых критически важных приложений требуются гарантии быстродействия, для таких случаев рекомендуется использовать heapsort.

16 голосов
/ 15 февраля 2015

Heapsort гарантированно O (N log N), что намного лучше, чем наихудший случай в быстрой сортировке. Heapsort не нужно больше памяти для размещения в другом массиве упорядоченных данных, как это требуется Mergesort. Так почему же коммерческие приложения придерживаются Quicksort? Что в Quicksort настолько особенное по сравнению с другими реализациями?

Я сам протестировал алгоритмы и увидел, что в Quicksort действительно есть что-то особенное. Он работает быстро, намного быстрее, чем алгоритмы Heap и Merge.

Секрет быстрой сортировки заключается в следующем: он почти не делает ненужных перестановок элементов. Своп требует много времени.

С Heapsort, даже если все ваши данные уже упорядочены, вы собираетесь поменять 100% элементов, чтобы упорядочить массив.

С Mergesort это еще хуже. Вы собираетесь записать 100% элементов в другой массив и записать его обратно в исходный, даже если данные уже упорядочены.

С помощью быстрой сортировки вы не меняете уже заказанное. Если ваши данные полностью упорядочены, вы почти ничего не поменяете! Хотя в худшем случае много шума из-за небольшого улучшения выбора pivot, кроме получения первого или последнего элемента массива, его можно избежать. Если вы получаете опору от промежуточного элемента между первым, последним и средним элементом, достаточно избежать наихудшего случая.

То, что превосходит Quicksort, - это не худший случай, но лучший случай! В лучшем случае вы делаете такое же количество сравнений, хорошо, но вы почти ничего не меняете. В среднем случае вы меняете часть элементов, но не все элементы, как в Heapsort и Mergesort. Вот что дает Quicksort лучшее время. Меньше своп, больше скорости.

Приведенная ниже реализация в C # на моем компьютере, работающая в режиме выпуска, превосходит Array.Sort по 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);
}
6 голосов
/ 05 декабря 2009

Вот пара объяснений:

http://www.cs.auckland.ac.nz/software/AlgAnim/qsort3.html

http://users.aims.ac.za/~mackay/sorting/sorting.html

По сути, даже если наихудший вариант для быстрой сортировки - O (n ^ 2), он в среднем будет работать лучше. : -)

1 голос
/ 31 июля 2010

Обозначение big-O означает, что время, необходимое для сортировки n элементов, ограничено выше функцией c*n*log(n), где c - некоторый неопределенный постоянный коэффициент. Нет никаких причин, по которым константа c должна быть одинаковой для quicksort и heapsort. Итак, реальный вопрос: почему вы ожидаете, что они будут одинаково быстрыми?

Quicksort всегда был несколько быстрее, чем heapsort на практике, но в последнее время разница стала больше, поскольку, как упоминалось ранее, локальность доступа к памяти стала настолько важной для скорости выполнения.

0 голосов
/ 05 декабря 2009

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

...