Quicksort: Выбор стержень

голоса
94

При реализации Quicksort, одна из вещей, которые вы должны сделать, это выбрать стержень. Но когда я смотрю на псевдокоде, как показано ниже, не ясно, как я должен выбрать стержень. Первый элемент списка? Что-то другое?

 function quicksort(array)
     var list less, greater
     if length(array) ≤ 1  
         return array  
     select and remove a pivot value pivot from array
     for each x in array
         if x ≤ pivot then append x to less
         else append x to greater
     return concatenate(quicksort(less), pivot, quicksort(greater))

Может кто-то поможет мне понять концепцию выбора оси поворота и действительно ли различные сценарии требуют различных стратегий.

Задан 02/10/2008 в 20:37
источник пользователем
На других языках...                            


13 ответов

голоса
72

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

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

Ответил 02/10/2008 в 20:41
источник пользователем

голоса
47

Это зависит от ваших требований. Выбор шарнира случайным образом делает его более трудным, чтобы создать набор данных, который генерирует O производительность (N ^ 2). «Медиана трое» (первый, последний, средний) также является способ избежать проблем. Остерегайтесь относительных показателей сравнения, хотя; если ваши сравнения являются дорогостоящими, тем Mo3 делает больше сравнений, чем выбор (одно значения поворота) в случайном порядке. записей базы данных может быть дорогостоящим, чтобы сравнить.


Обновление: Натяжение комментариев в ответ.

mdkess утверждал:

«Медиана 3» не первая последняя середина. Выберите три случайных индексов и взять среднее значение этого. Все дело в том, чтобы убедиться, что ваш выбор шарниров не является детерминированным - если это, наихудшие случае данные могут быть довольно легко генерируется.

На что я ответил:

  • Анализ ФИНД алгоритма Хоары медианы-Of-Three Partition (1997) по P Kirschenhofer, H Prodinger, C Мартинес поддерживает ваше утверждение (что 'средний троих' три случайных элементы).

  • Там в статье описано в portal.acm.org , что о «худшем случае Перестановка для Медиана троих Quicksort» Ханну Erkiö, опубликованной в The Computer Journal, Vol 27, № 3, 1984 [Update 2012-02- 26: Есть текст для статьи . Раздел 2 «Алгоритм» начинается: " При использовании медианы первых, средних и последних элементы A [L]: R, эффективных перегородки на части довольно равных размеров может быть достигнут в большинстве практических ситуаций. "Таким образом, он обсуждает первый средний последний mo3 подход.]

  • Еще одна короткой статьи, которая интересна М.Д. Макилра, «убийца Противником для Quicksort» , опубликованной в Software-практике и опыте, Vol. 29 (0), 1-4 (0 , 1999). Это объясняет , как сделать почти любой Quicksort себя квадратично.

  • AT & T Bell Labs Tech Journal, октябрь 1984 «Теория и практика в Строительство Работа Сортировка Рутинное» гласит : «Хора предложили разбиение вокруг медианы нескольких случайно выбранных линий. Седжвик [...] рекомендуется выбирать медиана первого [. ..] последний [...] и средний». Это указывает на то, что оба метода для «медианы троих» известны в литературе. (Update 2014-11-23: В статье , как представляется, имеется в IEEE Xplore или Wiley - если у вас есть членство или готовы платить определенную плату.)

  • «Инженерная Этакая Function» на JL Бентли и MD Макилрой, опубликованной в Software Практика и опыт, Vol 23 (11), ноябрь 1993 года, переходит в широкое обсуждение вопросов, и они выбрали адаптивный алгоритм разделения частично основанный на размер набора данных. Существует много дискуссий компромиссов для различных подходов.

  • Поиск Google для «медианы троих» работает довольно хорошо для дальнейшего отслеживания.

Спасибо за информацию; Я только столкнулся с детерминированной «медианой троих» раньше.

Ответил 02/10/2008 в 20:42
источник пользователем

