Quicksort: Het kiezen van de pivot

stemmen
94

Bij de uitvoering van Quicksort, één van de dingen die je moet doen is om een ​​spil te kiezen. Maar als ik kijk naar pseudocode als hieronder, is het niet duidelijk hoe ik de spil zou moeten kiezen. Eerste element van de lijst? Iets anders?

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

Kan iemand me helpen het concept te begrijpen van het kiezen van een spil en het al dan niet verschillende scenario's vragen om verschillende strategieën.

De vraag is gesteld op 02/10/2008 om 20:37
bron van user
In andere talen...                            


13 antwoorden

stemmen
72

Het kiezen van een willekeurige pivot minimaliseert de kans dat je worst-case O (n zullen tegenkomen 2 ) prestaties (altijd het kiezen van de eerste of laatste zou worst-case prestaties veroorzaken voor bijna gesorteerde of bijna omgekeerd gesorteerde gegevens). Keuze van middenelement zou ook in de meeste gevallen acceptabel zijn.

Ook als u dit zelf ten uitvoer brengen, zijn er versies van het algoritme dat werkt in-place (dwz zonder creëren van twee nieuwe lijsten en vervolgens aaneenschakelen hen).

antwoordde op 02/10/2008 om 20:41
bron van user

stemmen
47

Het hangt af van uw wensen. Kiezen van een scharnier willekeurig maakt het moeilijker om een ​​dataset die O (N ^ 2) prestaties levert creëren. 'Median-of-three' (eerste, laatste, midden) is ook een manier om problemen te vermijden. Pas op voor relatieve prestaties van vergelijkingen, hoewel; als je vergelijkingen zijn kostbaar, dan Mo3 doet meer vergelijkingen dan het kiezen van (een enkele pivot-waarde) in willekeurige volgorde. Database records kunnen duur zijn om te vergelijken.


Update: Het trekken van opmerkingen in antwoord.

mdkess beweerde:

'Mediaan van 3' is niet de eerste de laatste midden. Kies drie willekeurige indexen, en neem de middelste waarde van deze. Het hele punt is om ervoor te zorgen dat uw keuze van de draaipunten is niet deterministisch - als het is, kan het ergste geval de gegevens vrij eenvoudig worden gegenereerd.

Waarop antwoordde ik:

  • Analyse Van Hoare Vondst algoritme Met Median-of-three Partition (1997) door P Kirschenhofer, H Prodinger, C Martínez ondersteunt uw bewering (dat 'mediaan-of-three' is drie willekeurige items).

  • Er is een artikel beschreven portal.acm.org dat over 'The Worst Case Permutation voor Median-of-Three Quicksort' van Hannu Erkiö, gepubliceerd in The Computer Journal, Vol 27, No 3, 1984. [Update 2012-02- 26: Heb je de tekst voor het artikel . Hoofdstuk 2 'The Algorithm' vangt ' Via de mediaan van de eerste, middelste en laatste elementen van A [L: R], efficiënte wanden elders in tamelijk gelijke afmetingen kan op de meeste praktijksituaties. 'Zo is het bespreken van de eerste-middle-last Mo3 aanpak.]

  • Een andere korte artikel dat interessant is, is door MD McIlroy, "A Killer Tegenstander voor Quicksort" , gepubliceerd in Software-oefening en ervaring, Vol. 29 (0) 1-4 (0 1999). Er wordt uitgelegd hoe je bijna elke Quicksort gedragen kwadratisch.

  • AT & T Bell Labs Tech Journal, oktober 1984 "Theorie en praktijk in de bouw van een Werken Sort Routine" stelt: "Hoare stelde partitionering rond de mediaan van een aantal willekeurig gekozen lijnen. Sedgewick [...] aanbevolen de keuze van de mediaan van de eerste [. ..] laatste [...] en middle". Dit geeft aan dat beide technieken mediaan-of-three 'in de literatuur bekend. (Update 2014/11/23: Het artikel lijkt beschikbaar zijn op IEEE Xplore of van Wiley - als u het lidmaatschap hebben of bereid zijn om een vergoeding te betalen.)

  • 'Engineering Een Sorteren Function' door JL Bentley en MD McIlroy, gepubliceerd in Software Practice and Experience, Vol 23 (11), november 1993 gaat in een uitgebreide bespreking van de problemen, en ze kozen een adaptief partitionering algoritme gedeeltelijk gebaseerd op de grootte van de dataset. Er is veel discussie over trade-offs voor diverse benaderingen.

  • Een Google-zoekopdracht voor 'mediaan-of-three' werkt vrij goed voor verdere tracking.

