2.3 Kviksortering

Kviksortering blev opdaget af Tony Hoare i 1960. Den er en populær sorteringsalgoritme, fordi den er enkel at implementere, opfører sig godt på mange forskellige slags data og er væsentlig hurtigere end andre sorteringsmetoder i typiske anvendelser. Kviksortering bruger ingen ekstra plads bortset fra rekursionsstakken, tager gennemsnitligt tid proportional med \(N\log N\) for at sortere \(N\) elementer og har en usædvanlig kort indre løkke.

Grundideen for kviksortering.

Kviksortering er ligesom flettesortering en del-og-hersk-metode. Den virker ved først at opdele rækken i to dele, som siden sorteres rekursivt, hver for sig.

Quicksort overview
Kernen i metoden er opdelingsprocessen, som omarrangerer rækken således at de følgende tre betingelser er opfyldt: Efter opdelingen sorteres rækken ved at anvende metoden rekursivt på de to delrækker a[lo],...,a[j-1] og a[j],...,a[hi]. Kviksortering er en randomiseret algoritme idet den begynder med at blande rækken tilfældigt før sorteringen.

Opdeling.

Vi betrager nu opdelingsmetoden. Vi begynder med vilkårlig at udnævne elementet a[lo] til opdelingselement, dvs. det element, der straks anbringes i sin endelige position. Derefter afsøger vi rækken fra dens venstre ende indtil vi finder et element, der er større end eller lig med opdelingselementet. Samtidigt afsøger vi rækken fra dens højre ende indtil vi finder et element, der er mindre end eller lig med opdelingselementet.

Quicksort partitioning overview
De to elementer som standsede søgningen står begge i den forkerte halvdel af rækken, så vi lader dem bytte plads. Når til sidst de to søgninger krydser hinanden, færdiggør vi opdelingen ved at lade opdelingselementent a[lo] bytte plads med venstre delrækkes højreste ingang (a[j]) og returnerer dets index j.

Quicksort partitioning

Kviksortering.

Programmet Quick.java implementerer kviksortering ved brug af opdelingsmetoden foroven.

Quicksort trace

Implementationsdetaljer.

Det er værd at lægge mærke til en række subtile detaljer i forbindelse med vores implementation af kviksortering.

Påstand.

Kviksortering bruger \(\sim 2 N \ln N\) sammenligninger (og en sjettedel så mange ombytninger) i gennemsnit for at sortere en række af længde \(N\) med forskellige nøgler.

Påstand.

Kviksortering bruger \(\sim~ N^2/2\) sammenligninger i værste fald. Den indledende tilfældige blanding af rækken sikrer, at det værste fald indtræffer med forsvindende lille sandsynlighed.

Køretidens standardafvigelse er omtrent \(0,65 N\), så køretiden går mod gennemsnittet som \(N\) vokser og er tæt på gennemsnittet med høj sandsynlighed. Sandsynligheden af, at kviksortering bruger et kvadatrisk antal sammenligninger ved sortering af en stor række, er betydeling mindre, end at maskinen under kørslen bliver ramt af lynet.

Forbedringer.

Kviksortering er siden tresserne blevet analyseret og forbret af mange forskere.

Visualisering.

Programmet QuickBars.java visualiserer en opførslen af kviksortering ved brug af både opdeling efter medianen af 3 og afskæring til indsættelsessortering for små delrækker.

Quicksort visualization

Entropioptimal sortering.

I mange anvendelser møder man rækker, hvor samme sorteringsnøgle forekommer mange gange. I sådanne situationer kan man muligvis nedbringe køretiden fra linearitmisk til lineær.

En oplagt ide er at dele rækken i tre dele, afhængigt af om elementerne er skarpt mindre en, lig med, eller skarpt større end opdelingselementet. Dette er en klassisk programmeringsopgave, som blev populariseret af E. W. Dijkstra som det hollandske flagproblem: Opdelingen af en række med tre forskellige nøgler minder om farverne i det hollandske flag.

Dijkstras løsning baserer sig på et enkelt rækkegennmenløb fra venstre til højre, som opretholder tre pegere: En peger lt således at alle elementer i a[lo..lt-1] er mindre end v; en peger gt således at alle elementer i a[gt+1..hi] er større end v, og en peger i således at alle elementer i a[lt..i-1] er lig med . Elementerne i a[i..gt] er endnu ikke behandlet.

Quicksort 3-way partitioning overview

I begyndelsen er i lig med lo. Herfra sammenlignes a[i] og v med trevejssammenligningen fra grænsefladen Comparable. Der er tre tilfælde:

Quicksort 3-way partitioning trace

Programmet Quick3way.java implementerer kviksortering med trevejsopdeling.

Påstand.

Kviksortering med trevejsopdeling er entropioptimal.

Visualisering.

Programmet Quick3wayBars.java visualiserer opførslen af kviksortering med trevejsopdeling.

3-way quicksort visualization