голоса
1

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

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

Однако, для связанного списка, выбирая что-нибудь кроме первого, будет просто сделать еще хуже. Он выбрать средний элемент в указанном-листе, вы должны пройти через него на каждый шаг разбиения - добавление O (/ 2 N) операцию, которая выполняется LogN раза делая общее время O (1,5 N * N журнала) и что, если мы знаем, как долго этот список, прежде чем мы начнем - обычно мы не так, мы должны были бы уйти весь путь до конца, чтобы сосчитать их, а затем шаг на полпути через, чтобы найти середину, а затем шаг через третий раз, чтобы сделать фактический раздел: O (2,5н * журнал N)

Ответил 02/10/2008 в 20:42
источник пользователем

голоса
1

Это полностью зависит от того, как данные сортируются, чтобы начать с. Если вы думаете, что будет псевдослучайной тогда ваш лучший выбор либо выбрать случайный выбор или выбрать середину.

Ответил 02/10/2008 в 20:46
источник пользователем

голоса
16

Хех, я просто учил этот класс.

Есть несколько вариантов.
Простой: Выберите первый или последний элемент диапазона. (плохо на частично отсортированный входе) Лучше: Выберите элемент в середине диапазона. (лучше на частично отсортированный вводе)

Однако, выбирая любой произвольный элемент рискует плохо разбиение массива размера п в два массив размера 1 и п-1. Если вы достаточно часто, что ваша быстрая сортировка рискует стать O (N ^ 2).

Одно усовершенствование, которое я видел, это выбрать медиану (первый, последний, в середине); В худшем случае, он все еще может перейти к O (N ^ 2), но вероятностно, это редкий случай.

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

Если вы все еще сталкиваетесь с проблемами, затем срединный путь.

Ответил 02/10/2008 в 20:46
источник пользователем

голоса
8

Никогда никогда не выбрать фиксированный стержень - это может быть атакован использовать ваш алгоритм в худшем случае O (N ^ 2) во время выполнения, который просто напрашивается на неприятности. в худшем случае выполнения QuickSort происходит тогда, когда в разбиении результатов в одном массиве из 1 элемента, и один массив из п-1 элементов. Предположим, вы выбрали первый элемент в качестве вашего раздела. Если кто-то питает массив к вашему алгоритму, который в порядке убывания, ваш первый стержень будет самым большим, так что все остальное в массиве будет двигаться слева от него. Затем, когда вы рекурсия, то первый элемент будет самым большим снова, так что еще раз вы положили все, слева от него, и так далее.

Лучший методом является медиана из-3 метод, в котором вы выбираете три элемента случайным образом, и выбрать середину. Вы знаете, что элемент, который вы выбираете не будет первым или последним, но и, по центральной предельной теореме, распределение среднего элемента будет нормально, это означает, что вы будете склонны к середине (и, следовательно, , п Л.Г. п раз).

Если вы абсолютно хотите, чтобы гарантировать O (nlgn) выполнения для алгоритма, метод колонок-из-5 для нахождения медианы массива работает в O (N) времени, что означает, что уравнение рекуррентное для сортировки в худшем случае будет быть Т (п) = о (п) (найти медиану) + о (п) (раздел) + 2T (п / 2) (Recurse влево и вправо.) мастером теоремы, это O (п Л.Г. п) , Тем не менее, постоянный множитель будет огромным, а если худший случай производительность ваша главной задача, использовать слияние сортируемых, что лишь немного медленнее, чем быстрая сортировка в среднем, и гарантирует O (nlgn) время (и будет намного быстрее чем эта хромая срединной сортировка).

Объяснение алгоритма выбора алгоритма

Ответил 25/10/2008 в 22:50
источник пользователем

голоса
5

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

Например, распределительный орган трубы (1,2,3 ... N / 2..3,2,1) первый и последний оба будут 1 и случайный индекс будет некоторое число больше 1, принимая медиану дает 1 ( либо первый или последний), и вы получите extermely несбалансированное разделение.

