Quicksort: Velge pivot

stemmer
96

Ved implementering av Quicksort er en av de tingene du trenger å gjøre for å velge en pivot. Men når jeg ser på pseudokode som det nedenfor, er det ikke klart hvordan jeg bør velge pivot. Første elementet på listen? Noe annet?

 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 noen hjelpe meg å forstå konseptet med å velge en pivot og hvorvidt ulike scenarier krever ulike strategier.

Publisert på 02/10/2008 klokken 19:37
kilden bruker
På andre språk...                            


13 svar

stemmer
75

Velge en tilfeldig pivot minimerer sjansen for at du vil støte på worst-case O (n 2 ) ytelse (alltid velge første eller siste ville føre worst-case ytelse for nesten-sortert eller nesten-reverse-sorterte data). Velge midten element vil også være akseptabelt i de fleste tilfeller.

Også, hvis du gjennomfører dette selv, det finnes versjoner av algoritmen som arbeider på stedet (dvs. uten å skape to nye lister, og deretter sette sammen dem).

Svarte 02/10/2008 kl. 19:41
kilden bruker

stemmer
50

Det avhenger av dine behov. Valg av en dreietapp tilfeldig gjør det vanskeligere å skape et datasett som genererer O (N ^ 2) ytelse. 'Median-av-tre' (første, siste, i midten) er også en måte å unngå problemer. Vokt dere for relative ytelsen sammenligninger, skjønt; hvis sammenligninger er kostbare, så Mo3 gjør flere sammenligninger enn å velge (et enkelt dreie verdi) tilfeldig. Databaseposter kan være kostbart å sammenligne.


Oppdatering: Trekke kommentarer i svaret.

mdkess hevdet:

'Median av 3' er ikke første forrige midten. Velg tre tilfeldige indekser, og ta den midterste verdien av dette. Hele poenget er å sørge for at ditt valg av svinger er ikke deterministisk - hvis det er, kan verste fall dataene ganske enkelt genereres.

Som jeg svarte:

  • Analyse av Hoare oss finne algoritme Med Median-Of-Three Partition (1997) av P Kirschenhofer, H Prodinger, støtter C Martínez din påstand (at 'median-av-tre' er tre tilfeldige elementer).

  • Det er en artikkel beskrevet på portal.acm.org som handler om 'The Worst case Permutasjon for Median-of-Three Quicksort' av Hannu Erkiö, publisert i The Computer Journal, Vol 27, No 3, 1984. [Update 2012-02- 26: Fikk teksten for artikkelen . Seksjon 2 'algoritmen' begynner: ' Ved å benytte den midlere av de første, midtre og siste elementer av A [L: R], effektive skillevegger inn i deler av forholdsvis like store størrelser kan oppnås i de fleste praktiske situasjoner. 'Det er således diskuterer den første middel siste Mo3 tilnærming.]

  • Et annet kort artikkel som er interessant er av MD McIlroy, "A Killer Satan for Quicksort" , publisert i Software-praksis og erfaring, vol. 29 (0), 1-4 (0 1999). Den forklarer hvordan du gjøre nesten alle Quicksort oppfører kvadratisk.

  • AT & T Bell Labs Tech Journal, oktober 1984 "Teori og praksis i bygging av en fungerende Sorter Routine" stater "Hoare foreslo partisjonering rundt medianen av flere tilfeldig utvalgte linjer. Sedgewick [...] anbefales å velge medianen av den første [. ..] siste [...] og middels". Dette indikerer at begge teknikker for 'median-av-tre' er kjent i litteraturen. (Oppdater 2014-11-23: Artikkelen ser ut til å være tilgjengelig på IEEE Xplore eller fra Wiley - hvis du har medlemskap eller er forberedt på å betale en avgift.)

  • 'Engineering en sorteringsfunksjon' av JL Bentley og MD McIlroy, publisert i Software praksis og erfaring, Vol 23 (11), november 1993, går inn i en omfattende diskusjon av problemene, og de valgte en adaptiv partisjone algoritme delvis basert på størrelsen til datasettet. Det er mye diskusjon om avveininger for ulike tilnærminger.

  • Et Google-søk på 'median-av-tre' fungerer ganske bra for videre sporing.

