Übung 5

Übung Algorithmen I
20.5.15
Christoph Striecks
[email protected]
(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