300 likes | 394 Views
INF 295 Algoritmer og datastrukturer Forelesning 21 Merge, Quick og Bøtte, Radix og ekstern sortering. Hans Fr. Nordhaug (Ola Bø). Mergesort. Bygger på sammensmelting av sorterte lister
E N D
INF 295 Algoritmer og datastrukturerForelesning 21 Merge, Quick og Bøtte, Radix og ekstern sortering Hans Fr. Nordhaug (Ola Bø)
Mergesort • Bygger på sammensmelting av sorterte lister • Hver av de sorterte listene kan lages ved å sammensmelte sorterte lister som kan bygges ved å sammensmelte sorterte lister .... • Mergesort kjører i O(NlogN) worst case • Antall sammenlikninger er nesten optimal • Rekursiv algoritme • Kjøretid • Merge av to lister er lineær • Analysen bygger på at T(N)=2*T(N/2)+N • Plassbehov – Trenger en ekstra temporær array
Quicksort • Raskeste kjente sorteringsalgoritme i praksis • Gjennomsnittlig kjøretid O(NlogN) • O(N2) worst case – men kan gjøres svært usannsynlig • Har rykte for å være vanskelig å implementere • Algoritmen er en rekursiv splitt og hersk algoritme
Algoritme • Hvis antall elementer er 0 eller 1 returner • Plukk et vilkårlig element v i S. Elementet kalles pivot • Partisjoner de resterende elementene i S i to disjunkte mengder - S1: elementene som er mindre enn eller lik v S2: elementene som er større enn eller lik v • Returner quicksort( S1), fulgt av Pivot, fulgt av quicksort(S2)
Quicksort-algoritme • Hvordan plukke ut pivot-elementet? • Ideelt det midterste elementet for å få en effektiv algoritme • Splitt og hersk som mergesort • Ingen garanti for like store delproblemer • Likevel raskere enn mergesort i praksis • Hvorfor? • Alt foregår effektivt og i arrayen som skal sorteres • Krever i motsetning til merge ingen ekstra array
Å velge Pivot-element til Quicksort • Algoritmen fungerer uansett hvordan pivot velges • Men den fungerer dårlig hvis vi stadig bruker en pivot som er langt fra midten • Hva om vi bruker første element som pivot? • Svært dårlig på materiale som er (nesten) sortert • Gir kvadratisk tid på å gjøre (nesten) ingen ting. • Nesten sortert er ganske vanlig. Hvorfor? • Bokas konklusjon: An absolutely horrible idea • Bruke den største av de to første? Nei • Ta et tilfeldig element • Tilfeldig tallgenerering er for dyrt • Det beste valget er medianen av tabellen • Median er for dyrt • Velger medianen av første, siste og midtre element!
Partisjoneringsstrategi • Partisjonering: • Flytte alle elementer som er større enn Pivoten til høyre for Pivot • Flytte alle elementer som er mindre enn Pivoten til venstre for Pivot • Mange mulige strategier. Krever omtanke. • Løsning: • Flytt Pivot til slutten. Sett peker til hver ende • Flytt pekerne til du finner elementer som skal flyttes • Bytt elementene, flytt pekerne videre til du finner nye elementer som skal flyttes osv • Når de to pekerne har passert hverandre er du ferdig • Bytt Pivot med siste posisjon for venstrepeker • Hva gjør vi med elementer som er lik Pivot?
Små Arrayer • Quicksort er mindre effektiv enn innsettings-sortering for N<20 • Etterson Quicksort er rekursiv, vil det bli mange slike korte arrayer å sortere • Strategi: Kutt Quicksort og gå over til Insettingssortering når delarrayen er liten nok • Cutoff mellom 5 og 20 går bra.
Et eksempel på Quick-sort • Pivotgenerering gjøres på stedet • Positive sideeffekter: • Plasserer første, siste og pivot i riktig partisjon • Pivot flyttes til siste-1 • Pekerne kan dermed starte i første+1 og siste-2 • første og siste blir dermed sentinel (skiltvakter) som sikrer at pekerne vil stoppe innenfor arrayen • Mulig optimalisering av bytting • Indre loop er svært effektiv: • increment, sammenlikn, jump
Bøttesortering • Fungerer når vi har mer opplysninger om materialet som skal sorteres • Eksempel: Sortering av naturlige tall med gitt øvre grense • Idé • Opprett en array int[] antall=new int[øvre grense] • For hvert element inkrementerer vi tilsvarende antall • For hver i mellom 0 og øvre grense, skriv ut i så mange ganger som antall[i] tilsier
Bøttesortering • Kan også sortere mer kompliserte elementer enn heltall, bare vi sorterer på noe som svarer til heltall • Hvis tallområdet blir for stort er Radix-sort et alternativ • Kan være betydelig raskere enn Quick-sort • Ikke sammenlikningsbasert • Kan ikke brukes som generell algoritme • Kan ha mange anvendelser i praksis • Ikke uvanlig å ha små heltall som input
Radix-sortering • Når det blir for mange bøtter - • f.eks delnummere 12334098993356719 • Sortere i bøtter på "siffer" for "siffer" • Minst signifikante siffer først • "Siffer" kan være desimalt siffer - gir 10 bøtter • Ikke nødvendigvis effektivt - det blir mye flytting • Mer effektivt å bruke mange bits i hvert "siffer" • Radix er grunntallet i et tallsystem
Resultater Sorteringsalgoritmer 450 MHz Pentium Sortering av 10 000 tilfeldige tall mellom 0 og 100 000 Tidsforbruk bubblesort:9010 ms Tidsforbruk insertionsort:3950 ms Tidtaking har usikkerhet > 60 ms - klokka tikker bare 18,2 ganger per sekund!! Tidsforbruk heapsort:60 ms Tidsforbruk shellsort:50 ms Tidsforbruk mergeSort:60 ms Tidsforbruk quicksort:50 ms Tidsforbruk bucketsort:0 ms
Resultater Sortering av 1000 000 tilfeldige tall mellom 0 og 10 000 000 Avanserte sammenlikningsbaserte algoritmer Tidsforbruk heapsort:11040 ms Tidsforbruk shellsort:20210 ms Tidsforbruk HibbardShellsort:19010 ms Tidsforbruk SedgewickShellsort:11640 ms Tidsforbruk mergeSort:7360 ms Tidsforbruk quicksort:5110 ms Tidsforbruk javas sort (quicksort):6650 ms Algoritmer med direkte plassering Tidsforbruk bucketsort:2300 ms Bucketsort med 1 000 000 bøtter Tidsforbruk radixSort med bibliotekslister:12250 ms Tidsforbruk radixSort med skreddersydde lister:3790 ms Radixsort med 65 526 bøtter
Ekstern sortering • Mange tilfeller med for mye data til sortering i indre lager • Nye algoritmer er nødvendig • Elementer som ikke er naboer vil ta tid å aksessere • Diskaksess tar tid • Tape er spesielt krevende på grunn av sekvensiell aksess • Skal vi sortere med tape, trengs minst tre tapelesere - ellers er vi i trøbbel
Ekstern sortering • Grunnleggende algoritme: MergeSort • 4 taper Ta1, Ta2, Tb1, Tb2. Data på Ta1 • Algoritme • Gjenta: Fyll memory fra Ta1 og sorter internt M tall i gangen. Skriv til Tb1 og Tb2 vekselvis. Hver operasjon kalles en run. Rewind • Flett data for en run fra Tb1 og en run fra Tb2, skriv flettede data til Ta1 og Ta2 vekselvis. Hver operasjon kalles en run- og er dobbelt så lang som forrige run. Tilbakespol alle taper og gjenta (med rollene for Ta og Tb byttet om) til det bare er en run. Da er det ferdig sortert. • Antall omganger er log(N/M)
Ekstern sortering - eksempel • I dette eksemplet kunne altså maskinen lese inn tre tall i intern minne - resten måtte skje på tape. • Krever ca log(13/3) omganger.
Multiway merge • Har vi flere tapestasjoner, kan vi merge fra flere taper på en gang. • Tar hele tiden minste element fra en av de k tapene og leser neste post på tapen. • Binær heap gir rask henting av minste element
Polyphase merge • Gjøre k-veis fletting med k+1 taper istedenfor 2k. • Strategien er enkel – fordel runs ujevnt, og rewind når det kortest er ferdig. • Problemet er antall omganger. Valg fordeling etter (k) Fibonacci.
Konstruksjon av runs • Rett frem – les M elementer og sorter disse intern, gjenta. • Men hver gang et tall skrives tilbake til tape blir det en ledig plass internt – bruk denne vha av Replacement selection (RS). • Nytt tall velges fra minne med deleteMin (binær heap). Fyll inn et nytt tall fra tape i internt minne hver gang – enten i køen eller i ”dead space”. Bygg ny heap når køen er tom.
Oppsummering • Bruk vanlig innstikk på små array, ellers shellsort eller quicksort. • I spesielle tilfeller kan bøtte eller radix søk brukes. • Sorteringsalgoritmer kan kategoriseres. • Ekstern sortering er vanligvis basert på fletting.