Quicksort Fra idé til algoritme.
Quicksortalgoritme algoritmeidé 1. Del arrayen i to deler, slik at alle elementer i den ene delen er mindre enn alle elementer i den andre delen. Q U I C K S O R T A L G O R I T M E N E M I C K I G L A N R O O R S T U Q T michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 2
2. Velg en verdi som finnes i arrayen og bytt to og to elementer ved å starte fra de to endene slik at den ene delen inneholder verdiene som er mindre enn den valgte verdien, den andre delen inneholder verdiene som er større. Q U I C K S O R T A L G O R I T M E N michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 3
E M I C K I G L A N R O O R S T U Q T 3. Vi har da tre deler, først en del med elementer mindre enn eller lik den valgte verdi, så ett element med den valgte verdi, til slutt en del med elementer større enn eller lik den valgte verdi. Gjenta 1 3 på de to delene. Hvis de to delene blir sortert, vil da hele arrayen være sortert. michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 4
E M I C K I G L A N R O O R S T U Q T pivot = a [ t i l ] ; boolean ikkeferdig = true ; int i = fra ; int j = t i l 1; michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 5
A C E G I I K L M N R O O R S T U Q T while ( ikkeferdig ) { while ( a [ i ] < pivot ) { i++ ; while ( a [ j ] > pivot ) { j ; i f ( i < j ) { bytt ( a, i, j ) ; i ++; j ; else ikkeferdig = false ; michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 6
Ved å sørge for at begge delarrayer er kortere enn den vi har er vi sikret at de rekursive kallene går mot basistilfellet. Basistilfellet er en array av lengde mindre enn 4. Disse små delarrayene sorteres direkte når de er av lengde 2 eller 3. (Delarrayer av lengde 1 eller 0 er sortert). Vi videreutvikler algoritmen: michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 7
1. Hvis antall elementer som skal sorteres er 0 eller 1, returner. 2. Hvis antall elementer som skal sorteres er 2 eller 3, sorter dem uten rekursive kall og returner. 3. Plukk et («middels stort») element fra mengden som skal sorteres. Dette kalles pivot-elementet. 4. Del resten av elementene i to: De som er mindre enn pivot-elementet. De som er større enn pivot-elementet. 5. Sorter disse delmengdene hver for seg (ved hjelp av quicksort). 6. Returner sorteringen av de små elementene, etterfulgt av pivot-elementet, etterfulgt av sorteringen av de store elementene. michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 8
Algoritmevalg For små arrayer med N 20 er quicksort mindre effektiv enn f.eks. innstikksortering. På grunn av de rekursive kallene vil dette være et vanlig tilfelle! Løsning: Bruk en avskjæring mellom 5 og 20 (vanligvis 10) slik at arraysegmenter mindre enn dette sorteres ved hjelp av en annen sorteringsmetode. michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 9
Partisjoneringsstrategi Hvordan dele arrayen i en «liten» og en «stor» del? 1. Få pivot-elementet «vekk» ved å bytte det med det siste elementet. 2. La i starte på det første elementet, og j på det nest-siste. 3. Så lenge i er til venstre for j: 3.1 Flytt i mot høyre så lenge elementet i peker på er mindre enn pivot-elementet. 3.2 Flytt j mot venstre så lenge a[j] er større enn pivot-elementet. 3.3 i peker nå på et «stort» element og j på et «lite». Hvis i er til venstre for j, byttes disse elementene. 4. Bytt pivot-elementet med elementet i posisjon i. michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 10
Partisjoneringsstrategi utprogrammert boolean ikkeferdig = true ; int i = fra ; int j = t i l 1; while ( ikkeferdig ) { while ( a [ i ] < pivot ) { i++ ; while ( a [ j ] > pivot ) { j ; i f ( i < j ) { bytt ( a, i, j ) ; i ++; j ; else ikkeferdig = false ; michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 11
Hvordan velge pivot-element? Ideelt: Et pivot-element som deler mengden i to like store halvdeler. Best?: Velger pivot-elementet så «tilfeldig» som mulig. I algoritmen nedenfor er siste element valgt som pivot-element. michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 12
void quicksort ( char [ ] a, int fra, int t i l ) { // rekursjonsbunn hvis mindre enn 4 elementer i f ( t i l fra < 3 ) { // sortering utelatt else { // minst 4 elementer char pivot = a [ t i l ] ; // Partisjonerer arrayen ( se over ) // Flytter pivot elementet t i l midten bytt ( a, i, t i l ) ; // Alle elementer : a[ fra, i 1] <= a[ i ] <= a[ i +1, t i l ] quicksort ( a, fra, i 1); // a[ fra, i ] er sortert, alle <= a[ i +1, t i l ] quicksort ( a, i +1, t i l ) ; // a[ fra, t i l ] er sortert michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 13
Programmet effektiviseres best ved å sikre valg av et pivot-element så nær medianen (midterste verdi når sortert) som mulig. I algoritmen nedenfor bruker vi midten-av-tre partisjonering: Se på det første, midterste og siste elementet. Velg det mellomste av disse som pivot. OBS! Metoden vil også sortere disse tre elementene, samt skjule pivot-elementet. michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 14
midtenavtre() i Java char midtenavtre ( char [ ] a, int fra, int t i l ) { int midten = ( fra + t i l ) / 2; i f ( a [ midten ] < a [ fra ] ) { bytt ( a, fra, midten ) ; i f ( a [ t i l ] < a [ fra ] ) { bytt ( a, fra, t i l ) ; i f ( a [ t i l ] < a [ midten ] ) { bytt ( a, midten, t i l ) ; // Skjul pivot elementet nest lengst t i l høyre bytt ( a, midten, t i l 1); return a [ t i l 1]; michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 15
Quicksort med midten-av-tre-partisjonering void quicksort ( char [ ] a, int fra, int t i l ) { /* Hvis fra er nær t i l, sorter direkte /* og returner ( b a s i s t i l f e l l e t ). Hvis ikke : */ int pivotindeks ; char pivot = midtenavtre ( a, fra, t i l ) ; int i = fra ; int j = t i l 1; while ( true ) { while ( a[++ i ] < pivot ) { while ( a[ j ] > pivot ) { i f ( i < j ) bytt ( a, i, j ) ; else break ; pivotindeks = i ; bytt ( a, pivotindeks, t i l 1); quicksort ( a, fra, pivotindeks 1); quicksort ( a, pivotindeks+1, t i l ) ; michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 16
A C E G I I K L M N R O O R S T U Q T while ( ikkeferdig ) { while ( a [ i ] < pivot ) { i++ ; while ( a [ j ] > pivot ) { j ; i f ( i < j ) { bytt ( a, i, j ) ; i ++; j ; else ikkeferdig = false ; michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 17
A C E G I I K L M N O O Q R R S T T U while ( ikkeferdig ) { while ( a [ i ] < pivot ) { i++ ; while ( a [ j ] > pivot ) { j ; i f ( i < j ) { bytt ( a, i, j ) ; i ++; j ; else ikkeferdig = false ; michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 18
Oppgaver til neste fellesøvelse 1. Skriv en metode som sorterer 2 elementer 2. Skriv en metode som sorterer 3 elementer 3. Hvorfor kan vi under partisjoneringen ikke hoppe over elementer som er lik pivotelementet? (De ligger jo i utgangspunktet ikke «feil»). 4. Skriv om partisjoneringen slik at elementer lik pivotelementet ikke blir flyttet. 5. Vis at algoritmen skissert over (ikke den som bruker midten-av-tre) faktisk fungerer for delarrayer med lengde mindre enn 4 også. michael@ifi.uio.no INF1010 29. april 2010 (uke 17) 19