Takk for informasjonen; Jeg hadde bare møtt den deterministiske 'median-av-tre' før.

Svarte 02/10/2008 kl. 19:42
kilden bruker

stemmer
1

Hvis du sorterer en tilfeldig brukere samling (som en array), er det generelt best å plukke den fysiske midten elementet. Med dette, hvis matrisen er alle klare sorteres (eller nesten sortert), de to partisjoner vil være nær selv, og du får den beste hastigheten.

Hvis du sorterer noe med bare lineær tilgang (som en linket-listen), så er det best å velge det første elementet, fordi det er den raskeste element for å få tilgang. Her, derimot, hvis listen er allerede sortert, du er ødelagt - en partisjon vil alltid være null, og den andre har alt, produsere den verste tiden.

Men for en koblet-liste, plukke noe annet enn den første, vil bare gjøre vondt verre. Det plukke den midterste element i en liste som er oppført-, ville man måtte gå gjennom det på hver partisjon trinn - å legge et O (N / 2) operasjon som utføres LOGN ganger, noe som gjør total tid O (1,5 N * log N) og det er hvis vi vet hvor lang listen er før vi begynner - som regel vi ikke, så vi måtte gå hele veien gjennom å telle dem, så går halvveis gjennom for å finne midten, og deretter gå gjennom en tredje gang for å gjøre selve skilleveggen: O (2,5N * log N)

Svarte 02/10/2008 kl. 19:42
kilden bruker

stemmer
1

Det er helt avhengig av hvordan dataene er sortert til å begynne med. Hvis du tror det vil være pseudo-tilfeldig så din beste alternativet er å enten velge et tilfeldig utvalg eller velg midten.

Svarte 02/10/2008 kl. 19:46
kilden bruker

stemmer
16

Heh, jeg bare lært denne klassen.

Det er flere alternativer.
Enkelt: Plukk den første eller siste del av serien. (dårlig på delvis sortert inngang) Bedre: Plukk elementet i midten av serien. (bedre på delvis sortert inngang)

Imidlertid plukke vilkårlig element løper risikoen for dårlig fordeling av matrise av størrelse n i to rekker av størrelse 1 og n-1. Hvis du gjør det ofte nok, kjører din quicksort risikoen for å bli O (n ^ 2).

En forbedring jeg har sett er plukke median (fornavn, etternavn midten); I verste fall kan det likevel gå til O (n ^ 2), men på sannsynlighets, er dette et sjeldent tilfelle.

For de fleste data, plukke den første eller siste er tilstrekkelig. Men, hvis du finner ut at du kjører inn i verst tenkelige scenarier ofte (delvis sortert input), ville det første alternativet være å plukke den sentrale verdi (som er en statistisk god rotasjon for delvis sorterte data).

Hvis du fortsatt kjører på problemer, så gå median ruten.

Svarte 02/10/2008 kl. 19:46
kilden bruker

stemmer
8

Aldri noensinne velge en fast pivot - dette kan bli angrepet for å utnytte din algoritme verste fall O (n ^ 2) runtime, som er bare å be om trøbbel. Quicksort verste fall kjøretidsforekommer ved en oppdeling resulterer i en matrise av en element, og en rekke med n-1 elementer. Tenk deg at du velger det første elementet som partisjon. Hvis noen mater en rekke til din algoritme som er i synkende rekkefølge, vil ditt første dreie være den største, så alt annet i rekken vil flytte til venstre for det. Så når du recurse, vil det første elementet bli størst igjen, så nok en gang du setter alt til venstre for den, og så videre.

En bedre teknikk er median-av-tre-metoden, der du plukker opp tre elementer tilfeldig, og velg midten. Du vet at elementet som du velger ikke vil være den første eller den siste, men også ved å sentralgrensesetningen, vil fordelingen av midtelementet være normal, noe som betyr at du vil tendere mot midten (og dermed , n lg n tid).

