Quicksort Lars Vidar Magnusson 29.1.2014 Kapittel 7 Quicksort Randomisert Quicksort Analyse av Quicksort
Om Quicksort Quicksort er en svært populær sorteringsalgoritme. Algoritmen har i verstefall en kjøretid som er Θ(n 2 ). Forventet kjøretid er derimot Θ(n log n). Algoritmen har lave konstante kostnader. Algoritmen sorterer in-place.
Beskrivelse av Quicksort Quicksort er en rekursiv algoritme, så den faller inn under divide-and-conquer paradigmen. For å sortere en delarray A[p... r]. Divide: Del opp A[p... r] i to (muligens tomme) delarrays A[p... q 1] og A[q + 1... r] slik at A[p... q 1] A[q] og A[q + 1... r] A[q]. Conquer: Sorter delarrayene med to rekursive kall til Quicksort. Combine: Kombineringssteget er overflødig da A[p... r] er sortert etter de rekursive kallene.
Partition Algoritmen Divide steget i Quicksort blir tatt hånd om av Partition algoritmen. Partition fungerer ved å velge et pivot element og så dele inn A[p... r] slik at A[p... q 1] A[q] og A[q + 1... r] A[q].
Hvordan Partition Fungerer Partition bruker alltid siste element som pivot element. I tillegg til vil vi under kjøring alltid ha tre (mulig tomme) deler. A[r] = x er pivot elementet A[p... i] inneholder elementer som er mindre eller lik x A[i + 1... j 1] inneholder elementer som er større enn x A[j... r 1] inneholder elementer som ikke er partisjonert ennå
Hvordan Partition Fungerer Diagrammene under viser hvordan Partition utvider delarrayen utifra om A[j] er mindre eller større enn A[r].
Hvordan Partition Fungerer Diagrammene under viser hvordan Partition kjører på et konkret eksempel.
Korrektheten til Partition Vi kan bruke de fire ulike delarrayene som loop invariant hvis vi vil bevise korrektheten til Partition. Initialization: Før løkken starter er A[r] pivot og A[p... i] og A[i + 1... j 1] er tomme så loop invarianten er ivaretatt. Mantenance: Når løkken kjører, hvis A[j] pivot så byttes A[i + 1] med A[j] og både i og j økes, så da er loop invarianten ivaretatt. Termination: Algoritmen slutter når j = r, som beviser korrektheten til algoritmen.
Heapsort Algoritmen Med hjelpefunksjonen Partition på plass kan vi endelig presentere Quicksort algoritmen. Det første kallet til algoritmen er Quicksort(A, 1, n).
Kjøretiden til Quicksort Kjøretiden til Quicksort avhenger av hvorvidt rekursjonstreet er balansert eller ikke. Når treet er balansert er algoritmen Θ(n log n), som Merge-Sort og Heapsort Når treet er ubalansert er algoritmen Θ(n 2 ), som Insertion-Sort
Intuisjon for Verstefall Kjøretid av Quicksort Verstefall kjøretid for Quicksort forekommer når rekursjonstreet er helt ubalansert. Det ene barnet får n 1 elementer og det andre ingen. Vi får følgende recurrence ligning T (n) = T (n 1) + T (0) + Θ(n) = T (n 1) + Θ(n) = Θ(n 2 ) Dette gir samme kjøretid som Insertion-Sort, men ironisk nok så forekommer det på samme input som bestefall input for Insertion-Sort.
Intuisjon for Bestefall Kjøretid av Quicksort Bestefall kjøretid for Quicksort forekommer når rekursjonstreet er helt balansert. Hver delarray blir da ha n/2 elementer. Vi får følgende recurrence ligning T (n) = 2T (n/2) + Θ(n) = Θ(n log n) Dette gir samme kjøretid som Merge-Sort og Heapsort, så forskjellen mellom verstefall og bestefall er stor.
Balansert Partisjonering Gjennomsnittskjøretiden til Quicksort er mye nærmere bestefall enn verstefall. For å illustrere hvorfor dette er tilfellet kan vi se på et eksempel hvor vi har en nokså skjev deling på hvert nivå e.g. 9-til-1. T (n) = T (9n/10) + T (n/10) + Θ(n) = Θ(n log n) Uansett hvor ille inndelingen er vil dette gi Θ(n log n) så lenge inndelingen er en konstant.
Balansert Partisjonering Diagrammet under viser rekursjonstreet for en 9-til-1 konstant split.
Intuisjon for Gjennomsnitt Kjøretid Inndelingen i rekursjonstreet vil i praksis ikke ha en konstant inndeling. Det vil normalt være en miks mellom gode og dårlige oppdelinger Diagrammet under viser alternerende verstefall og bestefall, og bestefall. Det vil bare skille en konstant mellom de to i.e. den asymptotiske kjøretiden vil ikke påvirkes.
Randomized-Quicksort Algoritmen Det kan være fordelaktig å randomisere Quicksort for å forsikre oss om at alle input har samme sansynlighet. Vi ender da opp med Randomized-Quicksort. Denne algoritmen er lik Quicksort bortsett fra kallet til Randomized-Partition.
Randomized-Partition Algoritmen Vi kunne ha funnet en tilfeldig permutasjon av input, men dette ville økt konstantleddet i algoritmen. Vi randomiserer heller algoritmen ved å bruke tilfeldig utvalg (random selection). I stedet for å alltid bruke siste element som pivot, velger vi pivot tilfeldig. Dette oppnår samme effekt som å finne en tilfeldig permutasjon.
Bevis for Verstefall Kjøretid Vi skal bevise at verstefall kjøretid for Quicksort er O(n 2 ). T (n) = max (T (q) + T (n q 1)) + Θ(n) 0 q n 1 Vi gjetter på at T (n) cn 2 for en eller annen c > 0 og setter inn i ligningen over. T (n) max 0 q n 1 (cq2 + c(n q 1) 2 ) + Θ(n) = c max 0 q n 1 (q2 + (n q 1) 2 ) + Θ(n) Maksimum av (q 2 + (n q 1) 2 ) intreffer når q er enten 0 eller n 1, så da får vi Og derfor har vi at max 0 q n 1 (q2 + (n q 1) 2 ) (n 1) 2 T (n) cn 2 c(2n 1) + Θ(n) cn 2 = n 2 2n + 1 så lenge c(2n 1) Θ(n)
Gjennomsnittsanalyse av Randomized-Quicksort For å gjøre en gjennomsnittsanalyse av Randomized-Quicksort må vi først få en del ting på plass først. Den største kostnaden i algoritmen er partisjoneringen Hvert kall til Randomized-Partition fjerner pivot elementet fra mulige kandidater i.e. Randomized-Partition blir kalt på det meste n ganger. Arbeidet som blir gjort i Randomized-Partition kjører på konstant tid pluss antall sammenligninger som blir utført i for-løkka Vi lar X være det totale antallet sammenligninger som blir utført under alle kall til Randomized-Partition. For å gjøre det lettere å analysere så har vi følgende om A. Vi omdøper elementene i A til z 1,..., z n der z i er det iende største elementet Z ij er det inklusive settet {z i,..., z j } Vi lar X ij = I {z i is compared to z j }.
Gjennomsnittsanalyse av Randomized-Quicksort Da har nesten alt det som skal til for å finne det forventede totale antallet sammenligninger utført av Randomized-Partition n 1 n E[X ] = E X ij i=1 j=i+1 n 1 = n E[X ij ] i=1 j=i+1 n 1 n = Pr{z i is compared to z j } i=1 j=i+1 Uten å gå veldig grundig til verks kan vi si at Pr{z i is compared to z j } = 2 j i + 1 Dette kommer som en følge av for at z i og z j skal bli sammenlignet må en av de bli valgt som pivot før noe annet element.
Gjennomsnittsanalyse av Randomized-Quicksort Med sansyligheten Pr{X ij } på plass kan vi endelig utføre den siste utregningen. E[X ] = n 1 n i=1 j=i+1 n 1 n i = < i=1 k=1 n 1 n i=1 k=1 2 j i + 1 2 k + 1 2 k n 1 = O(log n) i=1 = O(n log n) variabelskifte k = j i harmonisk serie