Quicksort: Wybór pivot

głosy
94

Przy wdrażaniu Quicksort, jedna z rzeczy, które musisz zrobić, to wybrać pivot. Ale kiedy patrzę na pseudocode jak poniżej, nie jest jasne, w jaki sposób należy wybierać pivot. Pierwszy element listy? Coś innego?

 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))

Czy ktoś może mi pomóc zrozumieć ideę doboru przegub i czy poszczególne scenariusze wymagają różnych strategii.

Utwórz 02/10/2008 o 20:37
źródło użytkownik
W innych językach...                            


13 odpowiedzi

głosy
72

Wybór losowy pivot minimalizuje prawdopodobieństwo, że będzie można napotkać najgorszy O (n 2 ) Wydajność (zawsze wybiera pierwszy lub ostatni spowodowałoby wydajność najgorszy dla prawie posortowane lub prawie-reverse-posortowanych danych). Wybierając element środkowy będzie również dopuszczalne w większości przypadków.

Ponadto, jeśli realizują ten sam, istnieją wersje algorytmu, które pracują na miejscu (bez tworzenia dwóch nowych list, a następnie ich łączenie).

Odpowiedział 02/10/2008 o 20:41
źródło użytkownik

głosy
47

To zależy od wymagań. Wybór czop losowo utrudnia tworzenie zestawu danych, który generuje O (N ^ 2) Wyniki. „Mediana-of-trzy” (pierwsza, ostatnia, środkowa) to również sposób na uniknięcie problemów. Strzeż względnej wydajności porównań, choć; jeśli twoje porównania są kosztowne, a następnie MO3 robi więcej porównań niż wybierając jedną wartość (pivot) w sposób losowy. rekordy bazy danych mogą być kosztowne do porównania.


Aktualizacja: Ciągnięcie komentarzy do odpowiedzi.

mdkess stwierdził:

„Mediana 3” nie jest pierwszym ostatni środkowy. Wybierz trzy losowe indeksów i przyjmują wartość środku tego. Istotą jest, aby upewnić się, że wybór nie jest deterministyczny czopami - jeżeli tak, to najgorsze dane sprawa może być dość łatwo generowane.

Na co odpowiedział:

  • Analiza Find Algorytm Hoare jest z Median-Of-Three rozbiorze (1997) przez P Kirschenhofer, H Prodinger, C Martínez wspiera swoją tezę (że 'mediana-of-trzy' trzy przypadkowe przedmioty).

  • Jest to artykuł opisane w portal.acm.org że chodzi o 'The Worst Case Permutacja dla Mediana-of-Three Quicksort' Hannu Erkiö, opublikowane w komputerze Journal, Vol 27, nr 3, 1984. [Aktualizacja 2012-02- 26: Dostaliśmy tekst dla artykułu . Rozdział 2 „Algorytm” zaczyna: " Stosując medianę pierwszego, środkowego i końcowego elementów [L]: R, wydajne przegrody na części o odpowiednio równej wielkości można uzyskać w większości praktycznych sytuacjach. "Tak, to jest omawianiu podejścia pierwszej średnim ostatni MO3.]

  • Kolejny krótki artykuł, który jest interesujący jest przez MD McIlroy, „zabójca Przeciwnika dla Quicksort” , opublikowanym w Software-praktyk i doświadczeń, Vol. 29 (0) 14 (0, 1999). Wyjaśnia, jak zrobić prawie każdy Quicksort zachowywać kwadratu.

  • AT & T Bell Labs Tech Journal, październik 1984 „Teoria i praktyka w budowie Praca Sortuj Routine” stwierdza „Hoare zaproponował podział wokół mediany kilku losowo wybranych linii. Sedgewick [...] polecany wyborze medianę pierwszy [. ..] ostatni [...], a środkowy”. Oznacza to, że obie techniki „środkowa trojga” są znane w literaturze. (Aktualizacja 23.11.2014: W artykule wydaje się być dostępny w IEEE Xplore lub Wiley - jeśli masz członkostwa lub są przygotowane do uiszczenia opłaty).

  • „Technika rodzajem funkcji” JL Bentley i MD McIlroy, opublikowany w praktyce oprogramowania i doświadczenie, tom 23 (11), listopad 1993, idzie do szerokiej dyskusji na temat problemów, a oni wybrali adaptacyjny algorytm partycjonowania częściowo oparte na wielkość zbioru danych. Istnieje wiele dyskusji na kompromisy dla różnych podejść.

  • Wyszukiwarka Google dla „mediana-of-trzy” działa całkiem dobrze do dalszego śledzenia.