Ответил 26/10/2008 в 04:54
источник пользователем

голоса
1

Это легче сломать быструю сортировку в три секции делают это

  1. Обмен или элемент данных свопа функция
  2. Статсумма
  3. Обработка разделов

Это лишь немного больше, чем inefficent одной длинной функции, но намного проще понять.

Код следующим образом:

/* This selects what the data type in the array to be sorted is */

#define DATATYPE long

/* This is the swap function .. your job is to swap data in x & y .. how depends on
data type .. the example works for normal numerical data types .. like long I chose
above */

void swap (DATATYPE *x, DATATYPE *y){  
  DATATYPE Temp;

  Temp = *x;        // Hold current x value
  *x = *y;          // Transfer y to x
  *y = Temp;        // Set y to the held old x value
};


/* This is the partition code */

int partition (DATATYPE list[], int l, int h){

  int i;
  int p;          // pivot element index
  int firsthigh;  // divider position for pivot element

  // Random pivot example shown for median   p = (l+h)/2 would be used
  p = l + (short)(rand() % (int)(h - l + 1)); // Random partition point

  swap(&list[p], &list[h]);                   // Swap the values
  firsthigh = l;                                  // Hold first high value
  for (i = l; i < h; i++)
    if(list[i] < list[h]) {                 // Value at i is less than h
      swap(&list[i], &list[firsthigh]);   // So swap the value
      firsthigh++;                        // Incement first high
    }
  swap(&list[h], &list[firsthigh]);           // Swap h and first high values
  return(firsthigh);                          // Return first high
};



/* Finally the body sort */

void quicksort(DATATYPE list[], int l, int h){

  int p;                                      // index of partition 
  if ((h - l) > 0) {
    p = partition(list, l, h);              // Partition list 
    quicksort(list, l, p - 1);        // Sort lower partion
    quicksort(list, p + 1, h);              // Sort upper partition
  };
};
Ответил 10/03/2011 в 03:19
источник пользователем

голоса
0

В идеальном случае стержень должен быть средним значением во всем массиве. Это позволит уменьшить шансы получить худшую производительность случая.

Ответил 17/04/2013 в 15:57
источник пользователем

голоса
-1

В действительно оптимизированной реализации, метод выбора пивот должен зависеть от размера массива - для большого массива, он платит, чтобы провести больше времени, выбирая хорошую точку опоры. Не делая полный анализ, я думаю «середину O (журнал (п)) элементы» является хорошим началом, и это имеет дополнительный бонус, не требуя какую-либо дополнительную памяти: Использование хвоста вызова на раздел большего размера и не- место разделение, мы используем тот же O (журнал (п)) дополнительную память практически на каждом этап алгоритма.

Ответил 08/10/2013 в 20:50
источник пользователем

голоса
0

Сложность быстрой сортировки в значительной степени зависит от выбора величины поворота. Например, если вы всегда можете выбрать первый элемент в качестве опоры, сложность алгоритма становится в худшем случае, как O (N ^ 2). здесь это умный способ выбрать стержень element- 1. выбрать первый, промежуточный, последний элемент массива. 2. сравнить эти три числа и найти число, которое больше, чем один, и меньше, чем другой т.е. медиана. 3. сделать этот элемент в качестве поворотного элемента.

выбирая стержень с помощью этого метода разбивает массив почти две половины и, следовательно, сложность сводится к O (Nlog (п)).

Ответил 05/12/2013 в 06:05
источник пользователем

голоса
0

В среднем, Медиана 3 хороша для малого п. Медиана 5 является немного лучше для большого п. Ninther, который является «средним из трех медиан трех» даже лучше для очень большого п.

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

Ответил 19/10/2016 в 10:04
источник пользователем

голоса
0

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

Вы можете рассчитать его закругление (array.length / 2).

Ответил 09/08/2017 в 01:29
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more