Bedankt voor de informatie; Ik had alleen in aanraking met de deterministische 'mediaan-of-three' voor.

antwoordde op 02/10/2008 om 20:42
bron van user

stemmen
1

Als u het sorteren van een willekeurig toegankelijke collectie (zoals een array), is het algemene tips om de fysieke middelste punt opraapt. Met dit, als de array is al klaar gesorteerd (of bijna gesorteerde), de twee partities in de buurt te zijn, zelfs, en je zult de beste snelheid te krijgen.

Als je iets met alleen lineaire toegang (zoals een gekoppelde-list) worden sorteren, dan is het het beste om het eerste item te kiezen, want het is de snelste punt om toegang te krijgen. Hier, echter, als de lijst al wordt gesorteerd, je bent geschroefd - één partitie zal altijd nul zijn, en de andere hebben alles, het produceren van de slechtste tijd.

Echter, voor een gekoppelde-list, het plukken van iets anders dan de eerste, zal alleen erger maken. Het halen van de middelste punt in een monumentaal-lijst, zou je moeten om door het op elke partitie stap - het toevoegen van een O (N / 2) operatie die logn keer gebeurt het maken van de totale tijd O (1,5 N * log N) en dat is als we weten hoe lang de lijst is voordat we beginnen - meestal doen we niet dus we zouden moeten stap helemaal door te tellen, dan stap halverwege naar het midden te vinden, dan stap voor stap door een derde keer om de werkelijke verdeling te doen: O (2,5 N * log N)

antwoordde op 02/10/2008 om 20:42
bron van user

stemmen
1

Het is volledig afhankelijk van hoe uw gegevens worden gesorteerd om mee te beginnen. Als je denkt dat het zal pseudo-random zijn dan is uw beste inzet is om ofwel kies een willekeurige selectie of kies het midden.

antwoordde op 02/10/2008 om 20:46
bron van user

stemmen
16

Heh, ik heb net geleerd deze klasse.

Er zijn verschillende opties.
Eenvoudig: Kies de eerste of laatste element van de reeks. (bad van gedeeltelijk gesorteerde invoer) Beter: Zoek het onderdeel in het midden van het bereik. (better op gedeeltelijk gesorteerde invoer)

Echter, het plukken elk willekeurig element loopt het risico van slecht partitioneren van de matrix van grootte n in twee reeksen van grootte 1 en n-1. Als je dat vaak genoeg, je quicksort loopt het risico om O (n ^ 2).

Een verbetering die ik heb gezien is pick mediaan (eerste, laatste, midden); In het ergste geval kan het nog steeds naar O (n ^ 2), maar probabilistisch, dit is een zeldzaam geval.

Voor de meeste gegevens, het kiezen van de eerste of laatste is voldoende. Maar als u vindt dat u in werking stelt in worst case scenario's vaak (deels naargelang input), zou de eerste optie zijn om de centrale waarde (dat is een statistisch goed draaipunt voor het gedeeltelijk gesorteerde gegevens) te plukken.

Als je nog steeds actief is in de problemen, ga dan de mediaan route.

antwoordde op 02/10/2008 om 20:46
bron van user

stemmen
8

Nooit kiezen voor een vast scharnier - dit kan worden aangevallen om uw algoritme slechtste geval O (n ^ 2) runtime, die net is vragen om problemen te benutten. Quicksort slechtste geval runtime optreedt wanneer partitioneren resulteert in een reeks van 1-element, en een reeks van n-1 elementen. Stel dat u kiest voor de eerste element als uw partitie. Als iemand een array om uw algoritme dat in afnemende volgorde feeds, zal uw eerste scharnier de grootste te worden, dus alles anders in de array zal verplaatsen naar de linkerkant van het. Dan wanneer u recurse, zal het eerste element de grootste weer, dus eens te meer je alles te maken aan de linkerkant van het, en ga zo maar door.

Een betere techniek is de mediaan-of-3 methode, waar u drie elementen halen willekeurig, en kies het midden. U weet dat het element dat u kiest zal niet de eerste of de laatste, maar ook door de centrale limietstelling, de verdeling van de middelste element zal normaal zijn, wat betekent dat je de neiging zal hebben naar het midden (en dus , n lg n tijd).

