Sortering
Sorteringsproblemet Gitt en array med n elementer som kan sammenlignes med hverandre: Finn en ordning (eller permutasjon) av elementene slik at de står i stigende (evt. avtagende) rekkefølge Det er viktig å kunne sortere effektivt: Sortering er alltid #1 forbruker av prosessortid Raske algoritmer for søking, innsetting og fjerning i datastrukturer krever oftest at dataene er sortert Analyse og utvikling av effektive sorteringsalgoritmer er et sentralt forskningsområde i (klassisk) informatikk
Hvor raskt klarer vi å sortere? Bare det å se om dataene er sortert er O(n) Rett-frem algoritmer er O(n 2 ) Hvis vi sorterer ved å sammenligne og bytte om to og to elementer, kan det bevises at dette ikke kan gjøres raskere enn O(n log(n)) Smarte sorteringsalgoritmer er O(n log(n)) Hvis vi vet mer om dataene kan vi lage O(n) algoritmer som sorterer med andre mekanismer enn parvise sammenligninger og swapping (jfr. bucket sort og radix sort fra kapitlet om køer)
Tre klasser av sorteringsalgoritmer Sekvensielle: Bruker typisk to løkker inne i hverandre, O(n 2 ) Kun brukbare for små problemer Utplukksortering, innstikksortering, bubble sort Logaritmiske: Deler opp problemet i mindre deler og sorterer disse rekursivt, O(n log(n)), mye raskere for store datasett Quicksort, flettesortering Basert på bruk av spesielle datastrukturer: Alle dataene legges inn i en datastruktur og tas ut igjen i sortert rekkefølge, typisk O(n log(n)) Treesort, heapsort
Sortering av arrayer forenkling Læreboka bruker generiske metoder i Java som kan sortere arrayer som inneholder alt, så lenge dataene er Comparable For å fokusere på algoritmene og effektiviteten, og ikke på Java, forenkler vi sorteringsproblemet og programkoden til: Sortering av arrayer som bare innholder heltall
Utplukksortering (selection sort) Går gjennom arrayen n - 1 ganger I gjennomløp nr i (i = 0, 1, 2,, n - 2) : Arrayen er sortert fra starten og t.o.m. indeks i - 1 Finner det minste av de usorterte elementene på indeksene i, i + 1,, n - 1 ( utplukk / selection ) Setter dette minste elementet inn på indeks i med en ombytting (swapping) Etter n 1 gjennomløp står det største elementet igjen på index n 1, og hele arrayen er sortert
Utplukksortering, eksempel 64 25 12 22 11 11 25 12 22 64 11 12 25 22 64 11 12 22 25 64 11 12 22 25 64
Utplukksortering, animasjon
Effektivitet og implementasjon Utplukksortering programmeres enkelt med to løkker inne i hverandre: Ytre løkke går n - 1 ganger Indre løkke går n - 2, n - 3,..., 2, 1 ganger Totalt: O(n 2 ) Effektiviteten av utplukksortering er ikke avhengig av dataene, algoritmen gjør alltid like mange tester og ombyttinger Se Java-koden
Innstikksortering (insertion sort) Går gjennom arrayen n - 1 ganger I gjennomløp nr i (i = 1, 2, 3,, n - 1) : Arrayen er sortert fra starten og t.o.m. indeks i - 1 Setter element nr i inn på riktig plass blant de i - 1 første elementene ( insertion / innstikk ) Innsettingen gjøres ved å bytte element i med foregående element inntil det står riktig i sorteringen Etter n 1 gjennomløp er alle elementer satt inn på riktig plass og hele arrayen er sortert
Innstikksortering, eksempel 3 7 4 9 5 2 6 1 (0 swap) 3 7 4 9 5 2 6 1 (1 swap) 3 4 7 9 5 2 6 1 (0 swap) 3 4 7 9 5 2 6 1 (2 swap) 3 4 5 7 9 2 6 1 (5 swap) 2 3 4 5 7 9 6 1 (2 swap) 2 3 4 5 6 7 9 1 (7 swap) 1 2 3 4 5 6 7 9
Innstikksortering, animasjon
Effektivitet og implementasjon Innstikksortering programmeres med to løkker inne i hverandre: Ytre løkke går n - 1 ganger Indre løkke går inntil element i står riktig, maksimalt: 1, 2, 3,..., n - 1 ganger Worst-case: O(n 2 ) Effektivitet av innstikksort. avhenger av dataene: Er O(n) for nesten sorterte arrayer Gjennomsnittlig (random data): O(n 2 ) Se Java-koden
Bubble sort (litt anderledes enn i læreboka) Går gjennom arrayen n - 1 ganger I gjennomløp nr i (i = 0, 1, 2,,, n - 2) : Arrayen er sortert fra starten og t.o.m. indeks i - 1 Setter det minste av elementene i den gjenværende delen av arrayen på indeks i Finner minste element ved å starte i indeks n - 1 og swappe med foregående element hvis dette er større Fortsetter å swappe minste nabolement fremover, til det minste av de usorterte elementene står i indeks i Små verdier vil under sorteringen boble oppover i arrayen, store elementer synker nedover ( sink sort / percolation sort ) Etter n 1 gjennomløp er alle elementer satt inn på riktig plass og hele arrayen er sortert
Bubble sort, eksempel, n = 5 i = 0 i = 1 i = 2 i = 3 64 10 12 22 11 10 64 11 12 22 10 11 64 12 22 10 11 12 64 22 64 10 12 11 22 10 64 11 12 22 10 11 64 12 22 10 11 12 22 64 64 10 11 12 22 10 64 11 12 22 10 11 12 64 22 64 10 11 12 22 10 11 64 12 22 10 64 11 12 22
Bubble sort, animasjon
Effektivitet og implementasjon Bubble sort programmeres med to løkker inne i hverandre: Ytre løkke går n - 1 ganger Indre løkke går n - 1, n - 2, n 3,..., 2, 1 ganger Alltid O(n 2 ) Effektivitet av bubble sort avhenger av dataene: Går langsomt hvis det er mye ombytting av verdier Kan enkelt programmes til å avbryte med en gang arrayen er sortert (0 swaps i et gjennomløp) Se Java-koden
Sammenligning av sekvensielle sorteringsmetoder Effektivitet avhenger av hvor mye flytting av data som gjøres en swap tar mye lenger tid enn en if-test Utplukksortering: Forutsigbar, alltid samme effektivitet uansett data Instikksortering: Oftest mest effektiv, gjør lite ombytting Meget rask for nesten-sorterte arrayer Bedre enn smarte algoritmer for små datamengder Bubble sort: Langsom pga. mye swapping, lite brukt Se testprogram for sorteringsalgoritmer
Shell sort: En raskere algoritme Aka. inkrementell sortering og gap sort (Donald Shell, 1959) Sorterer f.eks. hvert 100. element innbyrdes med instikksortering, deretter hvert 50. element, hvert 25. element, hvert 12. element etc., og tilslutt alle elementer i siste gjennomgang Grovsorteringen går raskt for store gaps Hele sorteringen går (veldig mye) raskere fordi vi hele tiden bruker innstikksortering på en array som etterhvert vil være nesten sortert
Shell sort: Eksempel Shell sort med gap -sekvens 5, 3 og 1: Start: 81 94 11 96 12 35 17 95 28 58 41 75 15 5-sort : 35 17 11 28 12 41 75 15 96 58 81 94 95 3-sort : 28 12 11 35 15 41 58 17 94 75 81 96 95 1-sort : 11 12 15 17 28 35 41 58 75 81 94 95 96
En annen måte å se på Shell sort 23 42 18 96 77 30 11 87 54 29 74 16 56 48 79 33 23 42 18 96 77 30 11 87 23 29 18 16 56 30 11 33 54 29 74 16 56 48 79 33 54 42 74 96 77 48 79 87 23 29 18 16 56 30 11 33 54 42 74 96 77 48 79 87 23 29 18 16 23 29 11 16 56 30 11 33 54 30 18 33 54 42 74 96 56 42 74 87 77 48 79 87 77 48 79 96 23 29 11 16 54 30 18 33 56 42 74 87 77 48 79 96 23 29 11 16 11 16 18 29 54 30 23 30 18 33 54 33 56 42 56 42 74 87 74 48 77 48 77 87 79 96 79 96 11 16 18 29 23 30 54 33 56 42 74 48 77 87 79 96
Shell sort: Animasjon
Implementering og effektivitet Shell sort programmeres med tre løkker, der den ytre løkken går gjennom sekvensen av gaps, f.eks. 100, 50, 25, 12, 2, 1 De to indre løkkene gjør innstikksorteringen for hvert gap Effektivitet: Gjennomsnittlig ~ O(n 3/2 ) Erfaring viser at effektiviteten av Shell sort avhenger av valget av gap -sekvens: Shells opprinnelige forslag: n/2, n/4, n/8,..., 2, 1 Bedre: Rund av hvert gap til nærmeste oddetall Enda bedre: n/2, (n/2)/(2.2), (n/2)/(2.2 2 ), (n/2)/(2.2 3 ),..., 1 Se Java-koden og testprogram
Andre sekvensielle sorteringer Finnes mange andre forbedringer/varianter av sekvensielle algoritmer som også kan være bedre enn O(n 2 ), f.eks: Comb sort Cocktail sort Odd-even sort Alle disse er polynomiske og derfor før eller senere langsommere enn de logaritmiske O(n log(n)) metodene
Logaritmiske sorteringsalgoritmer Rekursive splitt og hersk metoder Deler arrayen i to (helst) omtrent like store deler Hver halvdel sorteres rekursivt på samme måte Metodene gjør et eller annet med de to halvdelene slik at de til slutt kan settes sammen til en hel ferdig sortert array Hvis vi deler i to ~like deler hver gang, blir det ca. log(n) nivåer med rekursive kall Hvis arbeidet som gjøre på hvert nivå samlet er O(n), vil hele sorteringen bli O(n log(n)) To logaritmiske metoder i dette kurset: Quicksort Flettesortering
Quicksort T.Hoare, 1960, eg. laget for automatisk oversetting Oftest meget rask i praksis Virker for generelle sorteringsproblemer Krever svært lite ekstra hukommelse Gjennomsnittlig effektivitet er O(n log(n)), worstcase er O(n 2 )
Quicksort: Algoritme Sortering av array A av lengde n: Hvis n > 1: Velg (på en eller annen måte) ett element p (kalt partisjoneringselement) i A, f.eks. element nr. 0 eller nr. n/2* Bytt om på elementene i A (partisjonér arrayen) slik at den deles i to deler: Alle elementer som er mindre eller lik p står til venstre Alle elementer som er større enn p står til høyre Partisjoneringselementet p står mellom de to delene Sortér de to delene rekursivt med Quicksort, hele arrayen er da ferdig sortert *: Lærebokens valg
Quicksort: Eksempel Bruker første element til å partisjonere (del)array(ene) Nivå 1: 26 33 35 29 19 12 22 19 22 12 26 29 35 33 Nivå 2: 19 22 12 26 29 35 33 12 19 22 26 29 35 33 Nivå 3: 12 19 22 26 29 35 33 12 19 22 26 29 33 35 Nivå 4: 12 19 22 26 29 33 35
Quicksort: Et eksempel til Nivå 1: 65 57 81 92 43 31 26 75 13 10 26 57 10 13 43 31 65 75 92 81 Nivå 2: 26 57 10 13 43 31 65 75 92 81 10 13 26 57 43 31 65 75 92 81 Nivå 3: 10 13 26 57 43 31 65 75 92 81 10 13 26 43 31 57 65 75 81 92 Nivå 4: 10 13 26 43 31 57 65 75 81 92 10 13 26 31 43 57 65 75 81 92 Nivå 5: 10 13 26 31 43 57 65 75 81 92
Partisjoneringsalgoritmen 6 8 1 4 9 0 3 5 2 7 6 er partisjoneringselement 6 8 1 4 9 0 3 5 2 7 7 står riktig plassert 6 8 1 4 9 0 3 5 2 7 swap 2 og 8 6 2 1 4 9 0 3 5 8 7 1 står riktig plassert 6 2 1 4 9 0 3 5 8 7 4 står riktig plassert 6 2 1 4 9 0 3 5 8 7 swap 5 og 9 6 2 1 4 5 0 3 9 8 7 0 og 3 står riktig plassert 3 2 1 4 5 0 6 9 8 7 swap 6 og 3, ferdig
Quicksort: Animasjon
Effektivitet og implementasjon Implementeres med to rekursive kall, der parameterene er øvre og nedre indeks for arraysegmentet som skal sorteres Skiller ut partisjoneringen i en egen metode Er O(n log(n)) i gjennomsnitt (random data) Hvis partisjoneringen gir mange skjeve oppdelinger (f.eks. ved nesten sorterte data) vil Quicksort dele opp arrayen ~n ganger, og algoritmen blir O(n 2 ) Se Java-koden
Effektivisering av Quicksort Forbedring av valg av partisjoneringselement, for å redusere muligheten for skjev oppdeling: Sammenlign f.eks. elementene på indeksene 0, n/2 og n-1, og bruk verdien som er i midten Bruk en enklere og lettere ikke-rekursiv metode, f.eks. innstikksortering, til å sortere korte segmenter av arrayen
Flettesortering (merge sort) John von Neumann (!), 1945 Velegnet for steinalderens sekvensielle lagringsmedia som magnet- og papirtape, hullkort etc. også utmerket for sortering av lenkede lister (oppgave) Garanterer O(n log(n)) effektivitet Krever at vi bruker ekstra array(er) til å kopiere data frem og tilbake under sortering
Flettesortering: Algoritme Sortering av array A av lengde n: Hvis n > 1: Sortér nedre og øvre halvdel av A hver for seg, rekursivt med flettesortering Flett de to halvdelene sammen til en sortert array Hele arrayen er da ferdig sortert
Flettesortering: Eksempel 26 33 35 29 19 12 22 26 33 35 29 19 12 22 oppdeling 26 33 35 29 19 12 22 oppdeling 26 33 35 29 19 12 22 bunn i rekursjonen 26 33 29 35 12 19 22 fletting 26 29 33 35 12 19 22 fletting 12 19 22 26 29 33 35
Fletting av sorterte arraysegmenter 1 13 24 26 2 15 27 38 1 13 24 26 2 15 27 38 1 1 13 24 26 2 15 27 38 1 2 1 13 24 26 2 15 27 38 1 2 13 1 13 24 26 2 15 27 38 1 2 13 15 1 13 24 26 2 15 27 38 1 2 13 15 24 1 13 24 26 2 15 27 38 1 2 13 15 24 26 1 13 24 26 2 15 27 38 1 2 13 15 24 26 27 1 13 24 26 2 15 27 38 1 2 13 15 24 26 27 38
Flettesortering: Animasjon
Effektivitet og implementasjon Implementeres med to rekursive kall, der parameterene er øvre og nedre indeks for arraysegmentet som skal sorteres Flettingen gjøres ved å kopiere dataene over i en temporær array og deretter flette de to halvdelene tilbake Programmering av flettingen krever indeksfikling Er alltid O(n log(n)), men krever minst O(n) ekstra hukommelse i tillegg til arrayen som sorteres Se Java-koden
Quicksort vs. flettesortering Quicksort er vesentlig raskere i de aller fleste tilfeller Flettesortering må alltid flytte data mellom temporær og original array og blir derfor langsommere Flettesortering er alltid O(n log(n)), men... Hvis det er viktig med garantert O(n log(n)) oppførsel, er det bedre å bruke en in-house algoritme som ikke swapper så mye, som f.eks. heapsort Se testprogram for sorteringer