290 likes | 421 Views
Datastructuren Sorteren, zoeken en tijdsanalyse. College 2. Vandaag. Medelingen over werkcolleg O-notatie Binary search Sorteren: analyse van insertion sort Bubble sort Merge Sort Heapsort. Werkcollege. Kies een tijdstip (di+do OF wo+vr) voor het werkcollege
E N D
Vandaag • Medelingen over werkcolleg • O-notatie • Binary search • Sorteren: analyse van insertion sort • Bubble sort • Merge Sort • Heapsort
Werkcollege • Kies een tijdstip (di+do OF wo+vr) voor het werkcollege • Kom naar de 1e bijeenkomst (volgende week dinsdag of woensdag) van Uw groep • Precieze zaal/groep (1, 2, 2a) of (3, 3a) wordt dan gemaakt • Kijk ook zelf even welke zaal ‘t leegst is • Blijf daarna steeds naar dezelfde groep gaan • NB: in de toegestane 4 keer afwezigheid zitten afwezigheid voor goede redenen bevat
Waarom O-notatie • Hoeveel operaties is nu eigenlijk een test als • if(A[i+1] > x) OR (x == 0) • ? • In elk geval: een constant aantal. Om dit niet precies te hoeven bekijken is de O-notatie bedacht: verstopt constanten in de notatie • 8, 9, 20203, 1: allemaal O(1) • n, 4n, 10n + log(n), 21n: allemaal O(n) Datastructuren
“Asympthotic notation” • O: asympthotische bovengrens • Formeel: • O(g(n)) = { f(n) | er zijn positieve constanten c en r, zodat 0 £ f(n) £ c * g(n) voor alle n ³ r} • O(g(n)) is dus een verzameling functies. In plaats van de schrijven f(n) Î O(g(n)) schrijft men echter f(n)=O(g(n)) • Intuitie: we schrijven f(n) = O(g(n)) als vanaf een bepaalde waarde van n (soms 0, soms meer) f(n) nooit meer dan een vaste constante keer g(n) is. • Dus: 3n2 = O(n2) • Nog meer voorbeelden (zometeen) • Feitelijk: laat de constante factor weg, en laat langzamer groeiende functies weg Datastructuren
Voorbeelden n2 + 4n + 6 log n3 12n + log n + 3 r2 – 10 23n+4 Datastructuren
Zoeken in een gesorteerde rij • Array met elementen A[1], …, A[n] • Gegeven een x, is er een i met A[i] == x, en zo ja, welke i? • We zagen al een algoritme dat dit oplost in O(n) tijd: bekijk de elementen van 1 t/m n totdat je x tegenkomt of alles bekeken hebt • Als de rij getallen gesorteerd is kan het sneller met een simpel maar belangrijk principe: binary search • Dus: neem aan: A[1] £ A[2] £ A[3] £ … £ A[n-1] £ A[n]
Binary search • Idee: houdt twee variabelen bij onder en boven, zodat x, als x in A zit, “tussen” onder en boven zit • Invariant: als er een i is met A[i] == x, dan onder£i£boven
Pseudocode • {Input: Gesorteerde array A[1 … n] , element x} • {Output: index i met A[i] == x, and 0 als zo’n i niet bestaat} • onder = 1; boven = n; • while (onder < boven) do • mid = ë (onder+boven)/2 û ; • if (A[mid] < x) then onder = mid+1 else boven = mid • if (A[onder] == x) then return onder else return 0 (zit er niet in)
Sorteeralgoritmen • Aantal algoritmen om te sorteren • Staart vorige keer: insertion sort • Simpel algoritme: bubble sort • Sneller: merge sort (ritsen) • Ook snel: heapsort (met datastructuur: heap) • In de praktijk heel snel: quicksort
Insertion sort • Sorteeralgoritme, met volgende idee: • Voeg steeds één element toe op de goede plek • We hebben een steeds groter goedgesorteerd deel • Array A loopt van 1 t/m lengte(A) INSERTION-SORT(A) for j = 2 to lengte(A) do key = A[j] {voeg A[j] op de goede plek in} i = j – 1; whilei > 0 and A[i] > keydo A[i+1] = A[i] {schuif eentje op} i = i – 1; A[i+1] = key Datastructuren
Tijdsanalyse van INSERTION-SORT 1 • Eerst dit: • Hoeveel stappen kost één slag van de loop voor 1 bepaalde waarde van j ? • Weer een loop. • Elke doorgang door de loop kost iets van 8 elementaire stappen • Deze loop gaan we hooguit j keer rond • Nog eens 6 operaties buiten de loop • Dus 8 j + 6 operaties voor deze slag INSERTION-SORT(A) for j = 2 to lengte(A) do (*) key = A[j] {voeg A[j] op de goede plek in} i = j – 1; whilei > 0 and A[i] > keydo A[i+1] = A[i] {schuif eentje op} i = i – 1; A[i+1] = key (**) Datastructuren
Tijdsanalyse van INSERTION-SORT 2 INSERTION-SORT(A) for j = 2 to lengte(A) do (*) key = A[j] {voeg A[j] op de goede plek in} i = j – 1; whilei > 0 and A[i] > keydo A[i+1] = A[i] {schuif eentje op} i = i – 1; A[i+1] = key (**) • Hoeveel stappen kost één slag van de loop voor een bepaalde waarde van j ? • 8 j + 6 of minder • We doen dit voor j=2, 3, … , tot lengte(A)=n • Totaal: constante keer n2 • Schrijven we als O(n2) Datastructuren
Voordelen Eenvoudig Geen extra geheugen Snel als rij al gesorteerd Nadelen Langzaam: O(n2) Voor en nadelen insertion-sort
Eenvoudig sorteeralgoritme repeat change = false; for i=1 to n-1do if (a[i] > a[i+1]) then verwissel a[i] en a[i+1] van plaats change = true until (change == false) Bubblesort Verwissel: hulp = a[i]; a[i] = a[i+1]; a[i+1] = hulp; Hoe snel? Correct?
Correctheid en tijd bubble sort • Als we klaar zijn is de array gesorteerd; we hebben steeds een permutatie van de input • Terminatie? Ja, want • Na i keer de hoofdloop gedaan te hebben staan op posities n - i +1, n – i +2, … , n de i grootste getallen in de array • Dus: na hooguit n keer de hoofdloop te doen, is het array gesorteerd en zijn we klaar
Verwissel: O(1) Binnenste deel: O(1) For: n keer O(1): O(n) Totaal: n keer O(n): O(n2) Er zijn ook inputs waar zo’n n2stappen gedaan worden, bijv.: het omgekeerde van een gesorteerde array (10, 9, 8, 7, 6, 5, 4, 3, 2, 1) Tijd • repeat • change = false; • for i=1 to n-1do • if (a[i] > a[i+1]) • then • verwissel a[i] en a[i+1] van plaats • change = true • until (change == false)
Mergesort • Algoritmische methode: divide and conquer (verdeel en heers) • Splits probleem in deelstukken • Los elk deelstuk afzonderlijk op • Combineer oplossing van deelstukken • Mergesort gebruikt divide and conquer strategie • Sorteer eerst, recursief de 1e helft van de array • Sorteer daarna, recursief, de 2e helft van de array • Voeg de twee gesorteerde helften samen door een soort van ‘ritsen’
Merge-sort I Mergesort(A, p, r) • {Input: array A, integers p, r, met 1£p£r£ lengte(A)} • {Output: A[p..r] is gesorteerd en bevat dezelfde elementen als A[p..r] in input} • If (p ³ r) then doe niets • else • midden = ë (p+r)/2 û ; • Mergesort(A,p, midden); • Mergesort(A,midden+1,r); • Merge(A,p,midden,r); {“Rits” de twee stukken in elkaar”}
Merge(A,p,q,r) (deel 1) • {Input: A[p…q] is gesorteerd, en A[q+1…r] is gesorteerd} • {Output: A[p…r] is gesorteerd} • n1 = q – p +1; • n2 = r – q; • Maak een array L[1…n1+1]; • Maak een array R[1..n2+1]; • for i=1 to N1 do L[i] = A[p+i – 1]; • for j=1 to N2 do R[j] = A[q+j]; • (rest komt zometeen) Eerst copieren in arrays L en R
Merge deel 2 • n1 = q – p +1; • n2 = r – q; • Maak een array L[1…n1+1]; • Maak een array R[1..n2+1]; • for i=1 to n1 do L[i] = A[p+i – 1]; • for j=1 to n2 do R[j] = A[q+j]; • L[n1] = MAXINT; {Stootblok (sentinel)} • L[n2] = MAXINT; {Stootblok}
Merge deel 2 • n1 = q – p +1; n2 = r – q; • Maak arrays L[1…n1+1] en R[1..n2+1]; • for i=1 to n1 do L[i] = A[p+i – 1]; • for j=1 to n2 do R[j] = A[q+j]; • L[n1] = MAXINT; {Stootblok} • L[n2] = MAXINT; {Stootblok} • pleklinks = 1; • plekrechts = 1; • for k = p to r do • {Vind het element op positie k in A} • if (L(pleklinks) £ R(plekrechts)) • then A[k] = L(pleklinks); pleklinks ++; • else A[k] = R[plekrechts); plekrechts ++;
Correctheid merge • Invariant • Aan het begin van de for-loop gelden: • A[p…k-1] bevat de k-p kleinste elementen uit L[1..n1+1] en R[1..n2+1] • A[p…k-1] is gesorteerd • L[pleklinks] is het kleinste element in L dat niet teruggezet is naar A • r[plekrechts] is het kleinste element in R dat niet teruggezet is naar A • Invariant geldt initieel, en blijft gelden • Bij terminatie: k = r+1; en dus ...
Tijd van mergesort • Wat is de tijd van een enkele mergeoperatie? • Als we twee stukken van lengte r mergen: O(r) (want…) • Analyse van mergesort hier wat informeler – kijk naar de “berekeningsboom”
Tijd van mergesort • O(n log n)
Next: • Heapsort