Dzięki za informację; Miałem tylko napotkał deterministyczny „medianę-of-trzy” przed.

Odpowiedział 02/10/2008 o 20:42
źródło użytkownik

głosy
1

Jeśli sortowanie losową dostępną kolekcję (jak tablicy), to ogólnie najlepiej wybrać fizycznej środkowy element. Z tym, jeśli tablica jest gotowy sortowane (lub prawie posortowane), obie partycje będą blisko nawet, a dostaniesz najlepszą prędkość.

Jeśli sortowania coś z dostępem tylko liniowego (jak połączonej z listy), to najlepiej wybrać pierwszą pozycję, ponieważ jest to najszybszy dostęp do poz. Tu jednak, gdy lista jest już posortowane, masz przerąbane - jedna partycja zawsze będzie pusty, a drugi ma wszystko, produkujących najgorszy czas.

Jednak dla połączonego liście, zbierając wszystko oprócz pierwszego, będzie tylko gorsza. To wybrać środkową pozycję w wymienionej liście, trzeba było przejść przez niego na każdym kroku działowej - dodanie O (n / 2), która jest wykonywana operacja logn razy co całkowity czas O (1,5 N * log N) i to, czy wiemy, jak długo jest lista zanim zaczniemy - zwykle nie dlatego, że mają do kroku przez całą drogę, aby je policzyć, to krok w połowie drogi, aby znaleźć w środku, a następnie kroku przez trzeci czas do rzeczywistej strefy: o (2,5 N * log N)

Odpowiedział 02/10/2008 o 20:42
źródło użytkownik

głosy
1

Jest to całkowicie zależne od tego, jak dane są sortowane na początku. Jeśli uważasz, że to będzie pseudolosowych wtedy najlepiej jest albo wybrać losowy wybór lub wybrać pola.

Odpowiedział 02/10/2008 o 20:46
źródło użytkownik

głosy
16

Heh, ja po prostu nauczył tej klasy.

Istnieje kilka opcji.
Proste: Wybierz pierwszego lub ostatniego elementu zakresu. (zły na wejściu częściowo posortowanych) Lepiej: Wybierz element w środku zakresu. (lepiej na wejściu części posortowanej)

Jednakże, zbieranie dowolną elementu ryzykuje słabo podziału tablicy wielkości n w dwóch macierzy o rozmiarze 1 do n-1. Jeśli nie na tyle często, że Twój quicksort ryzykuje coraz O (n ^ 2).

Jedna poprawa Widziałem to wybrać medianę (pierwsza, ostatnia, MID); W najgorszym przypadku, może to jeszcze przejść do O (N ^ 2), ale probabilistycznie, jest to rzadki przypadek.

Dla większości danych, zbieranie pierwsza lub ostatnia jest wystarczająca. Ale jeśli okaże się, że używasz do najgorszych scenariuszy często (częściowo sortowane wejściowych), pierwszą opcją byłoby podnieść wartość środkową (co jest statystycznie dobry pivot dla danych posortowanych częściowo).

Jeśli nadal działa na problemy, a następnie przejść środkową drogę.

Odpowiedział 02/10/2008 o 20:46
źródło użytkownik

głosy
8

Nigdy przenigdy wybrać stałą pivot - to mogą być atakowane wykorzystanie algorytmu za najgorszy O (n ^ 2) wykonania, która jest tylko kłopoty. najgorszym przypadku wykonawcze Quicksort za występuje, gdy podział wyniki jednego elementu tablicy 1, oraz jeden układ n-1 elementów. Załóżmy, że wybrać pierwszy element jako partycji. Jeśli ktoś żywi tablicę do swojego algorytmu, który jest w kolejności malejącej, Twoja pierwsza oś będzie największym, więc wszystko inne w tablicy przesunie się na lewo od niego. Wtedy, kiedy przeszukanie, pierwszy element będzie znowu największy, więc jeszcze raz umieścić wszystko na lewo od niej, i tak dalej.

Lepszą techniką jest metoda mediana-of-3, gdzie można wybrać trzy elementy w sposób losowy, a następnie wybierz pola. Wiesz, że elementem, który wybierzesz nie będzie pierwszą lub ostatnią, ale również przez centralne twierdzenie graniczne, podział elementu środkowego będzie normalnie, co oznacza, że ​​będzie zmierzać w kierunku środka (a zatem n log n czasie).

