Übung Algorithmen I 20.5.15 Christoph Striecks Christoph.Striecks@kit.edu (Mit Folien von Julian Arz, Timo Bingmann und Sebastian Schlag.) Roadmap I Organisation I Mergesort, Quicksort I Dual Pivot Quicksort I Vorsortiertheit und adaptive Sortieren Organisation I Übungsblatt 5, Aufgabe 1c): Σ = {A, b, e, g , h, i, l , m, n, o, r , t} Sortieren durch Mischen Idee: Teile und Herrsche Function mergeSort(he1 , . . . , en i) : Sequence of Element if n = 1 then return he1 i // base case else return merge( mergeSort(he1 , . . . , ebn/2c i), e mergeSort(h bn/2c+1 , . . . , en i)) Gegeben: zwei sortierte Folgen a und b Berechne: sortierte Folge der Elemente aus a und b 172 Merge Sort Function mergeSort(A : Array of Element; lo, hi : N) if hi − lo ≤ 1 then return // Basisfall mid := (lo + hi)/2 // mittleres Element mergeSort(lo, mid ), mergeSort(mid , hi) // Sortiere Hälften T := allocate (Array of Element size hi − lo) i := lo, j := mid , k := 0 // Laufindizes while i < mid ∧ j < hi if A[i] < A[j] T [k++] := A[i++] // Mische! else T [k++] := A[j++] endwhile while i < mid do T [k++] := A[i++] // Kopiere Reste while j < hi do T [k++] := A[j++] A[lo, . . . , hi − 1] := T [0, . . . , (hi − lo) − 1] // Kopiere zurück dispose (T ) Worst case: Θ(n log n), average case Θ(n log n). Tatsächliche Laufzeit einer Implementierung I Average case: Θ(n log n) I n = 100, 100 log2 100 ≈ 664 I (Demo) Quicksort erster Versuch Idee: Teile-und-Herrsche aber verglichen mit mergesort andersrum. Leiste Arbeit vor rekursivem Aufruf Function quickSort(s : Sequence of if |s | ≤ 1 then return s pick “some” p ∈ s a:= he ∈ s : e < p i b:= he ∈ s : e = p i c := he ∈ s : e > p i return Element) a), b, concatenation of quickSort( : Sequence of Element c) and quickSort( 182 Quicksort Analyse im schlechtesten Fall Annahme: Pivot ist immer Minimum (oder Max.) der Eingabe ( Θ(1) T (n) = Θ(n) + T (n − 1) if if n = 1, n ≥ 2. ⇒ T (n) = Θ(n + (n − 1) + · · · + 1) = Θ n2 183 Schlechtester Fall: Beispiel 184 Quicksort Analyse im besten Fall Annahme: Pivot ist immer Median der Eingabe ( O(1) T (n) ≤ O(n) + 2T (bn/2c) ⇒ if if n = 1, n ≥ 2. (Master-Theorem) T (n) = O(n log n) Problem: Median bestimmen ist nicht so einfach 185 Satz: C¯ (n) ≤ 2n ln n ≤ 1.45n log n n n 2 C¯ (n) = ∑ ∑ i =1 j =i +1 j − i + 1 n n−i +1 2 i 1 2 3 j n 3..n 4..n 2.. =∑ ∑ . . . . i =1 k =2 k . . n n 2 n − 1 n..n ≤∑ ∑ n 0/ i =1 k =2 k n 1 (harmonische =2n ∑ k =2 k =2n(Hn − 1) ≤ 2n(1 + ln n − 1) = 2n ln n . =:k z }| { j −i +1 2..n 2..n − 1 2..n − 2 . . . 2..2 0/ Summe) 191 Einige Quicksort-Analysen Hoare (1962) “Ur”-Quicksort – Average Case für 1 zufällig gewähltes Pivot für n = 100 erwartete Vergleiche: = 2n(Hn − 1) ≈ 2n loge n 837, 5 921, 0 (Demo.) Wild, Nebel (2012) “Yaroslavskiy”-Quicksort – Average Case für 2 zufällig gewählte Pivots erwartete Vergleiche: = 1.9n loge n − 2.46n + O(log n) für n = 100 629 + O(log 100) Vorgefertigte Sortieralgorithmen in aktuellen Programmiersprachen Hinweis: Verwenden Sie diese Sortieralgorithmen anstatt eigene zu implementieren! C++ I Zahlen: Variante von Quick-Sort #include <algorithm> int numbers[] = {42, 7, 9, 18, 1, 123}; std::vector<int> vec(numbers, numbers + 6); std::sort(vec.begin(), vec.end()) I Allgemeine Elemente: Variante von Merge-Sort #include <algorithm> bool less_than(Elements& a, Elements& b) { /*...*/ } ... Elements* elements = createElements(n); std::sort(elements, elements + n, less_than) ...oder... std::stable_sort(elements, elements + n, less_than) Java I Zahlen: Variante von Quick-Sort int[] numbers = {42, 7, 9, 18, 1, 123}; java.util.Arrays.sort(numbers) I Allgemeine Elemente: Variante von Merge-Sort Comparator<Elem> comparator = new Comparator<Elem> { int compare(Elem a, Elem b) { /*...*/ } } Elem[] elements = { /*...*/ } java.util.Arrays.sort(elements, 3, 15, comparator) Dual Pivot Quicksort Dual Pivot Quicksort I Idee: Partitioniere Eingabe in 3 Teile durch 2 Pivot-Elemente p≤q I Historisch: [Sedgewick 1975], [Hennequin 1991] ⇒ keine Verbesserung durch Multi-Pivot-Ansatz in Theorie und Praxis Sind die nächsten 15 Minuten also reine Zeitverschwendung? Nein. I 2009: [Yaroslavskiy 2009] I praktisch & theoretisch besser als Implementierung der Bibliothek I 2011: Dual Pivot Quicksort wird Standard in Java 7 I 2012: Average-case-Analyse [Wild, Nebel 2012] Dual Pivot Quicksort 1. Wähle 2 Pivotelemente p ≤ q 2. Klassifiziere Elemente in: I I I klein mittel groß wenn wenn wenn ·<p p≤·≤q q<· 3. Ordne alle Elemente entsprechend ihrer Klasse: <p p p≤·≤q q 4. Sortierte die 3 Teilbereiche rekursiv >q Partitionierung mit 2 Pivots Fall a[k] < p: p k g q ? l Partitionierung: look @ g look @ k X swap(k, l) <p? X skip × × <q? × look @ g X >q? <p? swap(g, k, l) × X skip swap(g, k) Partitionierung mit 2 Pivots Fall a[k] < p: p k g q ? l Partitionierung: look @ g look @ k X swap(k, l) <p? X skip × × <q? × look @ g X >q? <p? swap(g, k, l) × X skip swap(g, k) Partitionierung mit 2 Pivots Fall p ≤ a[k] ≤ q: p k g q ? l Partitionierung: look @ g look @ k X swap(k, l) <p? X skip × × <q? × look @ g X >q? <p? swap(g, k, l) × X skip swap(g, k) Partitionierung mit 2 Pivots Fall p ≤ a[k] ≤ q: p lk g q ? Partitionierung: look @ g look @ k X swap(k, l) <p? X skip × × <q? × look @ g X >q? <p? swap(g, k, l) × X skip swap(g, k) Partitionierung mit 2 Pivots ··· p <pl k g q ? Partitionierung: look @ g look @ k X swap(k, l) <p? X skip × × <q? × look @ g X >q? <p? swap(g, k, l) × X skip swap(g, k) Partitionierung mit 2 Pivots Fall q ≤ a[k]: p <pl k g q ? Partitionierung: look @ g look @ k X swap(k, l) <p? X skip × × <q? × look @ g X >q? <p? swap(g, k, l) × X skip swap(g, k) Partitionierung mit 2 Pivots Fall q ≤ a[k]: p <pl k g q ? Partitionierung: look @ g look @ k X swap(k, l) <p? X skip × × <q? × look @ g X >q? <p? swap(g, k, l) × X skip swap(g, k) Partitionierung mit 2 Pivots Fall q ≤ a[k] ∧ a[g ] > q: p <pl k g q ? Partitionierung: look @ g look @ k X swap(k, l) <p? X skip × × <q? × look @ g X >q? <p? swap(g, k, l) × X skip swap(g, k) Partitionierung mit 2 Pivots Fall q ≤ a[k] ∧ a[g ] > q: p <pl k ? q g Partitionierung: look @ g look @ k X swap(k, l) <p? X skip × × <q? × look @ g X >q? <p? swap(g, k, l) × X skip swap(g, k) Partitionierung mit 2 Pivots Fall q ≤ a[k] ∧ p ≤ a[g ] ≤ q: p <pl k ? q g Partitionierung: look @ g look @ k X swap(k, l) <p? X skip × × <q? × look @ g X >q? <p? swap(g, k, l) × X skip swap(g, k) Partitionierung mit 2 Pivots Fall q ≤ a[k] ∧ p ≤ a[g ] ≤ q: p <pl k ? q g Partitionierung: look @ g look @ k X swap(k, l) <p? X skip × × <q? × look @ g X >q? <p? swap(g, k, l) × X skip swap(g, k) Partitionierung mit 2 Pivots ··· p <pl p≤·≤q k ? q g Partitionierung: look @ g look @ k X swap(k, l) <p? X skip × × <q? × look @ g X >q? <p? swap(g, k, l) × X skip swap(g, k) Partitionierung mit 2 Pivots Fall q ≤ a[k] ∧ a[g ] < p: p <pl p≤·≤q k ? q g Partitionierung: look @ g look @ k X swap(k, l) <p? X skip × × <q? × look @ g X >q? <p? swap(g, k, l) × X skip swap(g, k) Partitionierung mit 2 Pivots Fall q ≤ a[k] ∧ a[g ] < p: p <p l p≤·≤q k ? q g >q Partitionierung: look @ g look @ k X swap(k, l) <p? X skip × × <q? × look @ g X >q? <p? swap(g, k, l) × X skip swap(g, k) Einige Quicksort-Analysen Hoare (1962) “Ur”-Quicksort – Average Case für 1 zufällig gewähltes Pivot für n = 100 erwartete Vergleiche: = 2n(Hn − 1) ≈ 2n loge n 837, 5 921, 0 Wild, Nebel (2012) “Yaroslavskiy”-Quicksort – Average Case für 2 zufällig gewählte Pivots erwartete Vergleiche: = 1.9n loge n − 2.46n + O(log n) (Demo.) für n = 100 629 + O(log 100) Kennzahlen der Vorsortiertheit und adaptive Sortierverfahren Adaptives Sortieren Warm-up Sortieren Sie diese Zahlenfolgen: 1 3 2 4 6 5 7 8 8 2 7 1 5 6 3 4 Adaptives Sortieren Warm-up Sortieren Sie diese Zahlenfolgen: 1 3 2 4 6 5 7 8 Nur zwei Inversionen → Einfach! 8 2 7 1 5 6 3 4 Nicht so einfach! (17 Inversionen) Adaptives Sortieren Sortieren I Worst und average case: Ω (n log n) ist untere Schranke I Für zufällige Permutationen I Aber: nutze bestimmte Eigenschaften der Folge aus I Adaptives Sortieren: Laufzeit steigt mit Länge n und Chaos m Chaos? I Unsortiertheit? Vorsortiertheit? Ist dieses Chaos messbar? I Ja. → Kennzahlen der Vorsortiertheit I Englisch: measures of presortedness oder measures of disorder Adaptives Sortieren Kennzahlen der Vorsortiertheit I Inversionen I Runs I Größte Distanz zwischen Inversionen I Größte Distanz zur korrekten Position I Vertauschungen I Verschachtelte Listen I Removals I SUS I SMS I Oszillationen I Alle anderen zusammen Adaptives Sortieren Kennzahlen der Vorsortiertheit I Inversionen I Runs I Größte Distanz zwischen Inversionen I Größte Distanz zur korrekten Position I Vertauschungen I Verschachtelte Listen I Removals I SUS I SMS I Oszillationen I Alle anderen zusammen Adaptives Sortieren Kennzahlen der Vorsortiertheit Inversionen I Inversion: Paar (i, j) ∈ N2 mit i < j und σ(i) > σ(j) I Siehe Übung letzte Woche I Best case minv = 0, worst case minv = Average case minv = n2 · 21 = Θ n2 I n (n−1) 2 = Θ n2 Adaptiv bzgl. minv : Insertion Sort I O(minv ) Vertauschungen, O(n + minv ) Vergleiche Adaptives Sortieren Kennzahlen der Vorsortiertheit Inversionen I Nachteil: Lokale Sortiertheit wird nicht erkannt. I Beispiel: I Quadratische Anzahl von Inversionen, aber einfach zu sortieren 5 6 7 8 9 0 1 2 3 4 Runs I Ein Run ist eine zusammenhängende Teilfolge aufsteigend sortierter Elemente I Das obige Beispiel hat 2 Runs. I Best case mruns = 1, worst case mruns = n I Average case? Runs Erwartete Anzahl von Runs I Für festes n sei #k die Anzahl der Permutationen mit k Runs. I Beobachtung: Permutation mit k Runs hat k − 1 Abstiege Runs Erwartete Anzahl von Runs I Für festes n sei #k die Anzahl der Permutationen mit k Runs. I Beobachtung: Permutation mit k Runs hat k − 1 Abstiege Runs Erwartete Anzahl von Runs I Für festes n sei #k die Anzahl der Permutationen mit k Runs. I Beobachtung: Permutation mit k Runs hat k − 1 Abstiege Runs Erwartete Anzahl von Runs I Für festes n sei #k die Anzahl der Permutationen mit k Runs. I Beobachtung: Permutation mit k Runs hat k − 1 Abstiege Runs Erwartete Anzahl von Runs I Für festes n sei #k die Anzahl der Permutationen mit k Runs. I Beobachtung: Permutation mit k Runs hat k − 1 Abstiege I Rückwärts gelesen hat sie n − (k − 1) = n − k + 1 Runs. I → Bijektion von Permutationen mit k Runs auf Permutationen mit n − k + 1 Runs I → #k = #n−k+1 E(R(σ)) = X σ∈Sn p(σ) · R(σ) = n X #k k=1 n! k Runs Erwartete Anzahl von Runs Indexvertauschung 2 · E(R(σ)) = 2 = = n X k=1 #k k= n! n X #k k=1 n X k=1 n! k+ n X #k k+ n! n X k=1 k=1 s. letzte Folie z}|{ n X #k k=1 n! }| { z #n−k+1 (n − k + 1) n! (n − k + 1) n #k n+1X n+1 (k + (n − k + 1)) = #k = n! n! n! n! k=1 Daraus folgt: E(R(σ)) = n+1 2 Runs Adaptives Sortieren 3 8 4 7 2 6 1 8 Runs Adaptives Sortieren 3 3 8 4 8 | 4 7 2 7 | 2 6 1 6 | 1 8 8 Runs Adaptives Sortieren 3 3 8 4 8 | 4 7 2 7 | 2 6 1 6 | 1 8 8 Idee: Mergesort! Runs Adaptives Sortieren 3 3 8 4 8 | 4 7 2 7 | 2 6 1 6 | 1 8 8 Idee: Mergesort! Runs Adaptives Sortieren 3 3 3 8 4 8 | 4 7 2 7 | 2 6 1 6 | 1 8 8 Idee: Mergesort! Runs Adaptives Sortieren 3 3 3 8 4 8 | 4 4 7 2 7 | 2 6 1 6 | 1 8 8 Idee: Mergesort! Runs Adaptives Sortieren 3 3 3 8 4 8 | 4 4 7 7 2 7 | 2 6 1 6 | 1 8 8 Idee: Mergesort! Runs Adaptives Sortieren 3 3 3 8 4 8 | 4 4 7 7 2 7 | 2 8 | 6 1 6 | 1 8 8 Idee: Mergesort! Runs Adaptives Sortieren 3 3 3 8 4 8 | 4 4 7 7 2 7 | 2 8 | 1 6 1 6 | 1 2 6 8 8 Idee: Mergesort! 8 Runs Adaptives Sortieren 3 3 3 8 4 8 | 4 4 7 7 2 7 | 2 8 | 1 6 1 6 | 1 2 6 8 8 Idee: Mergesort! 8 Runs Adaptives Sortieren 3 3 3 1 8 4 8 | 4 4 7 7 2 7 | 2 8 | 1 6 1 6 | 1 2 6 8 8 Idee: Mergesort! 8 Runs Adaptives Sortieren 3 3 3 1 8 4 8 | 4 4 7 2 7 2 7 | 2 8 | 1 6 1 6 | 1 2 6 8 8 Idee: Mergesort! 8 Runs Adaptives Sortieren 3 3 3 1 8 4 8 | 4 4 7 2 3 7 2 7 | 2 8 | 1 4 5 6 1 6 | 1 2 6 6 7 8 8 Idee: Mergesort! 8 8 Runs Adaptives Sortieren 3 3 3 1 8 4 8 | 4 4 7 2 3 7 2 7 | 2 8 | 1 4 5 6 1 6 | 1 2 6 6 7 I Natürlicher Mergesort I Laufzeit O(n + n log mruns ) 8 8 Idee: Mergesort! 8 8 Adaptives Sortieren Kennzahlen der Vorsortiertheit Runs I Nachteil: Globale Sortiertheit wird nicht erkannt I Beispiel: I Viele Runs, aber nur zwei ineinander verschränkte aufsteigende Teilsequenzen 1 0 3 2 5 4 7 6 8 Removals I Removals: minimale Anzahl von Elementen, deren Löschen eine sortierte Folge erzeugt I Das Beispiel hat eine längste aufsteigende Teilsequenz der Länge 5 → mrem = 4 I Best case: mrem = 0; worst case: mrem = n − 1
© Copyright 2024