Hvis du absolutt ønsker å garantere O (nlgn) runtime for algoritmen, den kolonner-of-5 metode for å finne medianen av en rekke kjører i O (n) tid, noe som betyr at tilbakefall ligningen for quicksort i verste fall vil være T (n) = O (n) (en median) + O (n) (skillevegg) + 2T (n / 2) (recurse venstre og høyre.) Ved Master sats, er denne O (n lg n) . Imidlertid vil konstant faktor være enorme, og hvis worst case ytelse er din primære bekymring, bruke en flettesortering i stedet, som er bare litt tregere enn quicksort i gjennomsnitt, og garanterer O (nlgn) tid (og vil være mye raskere enn dette halt median quicksort).

Forklaring av medianen av Medianer algoritme

Svarte 25/10/2008 kl. 21:50
kilden bruker

stemmer
5

Ikke prøv og bli for flink og kombinere sving strategier. Hvis du kombinert median på 3 med tilfeldig pivot ved å plukke medianen av den første, siste og en tilfeldig indeks i midten, så vil du fortsatt være sårbare for mange av de fordelinger som sender median på 3 kvadratisk (så det er faktisk verre enn vanlig tilfeldig dreie)

F.eks et rør organ fordeling (1,2,3 ... N / 2..3,2,1) første og siste vil begge være en og den tilfeldige indeksen vil være noen tall større enn 1, idet den midlere gir 1 ( enten første eller siste) og få deg en setting ubalansert partisjonering.

Svarte 26/10/2008 kl. 03:54
kilden bruker

stemmer
1

Det er lettere å bryte quicksort i tre seksjoner gjør dette

  1. Utveksling eller bytte dataelement funksjon
  2. Skilleveggen funksjon
  3. Behandler partisjonene

Det er bare litt mer inefficent enn en lang funksjon, men er mye enklere å forstå.

Kode følger:

/* 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
  };
};
Svarte 10/03/2011 kl. 02:19
kilden bruker

stemmer
0

Ideelt svinge bør være den midterste verdien i hele matrisen. Dette vil redusere sjansene for å få worst case ytelse.

Svarte 17/04/2013 kl. 14:57
kilden bruker

stemmer
-1

I en virkelig optimalisert gjennomføring, skal metoden for valg av pivot avhenge array størrelse - for et stort utvalg, det lønner seg å bruke mer tid på å velge en god pivot. Uten å gjøre en fullstendig analyse, vil jeg gjette "midt i O (log (n)) elementer" er en god start, og dette har fordelen av å ikke kreve noen ekstra minne: Bruk hale samtale på større partisjon og in- sted partisjonering, bruker vi samme O (log (n)) ekstra minne på nesten hvert trinn av algoritmen.

Svarte 08/10/2013 kl. 19:50
kilden bruker

stemmer
0

Hurtig slags kompleksitet varierer med valg av pivot verdi. for eksempel hvis man velger alltid første element som en dreietapp, blir algoritmen kompleksitet som verste som O (n ^ 2). her er en smart metode for å velge dreie element- 1. velge det første, mid, siste element i matrisen. 2. sammenligne disse tre tallene og finne nummeret som er større enn en og mindre enn andre dvs median. 3. gjøre dette element som svingeelement.

valg av svinge ved denne metode deler oppstillingen i nesten to halve og dermed kompleksiteten reduseres til O (nlog (n)).

Svarte 05/12/2013 kl. 05:05
kilden bruker

stemmer
0

I gjennomsnitt Median av tre er bra for små n. Median av 5 er litt bedre for større n. Den ninther, som er "median av tre medianer av tre" er enda bedre for veldig store n.

Jo høyere du går med prøvetaking jo bedre du får som n øker, men forbedringen dramatisk bremser ned når du øker prøvene. Og du pådrar overhead av prøvetaking og sortering prøver.

Svarte 19/10/2016 kl. 10:04
kilden bruker

stemmer
0

Jeg anbefaler å bruke midten indeksen, som det kan beregnes enkelt.

Du kan regne det ut ved å runde (tabell.length / 2).

Svarte 09/08/2017 kl. 01:29
kilden bruker

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