Jeśli koniecznie chcemy zagwarantować O (nlgn) czas pracy algorytmu, sposób na znalezienie medianę tablicy kolumny-of-5 działa w O (n) czasie, co oznacza, że ​​równanie nawrót do quicksort w najgorszym przypadku będzie zostać T (n) = o (n) (znaleźć mediana) + o (n) (przegroda) + 2T (n / 2) (rekursja w lewo i w prawo). do głównego twierdzenie to o (n log n) , Jednak stały czynnik będzie ogromny, a jeśli najgorsze wydajność sprawa jest Twój główny problem, użyj seryjnej sortowania zamiast, który jest tylko trochę wolniej niż quicksort średnio i gwarantuje O (nlgn) czas (i będzie znacznie szybciej niż tego kulawego mediany quicksort).

Wyjaśnienie mediana median Algorithm

Odpowiedział 25/10/2008 o 22:50
źródło użytkownik

głosy
5

Nie próbuj zbyt mądry i łączyć strategie obrotu. Jeśli połączone medianę 3 z losowym przegubu przez podniesienie mediana pierwszy, ostatni i losowej indeksu w środku, to nadal będziesz podatny na wielu dystrybucjach, które wysyłają mediana 3 kwadratowa (a więc jego faktycznie gorzej niż zwykły losowo przestawny)

Na przykład rozkład organów (1,2,3 ... N / 2..3,2,1) imię i obydwie wynosi 1, a współczynnik losowo będzie pewna liczbę większą niż 1, z medianę daje 1 ( pierwsze lub ostatnie) i masz extermely niesymetrycznego podziału.

Odpowiedział 26/10/2008 o 04:54
źródło użytkownik

głosy
1

Łatwiej jest rozbić quicksort na trzy części w ten sposób

  1. Funkcja Exchange lub element danych Swap
  2. Funkcja przegroda
  3. Przetwarzania partycji

To jest tylko nieznacznie więcej niż jeden długi inefficent funkcji, ale jest o wiele łatwiejsze do zrozumienia.

Kod następująco:

/* 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
  };
};
Odpowiedział 10/03/2011 o 03:19
źródło użytkownik

głosy
0

Idealnie obrotu powinna być wartość środkowa w całym układzie. Zmniejszy to szanse na uzyskanie wydajności najgorszy przypadek.

Odpowiedział 17/04/2013 o 15:57
źródło użytkownik

głosy
-1

W prawdziwie zoptymalizowanej realizacji, sposób wyboru pivot powinna zależeć od wielkości tablicy - na dużej tablicy, to opłaca się poświęcić więcej czasu na wybór dobrego pivot. Nie robiąc pełną analizę, przypuszczam „środku O (log (n)) Elementy” to dobry początek, a to ma dodatkowy bonus nie wymaga żadnej dodatkowej pamięci: Korzystanie tail-call na większej partycji i in- miejsce podziału, używamy tego samego o (log (n)) dodatkowej pamięci na niemal każdym etapie algorytmu.

Odpowiedział 08/10/2013 o 20:50
źródło użytkownik

głosy
0

Sortowanie szybkie złożoność jest bardzo zróżnicowana z doborem wartości obrotu. Na przykład, jeśli zawsze wybrać pierwszy element jako pivot, złożoność algorytmu staje się jak najgorzej jak O (n ^ 2). tutaj jest inteligentny sposób wybrać pivot element- 1. wybrać pierwszą, MID, ostatni element tablicy. 2. porównanie tych trzech liczb i znaleźć liczbę, która jest większa niż jeden i drugi mniejszy niż mediana tj. 3. aby ten element jako element przegubu.

Wybierając obrotu za tym sposobie rozdziela się macierz prawie dwóch pół i co za tym idzie zmniejsza złożoność O (nlog (n)).

Odpowiedział 05/12/2013 o 06:05
źródło użytkownik

głosy
0

Przeciętnie Mediana 3 jest dobrze dla małych n. Mediana 5 jest nieco lepiej większy n. Ninther, który jest „średnia z trzech środkowych trzech” jest jeszcze lepsze dla bardzo dużych n.

Im wyższa jest się z tym lepiej dostać jak n wzrasta próbkowania, ale poprawa dramatycznie spowalnia jak zwiększyć próbek. I ponieść napowietrznej pobierania próbek i sortowanie.

Odpowiedział 19/10/2016 o 10:04
źródło użytkownik

głosy
0

Polecam przy użyciu indeksu środkowy, jak można obliczyć łatwo.

Można go obliczyć przez zaokrąglenie (Array.length / 2).

Odpowiedział 09/08/2017 o 01:29
źródło użytkownik

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