Als u absoluut wilt O (NLGN) runtime garanderen het algoritme, de kolommen-van-5 methode voor het vinden van de mediaan van een reeks loopt in O (n) tijd, waardoor herhaling vergelijking voor quicksort in het ergste geval zal worden T (n) = O (n) (Zoek de mediaan) + O (n) (partitie) + 2T (n / 2) (recurse links en rechts.) Aan Meester stelling, is O (n lg n) . Toch zal de constante factor enorm zijn, en als ergste geval de prestaties van uw primaire zorg, gebruik dan een merge soort plaats, dat is slechts een klein beetje langzamer dan quicksort gemiddeld, en staat garant voor O (NLGN) tijd (en zal veel sneller dan dit lame mediaan quicksort).

Toelichting op de mediaan van Meden Algorithm

antwoordde op 25/10/2008 om 22:50
bron van user

stemmen
5

Probeer niet en krijg te slim en te combineren scharnierende strategieën. Als u gecombineerde mediaan van 3 met willekeurige spil door het kiezen van de mediaan van de eerste, de laatste en een willekeurige index in het midden, dan zul je nog steeds kwetsbaar voor veel van de met een mediane sturen van 3 kwadratisch (dus het is eigenlijk nog erger dan zijn plain willekeurige pivot)

Bijvoorbeeld een buis orgaanverdeling (1,2,3 ... N / 2..3,2,1) voor- en zullen beide 1 en de willekeurige index een getal groter dan 1, waarbij de mediaan geeft 1 ( de eerste of de laatste) en je krijgt een extreem onevenwichtige verdeling.

antwoordde op 26/10/2008 om 04:54
bron van user

stemmen
1

Het is makkelijker om de quicksort breken in drie delen om dit te doen

  1. Exchange of data uitwisselen elementfunctie
  2. De verdelingsfunctie
  3. Het verwerken van de scheidingswanden

Het is slechts iets meer inefficent dan één lange functie, maar is veel gemakkelijker te begrijpen.

Code volgt:

/* 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
  };
};
antwoordde op 10/03/2011 om 03:19
bron van user

stemmen
0

Idealiter moet het draaipunt van de middelste waarde in het gehele array. Dit zal de kans op het krijgen ergste geval de prestaties te verminderen.

antwoordde op 17/04/2013 om 15:57
bron van user

stemmen
-1

In een werkelijk optimale implementatie, moet de methode voor het kiezen van pivot zijn afhankelijk van de array size - voor een groot array, het loont om meer tijd te besteden kiezen van een goede pivot. Zonder het doen van een volledige analyse, zou ik denk dat "middle of O (log (n)) elementen" is een goed begin, en dit heeft de toegevoegde bonus van niet vereist geen extra geheugen: Met behulp van tail-call op de grotere partitie en in- plaats partitionering, gebruiken we dezelfde O (log (n)) extra geheugen op bijna elke fase van het algoritme.

antwoordde op 08/10/2013 om 20:50
bron van user

stemmen
0

complexiteit Snel sorteren's varieert sterk met de selectie van pivot waarde. bijvoorbeeld als u altijd kiezen voor eerste element als een spil, complexiteit algoritme wordt als slechtst als O (n ^ 2). Hier is een intelligente methode te kiezen pivot element- 1. Kies de eerste, middelste laatste element van de array. 2. Vergelijk de volgende drie nummers en vind het nummer dat groter is dan één en kleiner dan andere ie mediaan. 3. maken dat element scharnierelement.

kiezen van de scharnier volgens deze methode splitst de array in bijna twee halve en daarmee vermindert de complexiteit O (NLOG (n)).

antwoordde op 05/12/2013 om 06:05
bron van user

stemmen
0

Op het gemiddelde, Mediaan van 3 is goed voor kleine n. Mediaan van 5 een beetje beter voor grotere n. De ninther, dat is de "mediaan van drie medianen van de drie" is nog beter voor zeer grote n.

Hoe hoger je gaat met de bemonstering van de beter je als n toeneemt, maar de verbetering vertraagt ​​drastisch naar beneden terwijl je de monsters te verhogen. En u maakt de overhead van bemonstering en sorteren van monsters.

antwoordde op 19/10/2016 om 10:04
bron van user

stemmen
0

Ik raad het gebruik van het midden-index, als het gemakkelijk kan worden berekend.

U kunt het berekenen door afronding (Array.length / 2).

antwoordde op 09/08/2017 om 01:29
bron van user

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