1 Algorithmen und Datenstrukturen Wintersemester 2013/14 9. Vorlesung Sortieren in Linearzeit Prof. Dr. Alexander Wolff Lehrstuhl für Informatik I 2 Wir wissen. . . Satz. Jeder vergleichsbasierte Sortieralg. benötigt im schlechtesten Fall Ω (n log n) Vergleiche um n Objekte zu sortieren. Korollar. MergeSort und HeapSort sind asymptotisch worst-case optimal. 3 Wir durchbrechen die Schallmauer ( SpaghettiSort sortiert Spaghetti nach Länge ;-) CountingSort sortiert Zahlen in {0, . . . , k } RadixSort sortiert s -stellige b -adische Zahlen BucketSort sortiert gleichverteilte zufällige Zahlen aus: www.marions-kochbuch.de 4-45 CountingSort Idee: 1) für jedes x in der Eingabe: zähle die Anzahl der Zahlen ≤ x 2) benütze diese Information um x im Ausgabefeld direkt an die richtige Position zu schreiben Variable: A B Eingabefeld Ausgabefeld C k Rechenfeld begrenzt Universum: {0, . . . , k} Bsp: 1a) Für jedes x in A, zähle die Anz. der Zahlen gleich x 1 2 3 4 5 6 7 8 A 3 0 4 1 3 4 1 4 0 ⇒ 1 2 3 4 C 1 2 0 2 3 1b) Für jedes x in A, berechne die Anz. der Zahlen ≤ x C 1 3 0 1 2 3 4 4-59 CountingSort Idee: 1) für jedes x in der Eingabe: zähle die Anzahl der Zahlen ≤ x 2) benütze diese Information um x im Ausgabefeld direkt an die richtige Position zu schreiben Variable: A B Eingabefeld Ausgabefeld C k Rechenfeld begrenzt Universum: {0, . . . , k} Bsp: 1a) Für jedes x in A, zähle die Anz. der Zahlen gleich x 1 2 3 4 5 6 7 8 A 3 0 4 1 3 4 1 4 0 ⇒ 1 2 3 4 C 1 2 0 2 3 1b) Für jedes x in A, berechne die Anz. der Zahlen ≤ x C 1 3 3 5 8 0 1 2 3 4 2) Schreibe jedes x in A direkt an die richtige Position in B B 1 2 3 4 5 6 7 8 4-62 CountingSort Idee: 1) für jedes x in der Eingabe: zähle die Anzahl der Zahlen ≤ x 2) benütze diese Information um x im Ausgabefeld direkt an die richtige Position zu schreiben Variable: A B Eingabefeld Ausgabefeld C k Rechenfeld begrenzt Universum: {0, . . . , k} Bsp: 1a) Für jedes x in A, zähle die Anz. der Zahlen gleich x 1 2 3 4 5 6 7 8 A 3 0 4 1 3 4 1 4 0 ⇒ 1 2 3 4 C 1 2 0 2 3 1b) Für jedes x in A, berechne die Anz. der Zahlen ≤ x C 1 3 3 5 8 0 1 2 3 4 2) Schreibe jedes x in A direkt an die richtige Position in B 4 B 1 2 3 4 5 6 7 8 4-10 CountingSort Idee: 1) für jedes x in der Eingabe: zähle die Anzahl der Zahlen ≤ x 2) benütze diese Information um x im Ausgabefeld direkt an die richtige Position zu schreiben Variable: A B Eingabefeld Ausgabefeld C k Rechenfeld begrenzt Universum: {0, . . . , k} Bsp: 1a) Für jedes x in A, zähle die Anz. der Zahlen gleich x 1 2 3 4 5 6 7 8 A 3 0 4 1 3 4 1 4 0 ⇒ 1 2 3 4 C 1 2 0 2 3 1b) Für jedes x in A, berechne die Anz. der Zahlen ≤ x C 0 1 3 3 5 0 1 2 3 4 2) Schreibe jedes x in A direkt an die richtige Position in B B 0 1 1 3 3 4 4 4 1 2 3 4 5 6 7 8 CountingSort ist stabil! 5-3 CountingSort Plan: 1a) Für jedes x in A, zähle die Anz. der Zahlen gleich x 1b) Für jedes x in A, berechne die Anz. der Zahlen ≤ x 2) Schreibe jedes x in A direkt an die richtige Position in B Eingabefeld Ausgabefeld beschränkt Universum {0, . . . , k } CountingSort(A, B , k ) sei C [0..k ] = h0, 0, . . . , 0i ein neues Feld for j = 1 to A.length do C [A[j ]] = C [A[j ]] + 1 // (1a) // C [i ] enthält jetzt die Anz. der Elem. gleich i in A for i = 1 to k do C [i ] = C [i ] + C [i − 1] // (1b) // C [i ] enthält jetzt die Anz. der Elem. ≤ i in A for j = A.length downto 1 do Aufgabe: B [C [A[j ]]] = A[j ] // (2) Fülle die Felder mit Code, C [A[j ]] = C [A[j ]] − 1 der obige Idee umsetzt! 5-10 Laufzeit: O(n + k ) CountingSort Plan: 1a) Für jedes x in A, zähle die Anz. der Zahlen gleich x 1b) Für jedes x in A, berechne die Anz. der Zahlen ≤ x 2) Schreibe jedes x in A direkt an die richtige Position in B Eingabefeld Ausgabefeld beschränkt Universum {0, . . . , k } CountingSort(A, B , k ) sei C [0..k ] = h0, 0, . . . , 0i ein neues Feld for j = 1 to A.length do C [A[j ]] = C [A[j ]] + 1 // (1a) // C [i ] enthält jetzt die Anz. der Elem. gleich i in A for i = 1 to k do C [i ] = C [i ] + C [i − 1] // (1b) // C [i ] enthält jetzt die Anz. der Elem. ≤ i in A for j = A.length downto 1 do B [C [A[j ]]] = A[j ] // (2) C [A[j ]] = C [A[j ]] − 1 7 RadixSort (Jahr, Monat, Tag) Frage: Gegeben Liste von Menschen mit deren Geburtstagen. Wie würden Sie die Liste nach Alter sortieren? Drei (?) Lösungen: Geburtstage in Anz. Tage seit 1.1.1970 umrechnen, dann vergleichsbasiertes Sortierverfahren verwenden. Spezielle Vergleichsroutine schreiben und in vergleichsbasiertes Sortierverfahren einbauen. Liste 3× sortieren: je 1× nach Jahr, Monat, Tag. Aber in welcher Reihenfolge?? RadixSort(A, s ) Anz. Stellen (hier: 3) Laufzeit? for i = 1 to s do [1 = Index der niederwertigsten (!) Stelle] sortiere A stabil nach der i -ten Stelle z.B. mit CountingSort 9 BucketSort [CLRS] A 1 2 3 4 5 6 7 8 9 10 .78 .17 .39 .26 .72 .94 .21 .12 .23 .68 B 0 1 2 3 4 5 6 7 8 9 / .12 .21 .39 / .17 / .23 .26 / / / .68 / .72 .78 / / .94 / (c) www.seafish.org Eimerinhalt“: Verkettete Liste von Elementen aus A. ” Hilfsfeld B [0..n − 1]; jeder Eintrag entspricht einem Eimer“ der Weite 1/n ” Eingabefeld A[1..n] enthält Zahlen, Im Bsp. auf 2 Nachzufällig und gleichverteilt aus [0, 1) gezogen kommastellen gerundet! [ ] 10 BucketSort BucketSort(Feld A von Zahlen in [0, 1)) [CLRS] A 1 2 3 4 5 6 7 8 9 10 .78 .17 .39 .26 .72 .94 .21 .12 .23 .68 B 0 1 2 3 4 5 6 7 8 9 / .12 .21 .39 / .17 / .23 / / .68 / .72 / .94 / Korrektheit? .78 / .26 / n = A.length lege Feld B [0..n − 1] von Listen an for j = 1 to n do füge A[j ] in Liste B [bn · A[j ]c] ein ⇓ for i = 0 to n − 1 do sortiere Liste B [i ] = [ ni , i +1 n )∩A hänge B [0], . . . , B [n − 1] aneinander kopiere das Ergebnis nach A[1..n] 2 Fälle: – A[i ] und A[j ] in der gleichen Liste – A[i ] und A[j ] in verschiedenen Listen Laufzeit? – erwartet, hängt von den zufälligen Zahlen in A ab – hängt vom Sortieralgorithmus in Zeile 6 ab; wir nehmen InsertionSort: schnell auf kurzen Listen! 11 Erwartete Laufzeit von BucketSort TBS (n) = Θ (n) + Pn−1 i =0 TIS (ni ) = Θ (n) + Pn−1 i =0 O (ni2 ) Pn−1 E [TBS (n)] = E [Θ (n) + i =0 O (ni2 )] Pn−1 = Θ (n) + i =0 E [O (ni2 )] Pn−1 = Θ (n) + i =0 O (E [ni2 ]) = Θ (n) Behauptung: E [ni2 ] ≤ 2 − 1 n fest! Beweis. Def. Indikator-ZV Xj := 1, falls A[j ] in Eimer i fällt. Pn E [Xj ] = Pr[Xj = 1] = 1/n ⇒ ni = j =1 Xj P 2 P P n n n 2 ⇒ ni = = j =1 k =1 Xj Xk j =1 Xj Pn Pn P 2 = j =1 Xj + j =1 k 6=j Xj Xk 12 Erwartete Laufzeit von BucketSort Es gilt ni2 = E [ni2 ] ⇒ = Pn 2 X j =1 j + Pn 2 E [ X j ] j =1 Pn j =1 + P k 6=j Pn j =1 Xj Xk P k 6=j Behauptung: E [ni2 ] ≤ 2 − n1 E [Xj Xk ] Behandle die beiden Typen von Erwartungswerten getrennt: unabhängig von j ! E [X 2 ] = 1 · Pr[X 2 = 1] + 0 · Pr[X 2 = 0] j j j = 1 · Pr[Xj = 1] + 0 · Pr[Xj = 0] = 1 · E [Xj ] · E [Xk ] = E [Xj Xk ] = 1 n · 1 n = 1 n2 1 n +0= 1 n unabh. von j und k ! für j 6= k sind Xj und Xk unabhängig Fasse die Zwischenergebnisse zusammen: Pn Pn P 2 2 E [ni ] = j =1 E [Xj ] + j =1 k 6=j E [Xj Xk ] =n· 1 n + n · (n − 1) · 1 n2 = 1+ n−1 n = 2− 1 n 13 Zusammenfassung Jedes vergleichsbasierte Sortierverfahren braucht im schlechtesten Fall Ω (n log n) Vergleiche für n Zahlen. CountingSort sortiert Zahlen in {0, . . . , k } (stabil!) Laufzeit für n Zahlen: O (n + k ) RadixSort sortiert s -stellige b -adische Zahlen Laufzeit für n Zahlen: O (s · (n + b )) BucketSort sortiert gleichverteilte zufällige Zahlen erwartete Laufzeit für n Zahlen: O (n) Bem. Die Idee mit den (gleichgroßen) Eimern ist natürlich nicht nur auf Zufallszahlen beschränkt, aber hier läßt sie sich hübsch analysieren.