Modul: Algorithmik WS2011/12

Werbung
Modul: Algorithmik
WS 2011/12
Volker Diekert1
16. Februar 2012
1
Übungen: Armin Weiß
1 / 400
1. Vorlesung
17.10.2011
2 / 400
Landau-Symbole-Oh-oh
Definition:
◮
O(f)
Es gilt g ∈ O(f ), falls
∃c > 0 ∃n0 ∀n ≥ n0 : g (n) ≤ c · f (n).
Also: g wächst nicht schneller als f .
◮
o(f)
Es gilt g ∈ o(f ), falls
∀c > 0 ∃n0 ∀n ≥ n0 : g (n) ≤ c · f (n).
Also: g wächst echt langsamer als f .
3 / 400
Landau-Symbole-Omega-Theta
◮
Ω(f)
g ∈ Ω(f ) ⇔ f ∈ O(g )
Also: g wächst mindestens so schnell wie f .
◮
ω(f)
g ∈ ω(f ) ⇔ f ∈ o(g )
Also: g wächst echt schneller als f .
◮
Θ(f)
g ∈ Θ(f ) ⇔ (f ∈ O(g ) ∧ g ∈ O(f ))
Dies heißt, g und f wachsen asymptotisch gleichschnell.
4 / 400
Reale Rechenzeiten - früher
Eine Eingabe der Länge n werde linear in n Millisekunden verarbeitet.
(Aus dem Buch von Aho, Hopcroft, Ullman, 1974)
Algorithmus
n
n log(n)
n2
n3
2n
1 Minute
Rechenzeit
60.000
4.893
244
39
15
1 Stunde
Rechenzeit
3.6 ∗ 106
200.000
1.897
153
21
Wachstum bei 10-facher
Beschleunigung
∗10
∗10
√ (für große Werte)
∗√10 (ca. 3.16)
∗ 3 10 (ca. 2.15)
+ log2 (10) (ca. 3.3)
Je schneller der Rechner, desto wichtiger sind effiziente
Algorithmen!
5 / 400
Reale Rechenzeiten - heute?
Es wird hier angenommen, dass ein linearer Algorithmus eine Eingabe
der Länge n in n Mikrosekunden verarbeitet:
Algorithmus
n
n log(n)
n2
n3
2n
1 Minute
Rechenzeit
1 Stunde
Rechenzeit
6, 0 · 107
2, 8 · 106
7.746
391
26
3, 6 · 109
1, 3 · 108
60.000
1.533
32
Wachstum bei 10-facher
Beschleunigung
∗10
∗10
√ (für große Werte)
∗√10 (ca. 3,16)
∗ 3 10 (ca. 2,15)
+ log2 (10) (ca. 3,32)
Heute immer noch: Je schneller der Rechner, desto mehr
lohnen sich effiziente Algorithmen!
6 / 400
Reale Rechenzeiten
Vergleich (1 Operation = 1 ms) vs. (1 Operation = 1 µs)
Algorithmus
n
n log(n)
n2
n3
2n
1 Minute Rechenzeit
1 OP=1 ms
1 OP=1 µs
6, 0 · 104
4.893
244
39
15
6, 0 · 107
2, 8 · 106
7.746
391
26
1 Stunde Rechenzeit
1 OP=1 ms
1 OP=1 µs
3, 6 · 106
2, 0 · 105
1.897
153
21
3, 6 · 109
1, 3 · 108
60.000
1.533
32
10-fache
Beschl.
∗ 10
∼√∗ 10
∗√10
∗ 3 10
+ log2 (10)
7 / 400
Komplexitätsmaße
◮
Komplexität im ungünstigsten Fall (worst case).
◮
Komplexität im Mittel (average case) benötigt eine
Wahrscheinlichkeitsverteilung der Eingabe.
Mittlerer Zeitbedarf:
tA,Mittel (n) = E (tA (x), |x| = n)
Dabei sei E (tA (x), |x| = n) der bedingte Erwartungswert von
tA (x) unter der Bedingung |x| = n
8 / 400
Beispiel: Quicksort
Beim Quicksort-Algorithmus ist die Anzahl der Vergleiche im
ungünstigsten Fall tQ (n) ∈ Θ(n2 ).
Mittlerer Zeitbedarf: tA,Mittel (n) = 1.38n log n
9 / 400
Durchschnittsanalyse
Die Durchschnittskomplexitätsanalyse ist oft schwierig.
Gleichverteilung der Eingaben gleicher Länge:
tA,Mittel (n) =
1 X
tA (x)
|Xn |
x∈Xn
mit
Xn := {x ∈ X | |x| = n}
Sinnvoll?
10 / 400
Beispiel binäre Suchbäume
Beispiel 1.1
Gleichverteilung
bei binären Suchbäumen:
1 2n
Es gibt n+1
n (= (n + 1)-te Catalansche Zahl) verschiedene
Binärbäume mit n Knoten.
2n
4n
∼√
n
πn
n! ∼
√
2πn
n n
e
√
Gleichverteilung: Mittlere Baumhöhe ist ungefähr n.
Realistischer: eine Verteilung auf der Menge der binären
Suchbäume, die sich durch eine Gleichverteilung von
Permutationen ergibt.
Damit: Mittlere Baumhöhe Θ(log(n)). 1 2n
n! wächst wesentlich schneller als n+1
n
11 / 400
Maschinenmodelle: Turingmaschine
Die Turingmaschine (TM) ist ein mathematisch leicht exakt
beschreibbares Berechnungsmodell. Allerdings ist der zeitraubende
Speicherzugriff (Bandzugriff) in der Realität nicht gegeben.
Arbeitet ein Algorithmus auf einer TM schnell, so ist er schnell!
12 / 400
Maschinenmodelle: Registermaschine
x1 x2 x3 x4 x5 x6 x7 x8 x9
.
.
.
Eingabe READ ONLY
Speicher
RAM
0 = Akku
1 = 1.Reg
2 = 2.Reg
3 = 3.Reg
4 = 4.Reg
IC
✲
.
Programm
.
.
y1 y2 y3 y4 y5 y6 y7 y8 y9
.
.
.
Ausgabe WRITE ONLY
13 / 400
Unter Schranken / Beispiel Sortieren
Einer der schwierigen Aspekte der Algorithmentheorie ist die Suche
nach unteren Schranken.
Satz 1.2 (Sortieralgorithmen)
Jeder Sortieralgorithmus, der ausschließlich auf Schlüsselvergleichen
basiert, benötigt Ω(n log n) Vergleiche.
Beweis: Wir fassen Sortieralgorithmen als Entscheidungsbäume auf,
d.h. als Binärbäume, deren inneren Knoten mit Vergleichen der Art
gilt x[i ] < x[j]?“ beschriftet sind. Es gibt n! Permutationen von n
”
Elementen. Fasst man jede Permutation als einen Pfad durch einen
Entscheidungsbaum auf, so muss dieser mindestens n! Blätter haben.
Jeder binäre Baum mit n! Blättern hat eine Mindesthöhe von
log(n!) ∈ Ω(n log(n)). Die Höhe des Entscheidungsbaums entspricht
aber dem Zeitaufwand des Algorithmus.
14 / 400
Beispiel Zwei gleiche Zahlen in einem Feld“
”
Sei ein Feld a[1...n] gegeben. Frage: existiert i 6= j mit a[i ] = a[j]?
Satz 1.3 (Zwei gleiche Zahlen in einem Feld)
Jeder vergleichsbasierte Algorithmus, der die Existenz von zwei
gleichen Zahlen in einem Feld überprüft, benötigt Ω(n log(n))
Vergleiche.
15 / 400
Lösung einfacher Rekursionsgleichungen
Satz 1.4
Seien a, b ∈ N und b > 1, g : N −→ N und es gelte die
Rekursionsgleichung:
t(1) = g (1)
t(n) = a · t(n/b) + g (n)
Dann gilt für n = b k (d.h. für k = logb (n)):
t(n) =
k
X
i =0
ai · g
n
bi
.
16 / 400
Beweis des Satzes
k = 0 : Es gilt n =b 0 =
und t(1) = g(1).
P1k−1
n
k > 0 : Daher t b = i =0 ai · g bin+1 mit Induktion,
also t(n) = a · t
= a
n
b
k−1
X
i =0
=
=
k
X
i =1
k
X
i =0
+ g (n)
!
n ai · g
+ g (n)
b i +1
ai · g
n
ai · g
n
bi
bi
+ a0 g
n
b0
.
17 / 400
Mastertheorem I
Korollar 1.5 (Mastertheorem I)
Seien a, b ∈ N, mit b > 1 und es gelte die Rekursionsgleichung:
t(n) ∈ a · t(n/b) + Θ(nc )
Dann gilt:
Bemerkung:

c

; falls a < b c
 Θ(n )
Θ(nc log n) ; falls a = b c
t(n) ∈
log a

 Θ(n log
b)
; falls a > b c
log a
log b
= logb a. Falls a > b c , so ist logb a > c.
18 / 400
Beweis Mastertheorem I
Sei g (n) = nc . Damit ist t(n) = nc ·
1. Fall a < b c : t(n) ≤ nc ·
Pk
a i
i =0 ( bc )
nach dem Satz.
∞ X
a i
1
= nc ·
∈ O(nc ).
bc
1 − bac
i =0
Außerdem gilt t(n) ∈ Ω(nc ). Hieraus folgt t(n) ∈ Θ(nc ).
2. Fall a = b c : t(n) = (k + 1) · nc ∈ Θ(nc log n).
19 / 400
Beweis Mastertheorem I
3. Fall a > b c :
k a k+1
X
−1
a i
c ( bc )
=
n
·
a
c
b
bc − 1
i =0
a logb (n) Θ nc · c
b
!
nc · alogb (n)
Θ
b c logb (n)
Θ alogb (n) = Θ b logb (a)·logb (n)
Θ nlogb (a)
t(n) = nc ·
∈
=
=
=
20 / 400
Beispiel Mergesort
Algorithmus 1.1 Mergesort
procedure mergesort(l , r )
var m : integer;
begin
if (l < r ) then
m := (r + l ) div 2;
mergesort(l , m);
mergesort(m + 1, r );
mische(l , m, r );
endif
endprocedure
21 / 400
Mastertheorem II
Satz 1.6 (Mastertheorem II)
Sei r > 0,
Pr
i =0 αi
< 1 und für eine Konstante c sei
!
r
X
t(⌈αi n⌉) + c · n
t(n) ≤
i =0
Dann gilt t(n) ∈ O(n).
22 / 400
Beweis Mastertheorem II
Wähle ein ε und ein n0 > 0 so, dass
α
Pi nr 0 ≤ n0 − 1 und
Pr dass
i =0 ⌈αi n⌉ ≤ ( i =0 αi ) · n + r + 1 ≤ (1 − ε)n für alle n ≥ n0 .
Wähle ein γ so, dass c < γε und t(n) < γn für alle n < n0 .
Für den Induktionsschritt (n ≥ n0 ) gilt:
!
r
X
t(⌈αi n⌉) + cn
t(n) ≤
≤
i =0
r
X
i =0
!
γ⌈αi n⌉
+ cn
(mit Induktion)
≤ (γ(1 − ε) + c)n
≤ γn
23 / 400
Entwurfstrategien
1. Strategie
Teile und Beherrsche – Divide and Conquer
24 / 400
Beispiele für Divide and Conquer
Quicksort
Mergesort
weitere Beispiele (hier):
Multiplikation ganzer Zahlen: Matrixmultiplikation nach Strassen
25 / 400
Multiplikation ganzer Zahlen
Schulmäßige Multiplikation von zwei Binärzahlen der Länge n
erfordert O(n2 ) Operationen. Anderer Ansatz:
r =
A
B
s =
C
D
Dabei sind A (C ) die ersten und B (D) die letzten k Bits von r (s).
r = A 2k + B;
s = C 2k + D
r s = A C 22k + (A D + B C ) 2k + B D
Mastertheorem I: tmult-ng (n) = 4 · tmult-ng (n/2) + O(n) ∈ O(n2 )
Nichts gewonnen!
26 / 400
Multiplikation ganzer Zahlen
Berechne stattdessen besser rekursiv AC , (A − B)(D − C ) und BD.
Aufwand: 3 · tmult (n/2) + O(n)
Damit:
rs = A C 22k + (A − B) (D − C ) 2k + (B D + A C ) 2k + B D
Gesamtaufwand nach dem Mastertheorem I:
log 3
tmult (n) = 3 · tmult (n/2) + O(n) ∈ O(n log 2 ) = O(n1.58496... ).
Wir haben also durch den Teile-und-Beherrsche Ansatz den
Exponenten des naiven Ansatzes von 2 auf 1.58496... heruntergesetzt.
27 / 400
Schnelle Multiplikation
Nach einem Verfahren von Schönhage-Strassen (diskrete FFT in
geeigneten Restklassenringen) lassen sich zwei Binärzahlen der Länge
n in der Zeit O(n log n log log n) multiplizieren. (Der log log n-Term
lässt sich sogar noch weiter verkleinern.)
28 / 400
Matrixmultiplikation naiver Divide-and-Conquer
Die übliche Multiplikation
zweier n × n Matrizen
P
(ai ,j ) (bi ,j ) = ( nk=1 ai ,k bk,j ) erfordert O(n3 ) skalare Multiplikationen.
Divide-and-Conquer: A, B werden in 4 etwa gleichgroßen
Untermatrizen unterteilt, wobei sich das Produkt AB = C wie folgt
darstellen lässt:
A11
B11
A12
C11
B12
C12
=
A21
B21
A22
C21
B22
C22
29 / 400
Matrixmultiplikation naiver Divide-and-Conquer
A11
A21
A12
A22
B11
B21
B12
B22
=
C11
C21
C12
C22
Dabei ergeben sich folgende Beziehungen:
C11 = A11 B11 + A12 B21
C12 = A11 B12 + A12 B22
C21 = A21 B11 + A22 B21
C22 = A21 B12 + A22 B22
Also:
t(n) ∈ 8 · t(n/2) + Θ(n2 ) ∈ Θ(n3 )
Erneut keine Verbesserung.
30 / 400
Matrixmultiplikation nach Strassen
Berechne das Produkt von 2 × 2 Matrizen mit 7 Multiplikationen:
M1
:=
M2
:=
(A12 − A22 )(B21 + B22 )
M1 + M2 − M4 + M6
C11
=
(A11 + A22 )(B11 + B22 )
C12
=
M4 + M5
(A11 − A21 )(B11 + B12 )
C21
=
M6 + M7
C22
=
M2 − M3 + M5 − M7
M3
:=
M4
:=
(A11 + A12 )B22
M5
:=
M6
:=
A11 (B12 − B22 )
M7
:=
A22 (B21 − B11 )
(A21 + A22 )B11
Laufzeit: t(n) ∈ 7t(n/2) + Θ(n2 ).
Mastertheorem I (a = 7, b = 2, c = 2):
t(n) ∈ Θ(nlog2 7 ) = Θ(n2,81... )
Theoretischer Durchbruch 1968, Anwendungen z.B. im Flugzeugbau.
31 / 400
Wortproblem für kontext-freie Sprachen
Das Wortproblem für kontext-freie Sprachen lässt sich in der Zeit
O(n3 ) lösen: Stichwort CYK.
Eine Reduktion auf Multiplikation boolescher Matrizen liefert nach
Strassen:
O(nlog2 7 ).
32 / 400
2. Vorlesung, 18.10.2011
2. Strategie
Gierige Algorithmen – Greedy Algorithms
33 / 400
Optimalitätsprinzip
Greedy ( gierig“) bezeichnet Lösungsstrategien, die auf der
”
schrittweisen Berechnung von Teillösungen (lokalen Optima) basieren.
Dieses Verfahren eignet sich für Probleme, bei denen jede Teilfolge
einer optimalen Folge auch optimal ist (Optimalitätsprinzip).
34 / 400
Kürzeste Wege
Klassisches Beispiel für die Greedy-Strategie ist die Bestimmung
kürzester Wege (von einer Quelle aus) in einem Graphen mit positiven
Kantengewichten.
Graph: G = (V , E , γ) mit
V die Knotenmenge,
E ⊆ V × V die gewichteten Kanten,
γ : E → N0 die Gewichtsfunktion.
Das Gewicht eines Pfades ist gleich der Summe der Kantengewichte.
Sei d(u, v ) für u, v ∈ V das Minimum der Gewichte aller Pfade von u
nach v (mit der üblichen Konvention, dass d(u, v ) = ∞ gilt, falls kein
Pfad von u nach v existiert).
Die Aufgabe ist es, bei gegebenen Graphen G und Knoten u ∈ V für
jedes v ∈ V einen Pfad
P u = u0 , u1 , u2 , . . . , un−1 , un = v mit
minimalem Gewicht ni=1 γ(ui −1 , ui ) = d(u, v ) zu bestimmen.
35 / 400
Beispiel Dijkstra-Algorithmus
Startknoten
0
1
1
6
3
∞
3
8
2
13
4
∞
∞
3
6
∞
36 / 400
Beispiel Dijkstra-Algorithmus
Startknoten
0
1
1
6
3
7
3
8
2
13
4
∞
∞
3
6
14
37 / 400
Beispiel Dijkstra-Algorithmus
Startknoten
0
1
1
6
3
7
3
8
2
13
4
11
7
3
6
14
38 / 400
Beispiel Dijkstra-Algorithmus
Startknoten
0
1
1
6
3
7
3
8
2
13
4
11
7
3
6
13
39 / 400
Beispiel Dijkstra-Algorithmus
Startknoten
0
1
1
6
3
7
3
8
2
13
4
9
7
3
6
13
40 / 400
Beispiel Dijkstra-Algorithmus
Startknoten
0
1
1
6
3
7
3
8
2
13
4
9
7
3
6
12
41 / 400
Beispiel Dijkstra-Algorithmus
Startknoten
0
1
1
6
3
7
3
8
2
13
4
9
7
3
6
12
42 / 400
Dijkstra-Algorithmus
B := ∅;
R := {u};
U := V \{u};
v (u) := nil;
D(u) := 0;
while R 6= ∅ do
x := nil; α := ∞;
forall y ∈ R do
if D(y ) < α then
x := y ;
α := D(y )
endif
endfor
B := B ∪ {x};
R := R\{x}
forall (x, y ) ∈ E do
if y ∈ U then
D(y ) := D(x) + γ(x, y );
v (y ) := x; U := U\{y };
R := R ∪ {y }
elsif y ∈ R and D(x) + γ(x, y ) < D(y ) then
D(y ) := D(x) + γ(x, y );
v (y ) := x
endif
endfor
endwhile
43 / 400
Komplexität Dijkstra
Die Komplexität des Dijkstra–Algorithmus hängt von der Verwaltung
des Randes R ab. Die Verwaltung als Feld ist nur für dichte Graphen
optimal; in der Praxis sind jedoch viele Graphen dünn, z.B. ist die
Zahl der Kanten e in planaren Graphen linear in der Knotenzahl n
(genauer gilt die Eulerformel: e ≤ 3n − 6 für n ≥ 3). Folgende
Operationen werden benötigt:
insert
decrease-key
delete-min
Füge ein neues Element in R ein.
Verringere den Schlüsselwert eines Elements von R
(und erhalte die Eigenschaften des Datentyps R).
Suche ein Element mit minimalem Schlüsselwert
und entferne dieses aus R (und erhalte die Eigenschaften des Datentyps R).
44 / 400
Komplexität Dijkstra
B := ∅; R := {u}; U := V \ {u}; v (u) := nil; D(u) := 0;
while (R 6= ∅) do
x := delete-min(R);
B := B ∪ {x};
forall (x, y ) ∈ E do
if y ∈ U then
U := U \ {y }; v (y ) := x; D(y ) := D(x) + γ(x, y );
insert(R, y , D(y ));
elsif D(x) + γ(x, y ) < D(y ) then
v (y ) := x; D(y ) := D(x) + γ(x, y );
decrease-key(R, y , D(y ));
endif
endfor
endwhile
45 / 400
Bemerkungen zum Dijkstra-Algorithmus
1. Randverwaltung als Feld ergibt ein Aufwand von O(n2 ).
2. Für dünne Graphen (O(e) ⊆ o(n2 / log n)) ist es günstiger, den
Rand R in einer Prioritätswarteschlange (Heap) zu verwalten.
Der Aufwand des Algorithmus wird damit nach der obigen
Herleitung O(e log n + n log n) ⊆ O(e log n).
3. Bei Verwendung der Datenstruktur der Fibonacci-Heaps ergibt
sich ein Aufwand von O(e + n log n). (→ später)
4. Der Dijkstra-Algorithmus liefert nicht notwendigerweise ein
korrektes Ergebnis, falls für die Kantengewichte auch negative
Zahlen erlaubt sind.
46 / 400
Problem negativer Kantengewichte
Startknoten
0
1
1
6
3
∞
3
8
2
-13
4
∞
∞
3
6
∞
47 / 400
Problem negativer Kantengewichte
Startknoten
0
1
1
6
3
7
3
8
2
-13
4
∞
∞
3
6
∞
48 / 400
Problem negativer Kantengewichte
Startknoten
0
1
1
6
3
7
3
8
2
-13
4
11
7
3
6
∞
49 / 400
Problem negativer Kantengewichte
Startknoten
0
1
1
6
3
7
3
8
2
-13
4
11
7
3
6
13
50 / 400
Problem negativer Kantengewichte
Startknoten
0
1
1
6
3
7
3
8
2
-13
4
9
7
3
6
13
51 / 400
Problem negativer Kantengewichte
Startknoten
0
1
1
6
3
7
3
8
2
-13
4
9
7
3
6
12
52 / 400
Problem negativer Kantengewichte
Startknoten
0
1
1
6
3
7
3
8
2
-13
4
9
7
3
6
12
53 / 400
Problem negativer Kantengewichte
Startknoten
0
1
−∞
3
−∞
6
3
8
2
4
Negativer Zyklus
-13
7
−∞
3
6
−∞
54 / 400
Lösung: Bellman-Ford-Algorithmus
Wikipedia sagt:
Der Algorithmus von Bellman und Ford (nach seinen Erfindern
Richard Bellman und Lester Ford) dient der Berechnung der kürzesten
Wege ausgehend von einem Startknoten in einem kantengewichteten
Graphen. Anders als beim Algorithmus von Dijkstra, dürfen hier die
Gewichte der Kanten auch negativ sein. Kreise negativen Gewichtes,
die vom Startknoten aus erreichbar sind, müssen jedoch
ausgeschlossen werden, denn andernfalls könnten diese beliebig oft
durchlaufen und somit Wege immer geringeren Gewichts konstruiert
werden. Der Bellman-Ford-Algorithmus kann das Vorhandensein von
Kreisen negativen Gewichtes erkennen.
55 / 400
Minimale Aufspannende Bäume
Definition 1.7
Ein Graph G = (V , E ) heißt zusammenhängend, wenn je zwei Knoten
durch einen Pfad verbunden sind.
Definition 1.8
Ein Baum ist ein zusammenhängender, kreisfreier, ungerichteter
Graph.
Bemerkung 1.9
Jeder Baum mit n Knoten besitzt genau n − 1 Kanten.
Definition 1.10
Ein minimaler aufspannender Baum (minimal spanning tree, MSB) zu
einem gewichteten Graphen G = (V , E , γ) ist ein Baum
B = (V , P
F , γ|F ) mit F ⊆ E mit minimalem Gewicht
γ(B) := e∈F γ(e).
56 / 400
Prim-Algorithmus
wähle x0 ∈ V beliebig; B := {x0 }; R := ∅, U := V \ {x0 }; T := ∅;
forall (x0 , y ) ∈ E do
U := U \ {y }; v (y ) := x0 ; D(y ) := γ(x, y ); insert(R, y , D(y ))
endfor
while R 6= ∅ do
x := delete-min(R); B := B ∪ {x}; T := T ∪ {v (x)x};
forall (x, y ) ∈ E , y 6∈ B do
if y ∈ U then
U := U \ {y }; v (y ) := x; D(y ) := γ(x, y ); insert(R, y , D(y ));
elsif γ(x, y ) < D(y ) then
v (y ) := x; D(y ) := γ(x, y ); decrease-key(R, y , D(y ));
endif
endfor
endwhile
return T
57 / 400
Beispiel Prim-Algorithmus
Startknoten
1
3
6
8
2
4
13
3
6
58 / 400
Beispiel Prim-Algorithmus
Startknoten
1
3
6
8
2
4
13
3
6
59 / 400
Beispiel Prim-Algorithmus
Startknoten
1
3
6
8
2
4
13
3
6
60 / 400
Beispiel Prim-Algorithmus
Startknoten
1
3
6
8
2
4
13
3
6
61 / 400
Beispiel Prim-Algorithmus
Startknoten
1
3
6
8
2
4
13
3
6
62 / 400
Beispiel Prim-Algorithmus
Startknoten
1
3
6
8
2
4
13
3
6
63 / 400
Beispiel Prim-Algorithmus
Startknoten
1
3
6
8
2
4
13
3
6
64 / 400
Korrektheit des Prim-Algorithmus
Idee: Austauschsatz
Bi −1
y′
Ri −1
∈T
y
v (x)
6∈ T
x
65 / 400
Entwurfsstrategie: Dynamisches Programmieren
Beim Verfahren der dynamischen Programmierung werden tabellarisch
alle Teillösungen eines Problems bestimmt, bis schließlich die
Gesamtlösung erreicht ist. Die Teillösungen werden dabei mit Hilfe der
bereits existierenden Einträge berechnet.
66 / 400
Dynamisches Programmieren vs Backtracking
Dynamischen Programmieren ist eng verwandt mit der Problemlösung
durch Backtracking. Die zusätzliche Idee ist, Rekursion durch Iteration
zu ersetzen und durch tabellarisches Festhalten von Teillösungen
Mehrfachberechnungen zu vermeiden.
BEISPIEL: Erinnerung an die Theorie-Vorlesung: Satz von Kleene.
Bestimmung reguläre Ausdrücke:
NEA A = (Q, Σ, δ ⊆ Q × Σ × Q, I , F ),
Lk [i , j] = Sprache von i nach j über erlaubte Zwischenzustände aus
der Menge {1, . . . , k}.
L0 [i , j] = Kantenbeschriftung, (bzw. ε).
Gesucht Ln [i , j] für alle i ∈ I und j ∈ F .
Lk [i , j] := Lk−1 [i , j] + Lk−1 [i , k] · Lk−1 [k, k]∗ · Lk−1 [k, j]
67 / 400
Bestimmung reguläre Ausdrücke
Algorithmus 1.2 Reguläre Ausdrücke aus einem endlichen Automaten
Eingabe : NEA A = (Q, Σ, δ ⊆ Q × Σ × Q, I , F )
procedure NEA2REGEXP
(Initialisiere: L[i , j] := {a | (i , a, j) ∈ δ ∨ a = ε ∧ i = j})
begin
for k := 1 to n do
for i := 1 to n do
for j := 1 to n do
L[i , j] := L[i , j] + L[i , k] · L[k, k]∗ · L[k, j]
endif
endfor
endfor
endfor
end
68 / 400
Transitive Hülle
Algorithmus 1.3 Warshall-Algorithmus: Berechnung transitiver Hülle
Eingabe : Graph als Adjazenzmatrix (A[i , j]) ∈ Booln×n
procedure Warshall (var A : Adjazenzmatrix)
begin
for k := 1 to n do
for i := 1 to n do
for j := 1 to n do
if (A[i , k] = 1) and (A[k, j] = 1) then
A[i , j] := 1
endif
endfor
endfor
endfor
end
69 / 400
Korrektheit: Warshall
Die Korrektheit des Warshall-Algorithmus folgt aus der Invarianten:
1. Nach dem k-ten Durchlauf der ersten for-Schleife gilt A[i , j] = 1,
falls ein Pfad von i nach j über Knoten mit Nummern ≤ k
existiert.
– Beachte, k steht ganz außen!
2. Gilt A[i , j] = 1, so existiert ein Pfad von i nach j.
70 / 400
Transitive Hülle?
Algorithmus 1.4 Ist dies Verfahren korrekt?
Eingabe : Graph als Adjazenzmatrix (A[i , j]) ∈ Booln×n
procedure Warshall (var A : Adjazenzmatrix)
begin
for i := 1 to n do
for j := 1 to n do
for k := 1 to n do
if (A[i , k] = 1) and (A[k, j] = 1) then
A[i , j] := 1
endif
endfor
endfor
endfor
end
71 / 400
Antwort
Nein!
72 / 400
Von Warshall zu Floyd
Trägt man in die Adjazenz-Matrix vom Warshall-Algorithmus
Kantengewichte statt Boolesche Werte ein, so entsteht der
Floyd-Algorithmus zur Berechnung kürzester Wege.
73 / 400
Floyd-Algorithmus
Algorithmus 1.5 Floyd: Alle kürzesten Wege in einem Graphen
Eingabe : Gewichteter Graph als Adjazenzmatrix A[i , j] ∈ (N ∪ ∞)n×n ,
wobei A[i , j] = ∞ bedeutet, dass es keine Kante von i nach j gibt.
procedure Floyd (var A : Adjazenzmatrix)
begin
for k := 1 to n do
for i := 1 to n do
for j := 1 to n do
A[i , j] := min{A[i , j], A[i , k] + A[k, j]};
endfor
endfor
endfor
endprocedure
74 / 400
Floyd-Algorithmus
Der Floyd-Algorithmus liefert ein korrektes Ergebnis auch wenn die
Gewichte negativ sind, unter der Bedingung, dass keine negative
Schleifen vorhanden sind.
Zeitaufwand von Warshall und Floyd ist Θ(n3 ). Verbesserung“
”
dadurch, dass vor der j-Schleife zuerst getestet wird, ob A[i , k] = 1
(bzw. ob A[i , k] < ∞) gilt.
Damit erreicht man den Aufwand O(n3 ):
75 / 400
Floyd-Algorithmus
Algorithmus 1.6 Floyd-Algorithmus in O(n3 )
Eingabe : Adjazenzmatrix A[i , j] ∈ (N ∪ ∞)n×n
procedure Floyd (var A : Adjazenzmatrix)
begin
for k := 1 to n do
for i := 1 to n do
if A[i , k] < ∞ then
for j := 1 to n do
A[i , j] := min{A[i , j], A[i , k] + A[k, j]};
endfor
endif
endfor
endfor
endprocedure
76 / 400
Floyd-Algorithmus
Algorithmus 1.7 Floyd-Algorithmus mit negativen Zyklen
Eingabe : Adjazenzmatrix A[i , j] ∈ (Z ∪ ∞ ∪ −∞)n×n
procedure Floyd (var A : Adjazenzmatrix)
begin
for k := 1 to n do for i := 1 to n do
if A[i , k] < ∞ then for j := 1 to n do
if A[k, j] < ∞ then
if A[k, k] < 0
then A[i , j] := −∞
else A[i , j] := min{A[i , j], A[i , k] + A[k, j]}
endif
endif
endfor endif endfor endfor
endprocedure
77 / 400
3. Vorlesung 24.10.2011
Dynamisches Programmieren
78 / 400
Transitive Hülle und Matrixmultiplikation
Sei A = (aij ) die Adjazenzmatrix eines gerichteten Graphen mit n
Knoten. Der Warshall-Algorithmus berechnet den reflexiven
transitiven Abschluss A∗ in O(n3 ) Schritten. Hierbei ist
X
A∗ =
Ak
mit A0 = In und ∨ als Addition boolescher Matrizen
k≥0
Mit Induktion ergibt sich leicht, dass Ak (i , j) = 1 genau dann gilt,
wenn P
es von i nach j einen Weg der Länge k gibt. Klar ist auch
k
A∗ = n−1
k=0 A .
79 / 400
Transitive Hülle ≤ Matrixmultiplikation
Setze B = In + A. Dann gilt A∗ = B m für alle m ≥ n − 1. Also reicht
es, eine Matrix ⌈log2 (n − 1)⌉-mal zu quadrieren, um A∗ zu berechnen.
Sei M(n) der Aufwand, zwei boolesche n × n-Matrizen zu
multiplizieren, und sei T (n) der Aufwand, die reflexive transitive Hülle
zu berechnen. Dann gilt also:
T (n) ∈ O(M(n) · log n).
Hieraus folgt für alle ε > 0 nach Strassen
T (n) ∈ O(nε+log2 7 ).
80 / 400
Matrixmultiplikation ≤ Transitive Hülle
Die Beziehung M(n) ∈ O(T (n)) gilt unter der plausiblen Annahme
M(3n) ∈ O(M(n)). Denn seien A und B beliebige Matrizen, dann gilt:

∗ 

0 A 0
In A AB
 0 0 B  =  0 In B  .
0 0 0
0 0 In
Unter den (ebenfalls plausiblen) Annahmen M(n) ∈ Ω(n2 ) und
M(2n) ≥ (2 + ε)M(n) zeigen wir
T (n) ∈ O(M(n)).
Dies bedeutet: die Berechnung der transitiven Hülle ist bis auf
konstante Faktoren genauso aufwendig wie die Matrixmultiplikation.
81 / 400
Berechnung der Transitive Hülle
Eingabe: E ∈ Bool(n × n)
1. Teile E in vier Teilmatrizen A, B, C , D so, dass A und D quadratisch sind und jede Matrix ungefähr die Größe n/2 × n/2 hat:
A B
E=
.
C D
2. Berechne rekursiv D ∗ : Aufwand T (n/2).
3. Berechne F = A + BD ∗ C : Aufwand O(M(n/2)), da
M(n) ∈ Ω(n2 ).
4. Berechne rekursiv F ∗ : Aufwand T (n/2).
5. Setze
∗
E =
F∗
F ∗ BD ∗
∗
∗
∗
D CF D + D ∗ CF ∗ BD ∗
.
82 / 400
Berechnung der Transitive Hülle
Damit erhalten wir die Rekursionsgleichung
T (n) ≤ 2T (n/2) + c · M(n)
für ein c > 0.
Dies ergibt
P
i · M(n/2i )
2
k≥0
i
P
2
≤ c · k≥0 2+ε
· M(n)
T (n) ≤ c ·
(nach Mastertheorem)
(da M(n/2) ≤
1
2+ε M(n))
∈ O(M(n)).
83 / 400
Beispiel: Multiplikation einer Matrizenfolge
Multiplikation von links
(10 × 1)
(1 × 10)
(10 × 1)
(1 × 10)
(10 × 1)
84 / 400
Beispiel: Multiplikation einer Matrizenfolge
Multiplikation von links
(10 × 1)
(1 × 10)
(10 × 1)
(1 × 10)
(10 × 1)
(10 × 1)
(1 × 10)
(10 × 1)
100 Multiplikationen
(10 × 10)
85 / 400
Beispiel: Multiplikation einer Matrizenfolge
Multiplikation von links
(10 × 1)
(1 × 10)
(10 × 1)
(1 × 10)
(10 × 1)
(10 × 1)
(1 × 10)
(10 × 1)
(1 × 10)
(10 × 1)
100 Multiplikationen
(10 × 10)
100 Multiplikationen
(10 × 1)
86 / 400
Beispiel: Multiplikation einer Matrizenfolge
Multiplikation von links
(10 × 1)
(1 × 10)
(10 × 1)
(1 × 10)
(10 × 1)
(10 × 1)
(1 × 10)
(10 × 1)
(1 × 10)
(10 × 1)
100 Multiplikationen
(10 × 10)
100 Multiplikationen
(10 × 1)
100 Multiplikationen
(10 × 10)
(10 × 1)
87 / 400
Beispiel: Multiplikation einer Matrizenfolge
Multiplikation von links
(10 × 1)
(1 × 10)
(10 × 1)
(1 × 10)
(10 × 1)
(10 × 1)
(1 × 10)
(10 × 1)
(1 × 10)
(10 × 1)
100 Multiplikationen
(10 × 10)
100 Multiplikationen
(10 × 1)
100 Multiplikationen
(10 × 10)
100 Multiplikationen
(10 × 1)
(10 × 1)
88 / 400
Beispiel: Multiplikation einer Matrizenfolge
Multiplikation von links
Insgesamt: 400 Multiplikationen
89 / 400
Beispiel: Multiplikation einer Matrizenfolge
Multiplikation von rechts
(10 × 1)
(1 × 10)
(10 × 1)
(1 × 10)
(10 × 1)
90 / 400
Beispiel: Multiplikation einer Matrizenfolge
Multiplikation von rechts
(10 × 1)
(1 × 10)
(10 × 1)
(10 × 1)
(1 × 10)
(10 × 1)
(1 × 10)
10 Multiplikationen
(10 × 1)
(1 × 1)
91 / 400
Beispiel: Multiplikation einer Matrizenfolge
Multiplikation von rechts
(10 × 1)
(1 × 10)
(10 × 1)
(10 × 1)
(1 × 10)
(10 × 1)
(10 × 1)
(1 × 10)
(1 × 10)
10 Multiplikationen
10 Multiplikationen
(10 × 1)
(1 × 1)
(10 × 1)
92 / 400
Beispiel: Multiplikation einer Matrizenfolge
Multiplikation von rechts
(10 × 1)
(1 × 10)
(10 × 1)
(10 × 1)
(1 × 10)
(10 × 1)
(10 × 1)
(1 × 10)
10 Multiplikationen
(10 × 1)
(1 × 1)
10 Multiplikationen
10 Multiplikationen
(10 × 1)
(1 × 10)
(10 × 1)
(1 × 1)
93 / 400
Beispiel: Multiplikation einer Matrizenfolge
Multiplikation von rechts
(10 × 1)
(1 × 10)
(10 × 1)
(10 × 1)
(1 × 10)
(10 × 1)
(10 × 1)
(1 × 10)
10 Multiplikationen
(10 × 1)
(1 × 1)
10 Multiplikationen
(10 × 1)
10 Multiplikationen
(10 × 1)
(1 × 10)
(1 × 1)
10 Multiplikationen
(10 × 1)
94 / 400
Beispiel: Multiplikation einer Matrizenfolge
Multiplikation von rechts
Insgesamt: 40 Multiplikationen
95 / 400
Beispiel: Multiplikation einer Matrizenfolge
Multiplikation in optimaler Reihenfolge
(10 × 1)
(1 × 10)
(10 × 1)
(1 × 10)
(10 × 1)
96 / 400
Beispiel: Multiplikation einer Matrizenfolge
(10 × 1)
(1 × 10)
(10 × 1)
(10 × 1)
(1 × 10)
(10 × 1)
(1 × 10)
10 Multiplikationen
(10 × 1)
(1 × 1)
97 / 400
Beispiel: Multiplikation einer Matrizenfolge
(10 × 1)
(1 × 10)
(10 × 1)
(10 × 1)
(1 × 10)
(10 × 1)
10 Multiplikationen
(10 × 1)
(1 × 10)
10 Multiplikationen
(1 × 1)
(10 × 1)
(1 × 1)
(1 × 1)
98 / 400
Beispiel: Multiplikation einer Matrizenfolge
(10 × 1)
(1 × 10)
(10 × 1)
(10 × 1)
(1 × 10)
(10 × 1)
10 Multiplikationen
(10 × 1)
(10 × 1)
(1 × 10)
10 Multiplikationen
(1 × 1)
(10 × 1)
(1 × 1)
(1 × 1)
1 Multiplikation
(1 × 1)
99 / 400
Beispiel: Multiplikation einer Matrizenfolge
(10 × 1)
(1 × 10)
(10 × 1)
(10 × 1)
(1 × 10)
(10 × 1)
10 Multiplikationen
(10 × 1)
(10 × 1)
(1 × 10)
10 Multiplikationen
(1 × 1)
(10 × 1)
(1 × 1)
(1 × 1)
1 Multiplikation
(1 × 1)
10 Multiplikationen
(10 × 1)
100 / 400
Beispiel: Multiplikation einer Matrizenfolge
Insgesamt: 31 Multiplikationen
101 / 400
Multiplikation einer Matrizenfolge
A(n,m) sei eine Matrix A mit n Zeilen und m Spalten.
Annahme: A(n,m) := B(n,q) · C(q,m) benötigt n · q · m skalare
Multiplikationen.
1
2
3
N
.
, M(n
, M(n
, . . . , M(n
Eingabe: Matrizenfolge M(n
2 ,n3 )
0 ,n1 )
1 ,n2 )
N−1 ,nN )
cost(M 1 , . . . , M N ) := minimale Zahl der skalaren Multiplikationen,
um das Produkt M[1, N] = M 1 · · · M N zu berechnen.
Dynamisches Programmierung liefert:
cost(M i , . . . , M j ) =
mink {cost(M i , . . . , M k ) + cost(M k+1 , . . . , M j ) + ni −1 · nk · nj }
102 / 400
Multiplikation einer Matrizenfolge
for i := 1 to N do
cost[i , i ] := 0;
for j := i + 1 to N do
cost[i , j] := ∞;
endfor
endfor
for d := 1 to N − 1 do
for i := 1 to N − d do
j := i + d;
for k := i to j − 1 do
t := cost[i , k] + cost[k + 1, j]
+ n[i − 1] · n[k] · n[j];
if t < cost[i , j] then
cost[i , j] := t;
best[i , j] := k;
endif
endfor
endfor
endfor
return cost[1, N]
103 / 400
Die Multiplikation nach der best-Tabelle
cost[i , j], best[i , j] sind berechnet.
Was nun?
Wir benötigen nur noch die best[i , j]-Werte.
i
Setze M[i , i ] = M(n
und berechne M[1, N] rekursiv:
i −1 ,ni )
M[i , j] = M[i , best[i , j]] · M[best[i , j] + 1, j].
104 / 400
CYK-Algorithmus
for i := 1 to N do
Var[i , i ] := {A ∈ V | (A → ai ) ∈ P};
for j := i + 1 to N do
Var[i , j] := ∅;
endfor
Eingabe: w = a1 · · · aN
endfor
Var[i , j] := {A ∈ V | A ⇒∗ ai · · · aj }
for d := 1 to N − 1 do
(1 ≤ i ≤ j ≤ N)
for i := 1 to N − d do
j := i + d;
for k := i to j − 1 do
Var[i , j] := Var[i , j] ∪
{A ∈ V | (A → BC ) ∈ P, B ∈ Var[i , k], C ∈ Var[k + 1, j]}
endfor
endfor
endfor
return Var[1, N]
105 / 400
4. Vorlesung, 31.10.2011
Thema: Optimale Suchbäume
106 / 400
Optimale Suchbäume
Erzeugung von optimalen Suchbäumen:
Die direkte Methode Θ(n3 ).
Der Algorithmus von Donald E. Knuth hat einen Aufwand von Θ(n2 ).
(Teleskop-Summe)
Interessant ist hier, wie man durch eine genaue Analyse des Problems
den kubischen Algorithmus in einen quadratischen verwandeln kann.
Der Algorithmus ist nicht komplizierter, sondern der Gewinn liegt im
Auslassen überflüssiger Schritte.
107 / 400
Optimale Suchbäume
Sei ein linear geordnetes Feld gegeben mit v1 < v2 < · · · < vn .
Zugriffshäufigkeit auf Knoten v sei durch γ(v ) gegeben.
Der Wert γ(v ) kann sowohl die relativen als auch die absoluten
Häufigkeiten bezeichnen.
108 / 400
Optimale Suchbäume
Binärer Suchbaum: knotenbeschrifteter Baum
Für jeden Knoten v mit linkem Unterbaum L und u ∈ L gilt: u < v
(bzw. w ∈ R rechts und dann v < w ).
Jeder Knoten v hat ein Level ℓ(v ):
ℓ(v ) := 1+ Abstand des Knotens v zur Wurzel.
Das Auffinden eines Knotens auf Level ℓ erfordert ℓ Vergleiche.
Ziel:
Finde einen binären Suchbaum, der die gewichtete innere Pfadlänge
X
P(B) :=
ℓ(v ) · γ(v )
v ∈V
minimiert.
109 / 400
Optimale Suchbäume
Optimaler Suchbaum für 1, 2, 22 , 23 , 24 , 25 mittels Greedy-Strategie.
25
24
23
22
2
1
110 / 400
Optimale Suchbäume
Optimaler Suchbaum für 1,2,3,4,5,6,7.
5
3
2
7
4
6
1
Gewichtete innere Pfadlänge: 1 · 5 + 2 · 10 + 3 · 12 + 4 · 1 = 65
111 / 400
Optimale Suchbäume
Suchbaum für 1, 2, 3, 4, 5, 6, 7 nach Greedy-Strategie:
7
6
5
4
3
2
1
Gew. innere Pfadlänge:
7 + 12 + 15 + 16 + 12 + 15 + 12 + 7 = 86
112 / 400
Optimale Suchbäume
Die innere Pfadlänge bestimmt die durchschnittlichen Kosten einer
Sequenz von Find-Operationen.
Dynamische Programmierung möglich,
da die Unterbäume eines optimalen Baums auch optimal sind.
113 / 400
Optimale Suchbäume
Bezeichnungen:
◮
Knotenmenge = {1, . . . , n},
d.h. die Zahl i entspricht dem Knoten vi . Weiterhin wird ℓi
(bzw. γi ) eine andere Schreibweise für ℓ(i ) (bzw. γ(i )) sein.
◮
Pi ,j : gewichtete innere Pfadlänge eines optimalen Suchbaumes
der Knoten {i , . . . , j}.
◮
◮
◮
Ri ,j : Wurzel eines optimalen Suchbaumes für {i , . . . , j}.
Später: ri ,j kleinstmögliche Wurzel, Ri ,j größtmögliche Wurzel.
P
Γi ,j := jk=i γ(k): Gewicht der Knotenmenge {i , . . . , j}.
114 / 400
Optimale Suchbäume
Im dynamischen Ansatz sind nun Werte ri ,j oder Ri ,j gesucht, die
einen optimalen Suchbaum B mit Kosten Pi ,j realisieren. Man geht
nun direkt vor und berechnet P(B) rekursiv (dabei bezeichnet BL
bzw. BR den linken, bzw. rechten Unterbaum der Wurzel von B):
P(B) := P(BL ) + P(BR ) + Γ(B)
Wir realisieren diesen Ansatz zunächst in einem kubischen
Algorithmus.
Hier nur die Idee:
Algorithmus: Berechnung eines optimalen Suchbaums
◮
◮
Pi ,j = Γi ,j + mink∈i ...j {Pi ,k−1 + Pk+1,j }
ri ,j = k, für den minimalen Index Pi ,k−1 + Pk+1,j das Minimum
annimmt.
115 / 400
Optimale Suchbäume
Das folgende Lemma ist trivial:
Lemma (Monotonie der Funktion P(B))
Sei B ′ ein optimaler Suchbaum für {1, . . . , n} und v ∈ {1, . . . , n}. Sei
B ein optimaler Suchbaum für {1, . . . , n} \ {v }. Dann gilt
P(B) ≤ P(B ′ ).
116 / 400
Optimale Suchbäume
Beweis: Sei L′ bzw. R ′ der linke bzw. rechte Unterbaum von v . Ist
R ′ = ∅, so betrachten wir nun den Baum B̂ := B ′ \{v } (d.h., L′
rutscht ein Level höher). B̂ ist wieder ein Suchbaum und es gilt
offensichtlich P(B̂) ≤ P(B).
Ist R ′ 6= ∅, so sei v ′ der kleinste Knoten in R ′ . Sei R ein optimaler
Baum der Knoten aus R ′ \{v ′ }. Wir definieren einen Baum B̂ durch
Ersetzen von v durch v ′ in B ′ und von R ′ durch R. Dabei ist B̂
wieder ein Suchbaum und es gilt:
P(B ′ ) − P(B̂) ≥ γ(v ) · l ′ (v ) − γ(v ′ ) · l ′ (v ) + γ(v ′ ) · l ′ (v ′ ),
wobei der letzte Summand eine untere Schranke ist für die Differenz
P(R ′ ) − P(R) (bezogen auf den gesamten Baum B ′ bzw. B̂). Daraus
ergibt sich P(B̂) ≤ P(B ′ ) und damit P(B) ≤ P(B ′ ) für einen
optimalen Baum, der die Knotenmenge ohne v realisiert.
117 / 400
Optimale Suchbäume
Die folgende Beobachtung (von Knuth) ist entscheidend.
Satz 1.11 (Monotonie der Wurzel)
Sei r [i , j] (bzw. R[i , j]) die kleinste (bzw. größte) Wurzel eines
optimalen Suchbaumes für die Knoten {i , . . . , j}. Dann gilt für n ≥ 2:
r [i , j − 1] ≤ r [i , j],
R[i , j − 1] ≤ R[i , j].
Die Anschauung ist klar.
Der Beweis ist sehr schwierig.
118 / 400
Optimale Suchbäume
Beweis: Es reicht für n > 2 zu zeigen:
r [1, n − 1] ≤ r [1, n],
R[1, n − 1] ≤ R[1, n].
Mit Induktion ist das Lemma für j − i ≤ n − 2 bereits bewiesen.
Lemma: Sei {1, . . . , n} die Knotenmenge.
Sei Bj ein optimaler Suchbaum mit minimalem Level j von Knoten n
(also n möglichst hoch). Sei j1 die Wurzel von Bj .
Sei Bi ein optimaler Suchbaum mit Wurzel i1 ≥ j1 .
Dann existiert ein optimaler Suchbaum B ′
mit Wurzel i1 und Knoten n auf Level j.
119 / 400
Optimale Suchbäume
Diese Verbindung der Eigenschaften minimales Level für n und große
Wurzel ist für den Beweis des Satzes von Nutzen.
Beweis des Lemmas: Wir betrachten die rechten Äste, wobei die
Knoten von Bi mit ik und die Knoten von Bj mit jk bezeichnet sind:
Beachte i1 ≥ j1 .
Bj : j1
Bi :
j2
1
i1
i2
..
2
..
.
n
.
ij
j
n
i
120 / 400
Optimale Suchbäume
Bei festem i1 maximieren wir i2 , dann maximieren wir i3 usw. Der
neue Baum wird weiterhin mit Bi bezeichnet. Angenommen, in Bi und
Bj liegt der Knoten n auf gleichem Level j. Dann sind wir fertig.
Andernfalls können wir (zur Vereinheitlichung der Bezeichnung)
annehmen, dass der Knoten n in Bi auf Level i liegt und i > j gilt,
weil j minimal gewählt wurde.
Sei k maximal mit ik > jk . Dann gilt 1 ≤ k < j. Setze m = k + 1. Es
gilt {ik + 1, . . . , n} ( {jk + 1, . . . , n}, und mit Induktion und
Links-Rechts-Symmetrie des Satzes gilt
jm ≤ R[jk + 1, n] ≤ R[ik + 1, n] = im . Also folgt jm = im , da k
maximal gewählt wurde.
121 / 400
Optimale Suchbäume
Sei nun Ri der rechte Unterbaum in Bi , der im als Wurzel hat, und Rj
sei der rechte Unterbaum in Bj , der jm als Wurzel hat. Wegen im = jm
haben Ri und Rj dieselbe Knotenmenge und sind optimale
Suchbäume. Wir bilden einen Baum B ′ durch Ersetzen von Ri in Bi
durch Rj . Da P(Ri ) = P(Rj ) gilt, ergibt sich auch
P(B ′ ) = P(Bi ) = P(Bj ), d. h. B ′ ist optimal für {1, . . . , n}, hat i1 als
Wurzel und den Knoten n auf Level j.
122 / 400
Optimale Suchbäume
Angenommen i2 = j2 , also m = 2. Wir erhalten das folgende Bild.
Beachte i1 ≥ j1 .
Bi : i1
1
B ′ : i1
i2 = j 2
j2
i2
..
..
.
ij
.
n
n
2
j
i
123 / 400
Optimale Suchbäume
Symmetrie ergibt:
Lemma: Sei Bi ein optimaler Suchbaum für {1, . . . , n} mit
maximalem Level i von Knoten n. Sei i1 die Wurzel von Bi . Sei Bj ein
optimaler Suchbaum für {1, . . . , n} mit Wurzel j1 ≤ i1 . Dann existiert
ein optimaler Suchbaum B ′ für {1, . . . , n} mit Wurzel j1 und Knoten
n auf Level i .
124 / 400
Optimale Suchbäume
Es sei α das Gewicht des größten Knotens n, d.h. α := γn . Der Wert
α variiert zwischen 0 und ∞.
Mit rα (bzw. Rα ) bezeichnen wir die kleinste (bzw. größte) Wurzel
eines optimalen Suchbaums für die Knoten {1, . . . , n}.
Sei zunächst α = 0 und seien B ′ bzw. B ′′ optimale Suchbäume für die
Knoten {1, . . . n − 1} mit Wurzel r [1, n − 1] bzw. R[1, n − 1]. Nimmt
man jeweils den Knoten n als rechtesten hinzu, so erhält man
Suchbäume für {1, . . . n}, ohne die gewichtete innere Pfadlänge zu
erhöhen. Nach der Monotonie von P(B) sind diese Suchbäume
optimal. Es folgt:
r [1, n − 1] = r0 [1, n]
und
R[1, n − 1] ≤ R0 [1, n].
Man beachte, dass nicht r [1, n − 1] > r0 [1, n] gelten kann, sonst
würde man einfach den Knoten n entfernen.
Daher reicht es zu zeigen, dass rα und Rα monoton mit α steigen.
125 / 400
Optimale Suchbäume
Ab jetzt betrachten wir nur noch die Knotenmenge i ∈ {1, . . . , n}.
Bi sei ein optimaler Suchbaum unter der Nebenbedingung, dass der
Knoten n auf dem Level i liegt.
Dann gilt für eine gewisse Konstante c(i ):
Pα (Bi ) = α · i + c(i ).
Der Graph Pα (Bi ) ist eine Gerade mit Steigung i .
Aufgrund der Linearität erhalten wir das folgende (vertikal gestauchte)
Bild, bei der jede Steigung i = 1, . . . , n höchstens einmal vorkommt.
126 / 400
Optimale Suchbäume
Pα
✻
Pα (Bn )
Pα (Bℓ )
Pα (Bi )
Pα (Bk )
Pα (Bj )
✲
α0
α
127 / 400
Optimale Suchbäume
Wenn wir α vergrößern, sind wir am Punkt α0 gezwungen, das Level
von n zu verkleinern.
Bei einem Knick, besteht die Chance Rα0 > Rα0 −ε .
Wähle bei α0 einen optimalen Suchbaum Bi mit Rα0 als Wurzel und
wähle einen bei α0 optimalen Suchbaum Bj mit minimalem Level j.
Erinnerung:Die Verbindung der Eigenschaften minimales Level für n
und große Wurzel ist von Nutzen.
Wegen des ersten Lemmas gibt es einen optimalen Suchbaum bei α0
mit Rα0 als Wurzel und Knoten n auf Level j. Dieser Suchbaum ist
optimal bis einschließlich zum nächsten Knick“. Erst dort könnte Rα
”
echt größer werden. Folglich steigt Rα monoton.
128 / 400
Optimale Suchbäume
Wir fahren jetzt von rechts nach links und starten bei α = ∞.
Die Monotonie heißt jetzt, rα muss von rechts nach links fallen
Wenn wir α verkleinern, sind wir am Punkt α0 gezwungen, das Level
des Knotens n auf einen größeren Wert zu ändern.
Wähle bei α0 einen optimalen Suchbaum Bj mit rα0 als Wurzel und
einen ebenfalls bei α0 optimalen Suchbaum Bi mit maximalem Level
i . Wegen des zweiten Lemmas gibt es einen optimalen Suchbaum bei
α0 mit rα0 als Wurzel und Knoten n auf Level i . Dieser Suchbaum ist
optimal bis einschließlich zum nächsten Knick“ nach links . Erst dort
”
könnte rα echt kleiner werden.
Folglich steigt rα von links nach rechts.
Damit ist das Monotonie Lemma gezeigt!
129 / 400
Optimale Suchbäume
cost[n, n + 1] := 0;
for i := 1 to n do
cost[i , i − 1] := 0;
cost[i , i ] := γ(i );
Γ[i , i ] := γ(i );
r[i , i ] := i ;
endfor
for d := 1 to n − 1 do
for i := 1 to n − d do
j := i + d;
left := r [i , j − 1]; right := r [i + 1, j];
root := left;
t := cost[i , left − 1] + cost[left + 1, j];
for k := left + 1 to right do
if cost[i , k − 1] + cost[k + 1, j] < t then
t := cost[i , k − 1] + cost[k + 1, j];
root := k;
endif
endfor
Γ[i , j] := Γ[i , j − 1] + γ(j);
cost[i , j] := t + Γ[i , j];
r [i , j] := root;
endfor
endfor
130 / 400
Optimale Suchbäume
Laufzeit:
n−1 n−d
X
X
d=1 i =1
n−1
X
d=1
(1 + r [i + 1, i + d] − r [i , i + d − 1]) =
(n − d + r [n − d + 1, n] − r [1, d]) ≤
3 2
n ∈ Θ(n2 ).
2
Bemerkung 1.12
◮
Ein linear geordnetes Feld v1 , . . . , vn kann aus einem
ungeordneten Feld in O(n log n) Schritten erzeugt werden.
Dies fällt aber bei Θ(n2 ) nicht ins Gewicht.
Damit gilt: aus einem beliebigen Feld mit n Elementen kann
ein optimaler Suchbaum in Θ(n2 ) Schritten erzeugt werden.
◮
Der Algorithmus von Knuth wird in vielen Lehrbüchern nur
ohne Beweis erwähnt.
131 / 400
5. Vorlesung, 07.11.2011
Thema: Mittlere Höhe in zufälligen binären Suchbäumen
132 / 400
Mittlere Höhe binärer Suchbäume
Mittlere Höhe binärer Suchbäume:
Zunächst eine Wiederholung im Schnelldurchgang
133 / 400
Bezeichnungen
◮
◮
◮
π: Permutation von {1, . . . , n}
Schreibe π als Folge (π(1), π(2), π(3), . . . , π(n))
BI (π), I ⊆ {1, . . . , n}: Suchbaum mit den Elementen i ∈ I ,
eingefügt in der Reihenfolge (links nach rechts) von π.
(i ∈ I wird vor j ∈ I eingefügt, wenn π −1 (i ) < π −1 (j) gilt.)
◮
Zufallsvariablen:
Wurzel von B(π) ist i .“ ∈ {0.1}
”
XI (π) = Höhe von BI (π).“
”
YI (π) = 2XI (π) (Dies ist der Trick!)
Ri (π) =
134 / 400
Erwartungswerte
XI (π) =
Höhe von BI (π).“
”X (π)
YI (π) = 2 I
Ziel: E [Xn ] ∈ O(log n).
(unter der Annahme einer Gleichverteilung der Permutationen.)
Wir zeigen zunächst (denn dies ist einfacher!):
E [Yn ] ∈ O(n3 ).
135 / 400
Berechnung von E [Yn ]
n = 1: E [Y1 ] = 1.
n ≥ 2:
E [Yn ] = 2
n
X
i =1
E [Ri · max{Y{1,...,i −1} , Y{i +1,...,n} }]
P
Denn Yn (π) = 2 ni=1 (Ri (π) · max{Y{1,...,i −1} (π), Y{i +1,...,n} (π)}).
Man beachte, dass Ri (π) = 1, falls π(1) = i ist, und Ri (π) = 0
andernfalls.
136 / 400
Berechnung von E [Yn ]
Für i 6∈ I sind die Zufallsvariablen YI und Ri unabhängig. Also gilt:
E [Yn ] = 2
n
X
i =1
Es gilt E [Ri ] =
1
n
E [Ri ] · E [max{Y{1,...,i −1} , Y{i +1,...,n} }]
und max{YI , YJ }(π) ≤ YI (π) + YJ (π). Also:
n
2X
(E [Y{1,...,i −1} ] + E [Y{i +1,...,n} ]).
E [Yn ] ≤
n
i =1
Aufgrund der Linearität der Erwartungswerte und der Eigenschaft
E [YI ] = E [Y{1,...,|I |} ] = E [Y|I | ] gilt:
n
E [Yn ] ≤
2X
(E [Yi −1 ] + E [Yn−i ])
n
i =1
137 / 400
Berechnung von E [Yn ]
Aus
n
E [Yn ] ≤
erhalten wir
E [Yn ] ≤
2X
(E [Yi −1 ] + E [Yn−i ])
n
i =1
n
n−1
n−1
i =1
i =0
i =1
4X
4X
4X
E [Yi −1 ] =
E [Yi ] =
E [Yi ].
n
n
n
Da jeder Term in der Summe oben rechts zweimal gezählt wird
und ein leerer Baum nicht betrachtet werden muss.
138 / 400
Berechnung von E [Yn ]
Wir werden jetzt zeigen, dass E [Yn ] ≤
n = 1: E [Y1 ] = 1 =
1
4
n ≥ 2:
n−1
E [Yn ] ≤
=
4
3 .
4X
E [Yi ]
n
Induktion
i =1
1
·
n
n+3
4
1
4
≤
=
n−1
4X1
n
4
i =1
1
·
4
n+3
3
n+3
3
gilt.
i +3
3
n−1 1X
<
n
i =0
i +3
3
.
139 / 400
Berechnung von E [Xn ]
Ableitung von E [Xn ] aus E [Yn ]:
Sei X : Ω → R eine diskrete Zufallsvariable. Dann berechnet sich der
Erwartungswert durch
X
X
E [X ] =
Prob(i ) · X (i ) =
λi xi mit xi = X (i ), λi = Prob(i ).
i
i
Für eine konvexe Funktion f folgt dann aus der Jensenschen
Ungleichung:
X
f (E [X ]) ≤
λi f (xi ) = E [f (X )].
i
140 / 400
Berechnung von E [Xn ]
Jensensche Ungleichung:
f (E [X ]) ≤ E [f (X )].
In unserem Fall ist die konvexe Funktion f (x) = 2x :
24n3
(n + 3)(n + 2)(n + 1)
1 n+3
≤
.
=
2E [Xn ] ≤ E [2Xn ] = E [Yn ] ≤
4
24
24
3
Also E [Xn ] ≤ 3 log2 n ∈ O(log n).
141 / 400
Jensensche Ungleichung
Sei
Pk f : R → R eine konvexe Funktion und λi ≥ 0 für 1 ≤ i ≤ k mit
i =1 λi = 1. Dann gilt
f
k
X
i =1
λi x i
!
≤
k
X
λi f (xi ).
i =1
Beweis: Durch Induktion nach k.
k = 1: Dann ist λ1 = 1 und die Aussage trivial.
142 / 400
Jensensche Ungleichung
Schaubild der konvexen Funktion f :
λf (x) + (1 − λ)f (y )
x
λx + (1 − λ)y
y
Es gilt also für alle x < y und alle 0 ≤ λ ≤ 1:
f (λx + (1 − λ)y ) ≤ λf (x) + (1 − λ)f (y ).
143 / 400
Backtracking
Sucht man in einem Baum, so ist zum Beispiel Tiefensuche ein
effizientes Verfahren, wenn sich frühzeitig entscheidet, ob in einem
Teilbaum eine Lösung zu finden ist oder nicht.
Backtracking (dt. Zurückgehen“) beschreibt ein solches Verfahren,
”
bei dem man im Berechnungsgraphen eines Problems solange
vorwärts geht, bis ein Knoten erreicht wird, der eine Lösung darstellt
oder bei dem sicher davon ausgegangen werden kann, dass von diesem
Knoten aus keine Lösung mehr zu finden ist. Ist dies der Fall, dann
geht man den Pfad einen Schritt zurück und entscheidet sich für einen
anderen Folgepfad u.s.w..
Backtracking findet oft Anwendung bei Problemen, für die nur
exponentielle Algorithmen bekannt sind. Wir betrachten hier das
sogenannte Mautstraßenproblem (engl. Turnpike-Problem).
144 / 400
Mautproblem
Beispiel 1.13
Fährt man in Frankreich über die Autobahn nach Paris, so muss
Maut gezahlt werden. Man bekommt hierzu eine Karte, die eine
Tabelle mit den Entfernungen und Gebühren zwischen den Einund Ausfahrten enthält.
Die Frage ist nun: lässt sich die Lage der Ausfahrten aus den
Entfernungen rekonstruieren, wenn nur eine geordnete Liste der
Entfernungen mit ihren Vielfachheiten vorliegt?
Wir suchen also eine Lösung für folgendes Problem:
Es seien 0 = x0 < x1 < · · · < xn positive Zahlenwerte und A eine
n × n-Matrix mit Ai ,j = |xi − xj |. Weiterhin sei D ein Feld, das die
Werte Ai ,j , 1 ≤ i < j ≤ n, in sortierter Reihenfolge enthält. Gesucht
sind die Werte xk für 1 ≤ k ≤ n, wenn D gegeben ist.
145 / 400
Mautproblem
Bemerkung 1.14
1. Sind die Werte xk bekannt, dann kann A (bzw. D) in O(n2 )
(bzw. in O(n2 log n)) Schritten berechnet werden.
2. Sei D bekannt. Ist die Folge x0 , . . . , xn eindeutig bestimmt,
falls sie existiert? Wie komplex ist dann die Rekonstruktion?
Die erste Frage kann mit Nein beantwortet werden. Selbst dann
existiert keine eindeutige Lösung, wenn in D alle Einträge verschieden
sind (bis jetzt wurde aber kein Beispiel für n > 6 gefunden).
146 / 400
Mautproblem (Prozedur maut)
procedure maut(var X : XFeld; var D: DFeld; n: integer; var found: boolean);
begin
found := false;
X0 := 0;
(* Initialisierung linker und rechter Rand *)
Xn := max(D);
D := D − max(D);
(* D aktualisieren *)
X1 := Xn − max(D);
(* X1 kann wegen Symm. so gewählt werden *)
if (X1 − X0 ) ∈ D then
D := D − {|X1 − X0 |, |Xn − X1 |};
place(X , D, n, 1, n, found)
endif
endprocedure
147 / 400
Mautproblem (Prozedur place)
if D = ∅ then
found := true
else
d := max(D);
(* probiere Xl +1 := Xn − d *)
D ′ := {|Xj − (Xn − d)| | j ∈ {0, . . . , l} ∪ {r , . . . , n}};
if D ′ ⊆ D then
Xl +1 := Xn − d;
D := D − D ′ ;
(* D aktualisieren *)
place(X , D, n, l + 1, r , found );
(* rekursiv Lösung suchen *)
if found = false then
(* falls Misserfolg: Backtracking *)
D := D + D ′
(* D wiederherstellen *)
endif
endif
(* probiere Xr−1 := d *)
D ′ := {|Xj − d| | j ∈ {0, . . . , l} ∪ {r , . . . , n}};
if (found = false) and D ′ ⊆ D then
Xr−1 := d;
D := D − D ′ ;
(* D aktualisieren *)
place(X , D, n, l, r − 1, found );
(* rekursiv Lösung suchen *)
if found = false then
(* falls Misserfolg: Backtracking *)
D := D + D ′
(* D wiederherstellen *)
endif
endif
endif
148 / 400
Mautproblem: Komplexität
Zeitanalyse zum maut-Algorithmus: Die Datenstruktur für D sollte die
Operationen max(D), member, delete, insert effizient erlauben d.h. in
O(log(n)) (= O(log(n2 ))) Schritten. Dies wird etwa durch eine
geordnete Liste ermöglicht, bei der die delete-Funktion nur die Plätze
markiert, die insert-Funktion (wir fügen nur Werte ein, die wir vorher
gelöscht haben) hebt die Markierung von delete wieder auf. Member
ist einfach eine binäre Suche, und max(D) ist der Wert des Feldes mit
dem höchsten nicht-markierten Index. Alternativ ginge z.B. auch ein
AVL-Baum.
Der worst-case ist exponentiell und tritt dann ein, wenn sehr viel
zurückgesetzt werden muss. Beispiele hierfür sind bekannt, es zeigt
sich aber (empirisch), dass dieser Fall selten auftritt.
149 / 400
6. Vorlesung 08.11.11
Themen:
Quicksort, Heapsort vs. Bottom-up Heapsort vs. Ultimatives Heapsort
Theoie vs. Praxis
150 / 400
Quicksort
Quicksort-Algorithmus 1962 von Hoare:
Wähle Pivotelement.
Zerlege das Feld in zwei Teilfelder mit Pivot als Trennung.
Nach linear vielen Vertauschungen sind die Elemente im linken Teilfeld
kleiner oder gleich Pivot, im rechten größer oder gleich Pivot.
Wende auf beide Teilfelder rekursiv den gleichen Algorithmus an.
Wichtig: die Wahl des Pivots.
Laufzeit ist optimal, falls das Pivotelement gleich dem mittleren
Element des Feldes (Median) ist.
In der Praxis bewährt:
die Median-aus-Drei-Methode.
151 / 400
Partitionieren
Zunächst geben wir die Prozedur zum Partitionieren eines Feldes A
(im Bereich ℓ bis r ) bzgl. eines Pivot-Elements P = A[p] an, wobei
ℓ < r und p ∈ {ℓ, . . . , r } gelte. Ergebnis dieser Prozedur ist ein Index
m ∈ {ℓ, . . . , r − 1}, der folgende Eigenschaften erfüllt:
A[i ] ≤ P für alle ℓ ≤ i ≤ m
und
A[i ] ≥ P für alle m + 1 ≤ i ≤ r .
Das rechte Teilfeld enthält (irgendwo) das Pivotelement.
152 / 400
Partitionieren
function partitioniere(A[ℓ . . . r ] : array of integer, p : integer) : integer
(* Partitioniere A[ℓ . . . r ] bzgl. A[p]; Rückgabewert = Index m *)
begin
P := A[p];
(* Pivot-Element merken *)
swap(A[ℓ], A[p]);
(* Pivot an erste Pos. stellen *)
x := ℓ − 1;
y := r + 1;
while x < y do
repeat x := x + 1 until A[x] ≥ P;
repeat y := y − 1 until A[y ] ≤ P;
if x < y then
swap(A[x], A[y ]);
endif
endwhile
return y
endfunction
153 / 400
Partitionieren
Man vergewissere sich, dass die geforderten Eigenschaften von dieser
Implementierung wirklich erfüllt werden, und dass es außerdem nicht
vorkommen kann, dass der linke bzw. rechte Zeiger“ (x bzw. y ) über
”
die Feldgrenze ℓ bzw. r hinauswandert.
Die vorgestellte Implementierung führt bis zu n + 2
Schlüsselvergleichen auf einem Feld mit n Elementen durch.
Jedes Element außer A[p] muss jedoch nur einmal mit P verglichen
werden.
Dies lässt sich durch eine andere Implementierung erreichen. (Übung)
Bei der Durchschnittsanalyse von Quicksort werden wir daher von
n − 1 Schlüsselvergleichen ausgehen. Außerdem gehen wir davon aus,
dass das Pivotelement die Felder am Ende trennt.
154 / 400
Quicksort
procedure quicksort(A[ℓ . . . r ] : array of integer)
begin
if ℓ < r then
p := Index des Median von A[ℓ], A[(ℓ + r ) div 2], A[r ];
m := partitioniere(A[ℓ . . . r ], p);
(* Feld bzgl. A[p] partitionieren *)
quicksort(A[ℓ . . . m]);
(* linkes Teilfeld sortieren *)
quicksort(A[m + 1 . . . r ]);
(* rechtes Teilfeld sortieren *)
endif
endprocedure
155 / 400
Quicksort, kleineres Teilfeld zuerst
procedure quicksort(A[ℓ . . . r ] : array of integer)
begin
if ℓ < r then
p := Index des Median von A[ℓ], A[(ℓ + r ) div 2], A[r ];
m := partitioniere(A[ℓ . . . r ], p);
(* Feld bzgl. A[p] partitionieren *)
if m − ℓ < r − m then
(* kleineres Teilfeld zuerst *)
quicksort(A[ℓ . . . m]);
(* linkes Teilfeld sortieren *)
quicksort(A[m + 1 . . . r ]);
(* rechtes Teilfeld sortieren *)
else
quicksort(A[m + 1 . . . r ]);
(* rechtes Teilfeld sortieren *)
quicksort(A[ℓ . . . m]);
(* linkes Teilfeld sortieren *)
endif
endif
endprocedure
156 / 400
Quicksort: Komplexität
Worst-case Der Quicksort-Algorithmus ist quadratisch:
In jedem Schritt enthält eines der beiden Teilfelder genau ein Element.
Mögliches Szenario: Liste bereits (fast) sortiert, Pivot ist stets das
erste oder letzte Element.
Hier: Durchschnittsanalyse unter der Annahme einer zufälliger
Auswahl des Pivotelements. (Alternativ: Feld ist zufällig angeordnet)
Satz 1.15
Q(n) := P
die mittlere Anzahl der Schlüsselvergleiche.
H(n) := nk=1 k1 = n−te harmonische Zahl.
Dann gilt für Quicksort:
Q(n) = 2(n + 1)H(n) − 4n.
157 / 400
Quicksort: Durschnittsanalyse
Beweis: Für n = 1 gilt offensichtlich Q(1) = 0 = 2 · 2 · 1 − 4 · 1. Für
n ≥ 2 gilt:
n
Q(n) = (n − 1) +
= (n − 1) +
1X
[Q(i − 1) + Q(n − i )]
n
i =1
n
2X
n
i =1
Q(i − 1)
Dabei ist (n − 1) die Zahl der Vergleiche beim Pivotieren und
[Q(i − 1) + Q(n − i )] die mittlere Zahl der Vergleiche für das
rekursive Sortieren der beiden Teilhälften; dabei sind alle Positionen
für das Pivotelement gleich wahrscheinlich (deswegen der Faktor 1/n).
158 / 400
Quicksort: Durschnittsanalyse
Damit gilt:
nQ(n) = n(n − 1) + 2
n
X
i =1
Q(i − 1)
159 / 400
Quicksort: Durschnittsanalyse
Also:
nQ(n) − (n − 1)Q(n − 1) = n(n − 1) + 2
n
X
i =1
Q(i − 1)
−(n − 1)(n − 2) − 2
n−1
X
i =1
Q(i − 1)
= n(n − 1) − (n − 2)(n − 1) + 2Q(n − 1)
= 2(n − 1) + 2Q(n − 1)
Wir erhalten:
nQ(n) = 2(n − 1) + 2Q(n − 1) + (n − 1)Q(n − 1)
= 2(n − 1) + (n + 1)Q(n − 1)
160 / 400
Quicksort: Durschnittsanalyse
Q(n)
n+1
=
2(n − 1) Q(n − 1)
2(n − 1) 2(n − 2) Q(n − 2)
+
=
+
+
n(n + 1)
n
n(n + 1) (n − 1)n
n−1
=
n
X
2(k − 1)
k(k + 1)
k=1
= 2
n
X
(k − 1)
k(k + 1)
k=1
n
X
= 2
k=1
n
X
1
k
−
k(k + 1)
k(k + 1)
k=1
161 / 400
Quicksort: Durschnittsanalyse
Q(n)
n+1
= 2
"
= 2
= 2 2
n
X
k=1
"
n
X
1
1
−
k +1
k(k + 1)
n
X
k=1
k=1
n
X1
2
−
k +1
k
k=1
#
#
1
+ H(n) − 1 − H(n)
n+1
= 2H(n) +
4
− 4.
n+1
162 / 400
Quicksort: Durschnittsanalyse
Schließlich erhält man für Q(n):
Q(n) = 2(n + 1)H(n) + 4 − 4(n + 1)
= 2(n + 1)H(n) − 4n.
Es ist H(n) − ln n ≈ 0, 57 . . . = Eulersche Konstante. Also:
Q(n) ≈ 2(n + 1)(0, 57 + ln n) − 4n
≈ 2n ln n − 2, 8n ≈ 1, 38n log n − 2.8n.
Theoretische Grenze: log(n!) = n log n − 1, 44n;
Quicksort ist im Mittel um 38% schlechter. Die Durchschnittsanalyse
der Median-aus-Drei Methode liefert 1, 18n log n − 2, 2n.
Dies ist im Mittel nur noch um 18% schlechter.
163 / 400
Heapsort
Definition 2.1
Ein (Min-)Heap ist ein Feld a[1 . . . n] mit der Eigenschaft
a[i ] ≤ min{a[2i ], a[2i + 1]}.
Heapsort aus zwei Teilen:
Heapaufbau:
Der Heap wird dabei von rechts nach links aufgebaut, wodurch viele
kleine und nur wenige große Heaps betrachtet werden.
Vertausche nun a[1] und a[n]. Die Heapbedingung in a[1 . . . n − 1] ist
jetzt in a[1] eventuell verletzt und a[1] muss einsinken.
164 / 400
Heapsort
x
y
2 Vergleiche, um min{x, y , z}
zu bestimmen
z
Ist y das Minimum, dann vertausche x und y .
Analog für z.
Ist x das Minimum, dann stoppt der Einsinkprozess.
165 / 400
Reheap
Algorithmus 2.1 Reheap
procedure reheap(i , n: integer)
var m: integer;
begin
if i ≤ n/2 then
m := min{a[i ], a[2i ], a[2i + 1]};
if (m 6= a[i ]) ∧ (m = a[2i ]) then
swap(i , 2i );
reheap(2i , n)
elsif (m 6= a[i ]) ∧ (m = a[2i + 1]) then
swap(i , 2i + 1);
reheap(2i + 1, n)
endif
endif
endprocedure
(∗ i ist die Wurzel ∗)
(∗ 2 Vergleiche! ∗)
(∗ vertausche x, y ∗)
(∗ vertausche x, z ∗)
166 / 400
Heap-Aufbau
for i :=
n
2
downto 1 do reheap(i , n) endfor
Die Invariante hierfür ist: a[i . . . n] erfüllt die Heap-Bedingung bereits
vor dem Aufruf
reheap(i − 1, n).
Für i = n2 + 1 ist dies richtig.
Setze i um 1 herab, dann ist die Heapbedingung in a[i ] eventuell
verletzt. Nach dem Einsinken ist die Invariante erneut erfüllt.
167 / 400
Zeitanalyse
Das Einsinken kostet
2 · (Höhe des Teilbaums unter a[i ]) Vergleiche.
Wir führen die Analyse für n = 2k − 1 durch, d.h. die maximale Höhe
des Heaps ist k − 1. Allgemein gibt es
◮
◮
◮
◮
20 Bäume der Höhe k − 1,
21 Bäume der Höhe k − 2,
2i Bäume der Höhe k − 1 − i ,
2k−1 Bäume der Höhe 0.
168 / 400
Zeitanalyse
Daher sind zum Heapaufbau maximal
2·
k−1
X
i =0
2i (k − 1 − i )
Vergleiche nötig. Nach einer Indexvertauschung ergibt sich:
k
2 ·
k−1
X
j=0
j · 2−j ≤ 2k ·
Wir zeigen auf der nächsten Folie
Satz 2.2
P
j≥0 j
X
j≥0
j · 2−j
· 2−j = 2 und erhalten damit:
Heapaufbau ist in linearer Zeit möglich.
169 / 400
−j
=
j≥0 2
P
P
j≥0 j
· 2−j = 2
Wir setzen für |z| < 1:
f (z) =
X
j≥0
zj =
1
1−z
und leiten ab:
f ′ (z) =
X
j≥0
Also:
z · f ′ (z) =
Also:
X
j≥0
j · (1/2)j =
j · z j−1 =
X
j≥0
j · zj =
1
(1 − z)2
z
(1 − z)2
f ′ (1/2)
1
=
=2
2
2 · (1 − (1/2))2
170 / 400
Standard Heapsort
Algorithmus 2.2 Heapsort
procedure heapsort(n: integer)
begin for i := n2 downto 1
do reheap(i , n) endfor
for i := n downto 2 do
swap(1, i ); reheap(1, i − 1)
endfor
endprocedure
Satz 2.3
Standard Heapsort erfordert 2n log n + O(n) Vergleiche.
Beweis: Der Aufbau erfordert O(n) und der Abbau durch Einsinken
2n log n Vergleiche.
171 / 400
Bottom-Up-Heapsort
Bemerkung 2.4
Eine Analyse der Durchschnittskomplexität von Heapsort ergibt
einen mittlerer Aufwand von 2n log n Vergleichen. Damit ist
Standard-Heapsort zu Quicksort nicht konkurrenzfähig.
Bottom-Up-Heapsort benötigt wesentlich weniger Vergleiche.
Nach dem Entfernen der Wurzel wird zuerst der potentielle
Einsinkpfad des Elementes bestimmt.
Es wird der Weg verfolgt, der immer zum kleineren der beiden
Nachfolger führt (Kosten: nur log n statt 2 log n Vergleiche).
Da erwartungsgemäß dieses Element tief einsinken wird, bietet sich
anschließend eine bottom-up Bestimmung der tatsächlichen Position
auf dem Einsinkpfad an.
Hoffnung: Die bottom-up Bestimmung erfordert insgesamt nur O(n)
Vergleiche.
172 / 400
Grundidee
x0
Es gilt xi ≤ yi für 1 ≤ i ≤ Höhe.
✛
✲ y1
x1 Vergleich
Das Einsinken geschieht längs dieses Pfades,
der mit log n Vergleichen bestimmt werden kann.
y2 ✛
✲ x
2
y3 ✛
✲ x3
x4 ✛
✲ y4
173 / 400
Die mittlere Höhe eines Knotens
Die erwartete Höhe eines Knotens in einem Binärbaum mit zufälliger
Knotenverteilung ist:
0·
∞
1
1
1X n
1
+ 1 · + 2 · + ... =
=1
2
4
8
2
2n
n=0
Wir bestimmen jetzt vom Blatt aus (also bottom-up) die tatsächliche
Position auf dem Einsinkpfad. Ist die Position i gefunden, so werden
die Elemente x0 , . . . , xi zyklisch vertauscht (x0 geht an die Stelle von
xi , und x1 , . . . , xi rutschen hoch).
174 / 400
Varianten
Es kann gezeigt werden, dass im schlechtesten Fall höchstens
1.5n log n + o(n log n) Vergleiche benötigt werden. Wenn man nach
Carlsson auf dem Pfad eine binäre Suche anwendet, so kommt man
auf einen Aufwand von höchstens n log n + O(n log log n). Eine binäre
Suche ist aber in der Praxis zu aufwendig und außerdem steigt man in
den Pfad i.a. zu hoch ein. Durch eine leichte Abwandlung der reinen
bottom-up Positionsbestimmung können wir die Zahl der wesentlichen
Vergleiche auf n log n + o(n log n) bringen.
175 / 400
Ultimatives Heapsort
Eine Grundidee des ultimativen Heapsorts ist es, ein (potentiell
schweres) Element von der Wurzel bis zu einem Blatt absinken zu
lassen, ohne es danach durch einen Aufstieg an die korrekte Position
zu bringen.
Wir nehmen also in Kauf, die Heap-Bedingung an einem Blatt zu
verletzen.
Das Absinken lässt sich am einfachsten durch die folgende rekursive
Prozedur beschreiben, die ein Element von der Position i bis maximal
zur Position j absinken lässt:
176 / 400
Grundidee
Algorithmus 2.3 Absinken eines Elements
procedure down(i , j):
if 2i > j then
skip
elsif 2i = j then
swap(i , j)
elsif a[2i ] < a[2i + 1] then
swap(i , 2i );
down(2i , j)
else
swap(i , 2i + 1);
down(2i + 1, j)
endif
endprocedure
177 / 400
Zweischichtenheap
Ein Feld a[1 . . . n] erfüllt die Zweischichten-Heap-Bedingung bzgl.
(L, S), falls
◮
∀i ∈ L ∀j ∈ S : a[i ] ≤ a[j]
◮
∀i ∈ L : a[i ] ≤ min{a[2i ], a[2i + 1]}
(d.h., (a[i ] ≤ a[2i ] oder 2i > n) und (a[i ] ≤ a[2i + 1] oder
2i + 1 > n))
◮
∀j ∈ S : 2j und 2j + 1 sind schwarz,
(d.h., (2j ∈ S oder 2j > n) und (2j + 1 ∈ S oder 2j + 1 > n))
178 / 400
Lemma:
Sei a[1 . . . n] ein Zweischichten-Heap bzgl. (L, S) mit v |L| ≥ c und
{n − c + 1, . . . , n} ⊆ S, 1 ≤ c ≤ n2 . Dann kann der Heap durch reines
Absinken um c Elemente abgebaut werden, ohne die
Zweischichten-Heap-Bedingung zu verletzen. Dabei werden nur weiße
Elemente in sortierter Reihenfolge entnommen.
179 / 400
Ultimatives Heapsort
Algorithmus 2.4
procedure ult-heap(a[1 . . . n])
begin
if n klein“ then sortiere direkt
”
else finde den Median von a[1 . . . n];
benutze Median
als
Pivot-Element, danach:
• L = 1, . . . , n2 ,
• max a[1], . . . , a[ n2 ] ≤ Median,
• S = n2 + 1, . . . , n ,
• min a[ n2 + 1], . . . , a[n] ≥ Median;
(∗ Θ(n) Vergleiche ∗)
(∗ Θ(n) Vergleiche
n
Stelle Heap-Eigenschaft für a[1 . . . 2 ] her (Θ(n) Vergleiche) ∗)
(∗
1 do
for i = ⌈ n4 ⌉ downto
n
reheap(i , 2 ])
endfor
∗)
180 / 400
Ultimatives Heapsort, 2. Teil
Algorithmus 2.5
Baue den Heap um n2 Elemente ab:
for j = n downto ⌊ n2 ⌋ + 1 do
swap(1, j);
down(1, j − 1)
endfor
(∗ Das Restfeld a[1 . . . ⌊ n2 ⌋] ist unsortiert ∗)
(∗ Rekursion ∗)
ult-heap(a[1 . . . ⌊ n2 ⌋])
endif
(∗ Das Feld a[1 . . . n] ist in umgekehrter Reihenfolge sortiert ∗)
endprocedure
181 / 400
Anzahl der Vergleiche
n · log2 n + Θ(n)
Ultimatives Heapsort ist ein internes Sortierverfahren, das nur auf
Schlüsselvergleichen beruht und bis auf evtl. Verbesserungen im
linearen Term optimal ist. Asymptotisch sind keine weiteren
Verbesserungen möglich.
Aufgrund der hohen Konstanten, die sich in dem linearen Term
verstecken, ist dieses Verfahren nur theoretischen Interesse.
Soll Heapsort in der Praxis verwendet werden, so ist reines
Bottom-Up-Heapsort die geeignete Variante.
182 / 400
−j
=
j≥0 2
P
P
j≥0 j
· 2−j = 2
Wir setzen für |z| < 1:
f (z) =
X
j≥0
zj =
1
1−z
und leiten ab:
f ′ (z) =
X
j≥0
Also:
z · f ′ (z) =
Also:
X
j≥0
j · (1/2)j =
j · z j−1 =
X
j≥0
j · zj =
1
(1 − z)2
z
(1 − z)2
f ′ (1/2)
1
=
=2
2
2 · (1 − (1/2))2
183 / 400
Medianberechnung in linearer Zeit
Gegeben sei ein Feld a[1 . . . n] von Zahlen. Gesucht ist für ein
1 ≤ k ≤ n das k-kleinste Element m, d.h. die Zahl
m ∈ {a[i ] | 1 ≤ i ≤ n} so, dass
|{i | a[i ] < m}| < k
und
|{i | a[i ] > m}| ≤ n − k
Wir berechnen also ein k-tes Element, also ein SELECT.
184 / 400
Median der Mediane
Bestimme ein Pivotelement als Median der Mediane aus 5: Wir teilen
das Feld in Fünferblöcken auf. In jedem Block wird der Median
bestimmt (mit 6 Vergleichen möglich). Wir bestimmen rekursiv den
Median p dieses Feldes (mit dem gesamten Algorithmus). Der Wert p
wird als Pivotelement im folgenden verwendet. Kosten: T ( n5 ).
185 / 400
Quicksortschritt
Mit dem Pivot p zerlege so, dass für gewisse m1 < m2 gilt:
a[i ] < p
a[i ] = p
a[i ] > p
für 1 ≤ i ≤ m1
für m1 < i ≤ m2
für m2 < i ≤ n
Kosten: maximal n Schritte. Fallunterscheidung:
1. k ≤ m1 : Suche das k-te Element rekursiv in a[1], . . . , a[m1 ].
2. m1 < k ≤ m2 : Das Ergebnis ist p.
3. k > m2 : Suche das (k − m2 )-te Element in a[m2 + 1], . . . , a[n].
186 / 400
30 – 70 Aufteilung
Die Wahl des Pivots als Median-aus-Fünf ergibt die folgende
Ungleichungen für m1 , m2 :
3
n ≤ m2
10
und
m1 ≤
7
n
10
Damit ergeben sich die Kosten für den Rekursionsschritt als T ( 7n
10 ).
187 / 400
Zeitanalyse
Sei T (n) die Gesamtzahl der Vergleiche. Wir erhalten folgende
Rekursionsgleichung für T (n):
n
7n
+T
T (n) ≤ T
+ O(n)
5
10
Aus dem Mastertheorem II folgt damit T (n) ∈ O(n).
188 / 400
Zahl der notwendigen Schlüsselvergleiche
T (n) ≤ T
n
5
+T
7n
10
+
6n 2n
+
5
5
Hierbei ist 6n
5 der Aufwand für die Bestimmung der Blockmediane und
2n
5 der Aufwand für den Zerlegungsschritt ist.
Denn wir können bei der richtigen Positionierung des Medians der
Mediane jeweils in Fünferblöcken operieren und müssen daher aus
einem Fünferblock jeweils nur noch zwei Elemente mit dem
Pivotelement vergleichen.
7
9
= 10
erhalten
Es ergibt sich, dass damit T (n) ≤ 16n gilt: mit 15 + 10
8n
8n
9n
wir T (n) ≤ T ( 10) + 5 und damit T (n) ≤ 10 · 5 = 16n.
n
Der Term T 7n
10 wird im Mittel näher bei T 2
189 / 400
7. Vorlesung am 14.11.11
Quickselect
190 / 400
Quickselect
Algorithmus 2.6
function quickselect(A[ℓ . . . r ] : array of integer, k : integer) : integer
begin
if ℓ = r then
return A[ℓ]
else
p := random(ℓ, r );
m := partitioniere(A[ℓ . . . r ], p);
k ′ := (m − ℓ + 1);
if k ≤ k ′ then
return quickselect(A[ℓ . . . m], k)
else
return quickselect(A[m + 1 . . . r ], k − k ′ ) endif endif
endfunction
191 / 400
Analyse von Quickselect
n
Q(n) ≤ (n − 1) +
1X
max{Q(i − 1), Q(n − i )}
n
i =1
Hierbei ist (n − 1) wiederum die Anzahl der Vergleiche für das
Pivotieren und max{Q(i − 1), Q(n − i )} mindestens so großwie die
mittlere Anzahl der Vergleiche für den rekursiven Aufruf auf einem der
beiden Teilfelder.
Wir machen die Annahme, dass wir stets im größeren Teilfeld suchen.
Daher ≤.
192 / 400
AQS
Es gilt:
n
Q(n) ≤ (n − 1) +
1X
max{Q(i − 1), Q(n − i )}
n
i =1
n
X
≤ (n − 1) +
1
n
≤ (n − 1) +
n−1
2 X
Q(i )
n
n
i =⌊ 2 ⌋
Fallunterscheidung:
i =1
Q(max{i − 1, n − i })
Behauptung: Q(n) ≤ 4 · n:
Beweis : OK für n = 0, 1.
Mit Induktion sei Q(i ) ≤ 4 · i für alle i < n.
193 / 400
AQS
Q(n) ≤ (n − 1) +
≤ (n − 1) +
n−1
2 X
Q(i )
n
n
i =⌊ 2 ⌋
n−1
8 X
i
n
n
i =⌊ 2 ⌋
!
(n − 1)n ( n2 − 1) n2
−
2
2
n−1
4 n−3
≤ (n − 1) + 4(n − 1) −
n
2
2
< 4n − 1 < 4 · n.
8
= (n − 1) +
n
194 / 400
Durchschnittsanalyse von Quickselect
◮
Q(n) durchschnittliche Anzahl an Vergleichen von quickselect,
um das kte Element in einem Feld der Größe n zu finden.
Es bezeichne π eine Permutation von n-Elementen und es sei
1 ≤ k ≤ n fest. Im Folgenden bezeichnen i , j Werte mit
1 ≤ i < j ≤ n.
Im Laufe von Quickselect (oder auch Quicksort) werden i und j
maximal einmal verglichen. Falls sie verglichen werden ist dann i
oder j aktuelles Pivotelement.
◮
Xij (π) := “i wird mit j verglichen′′ ∈ {0, 1}.
195 / 400
Durchschnittsanalyse von Quickselect
Unterscheide 3 Fälle:
1. Fall: i < j ≤ k. Xij (π) = “i oder j wird als erstes
im Interval [i , . . . , k] gezogen′′ = k−i2+1
X
X
1≤i <k i <j≤k
k −i
k −i +1
1≤i <k
X
1
=2
1−
k −i +1
Xij (π) = 2
X
1≤i <k
= 2(k − 1 − H(k))
wobei H(k) ∈ Θ(ln k) die harmonische Funktion ist.
2. Fall: k ≤ i < j, analog folgt Xij (π) =
X
X
k<j≤n k≤i <j
2
j−k+1
und
Xij (π) = 2(n − k − H(n − k + 1))
196 / 400
Durchschnittsanalyse von Quickselect
3. Fall: i < k < j Xij (π) =
X
X
2
j−i +1
Xij (π) = 2
1≤i <k k<j≤n
X
X
1≤i <k k<j≤n
=2
X 1≤i <k
<2
X
1≤i <k
1
j −i +1
1
1
+ ··· +
k −i +2
n−i +1
(ln(n − i + 1) − ln(k − i ))
n
= 2 ln
k −1
197 / 400
Durchschnittsanalyse von Quickselect
und
X
X
Xij (π) = 2
1≤i <k k<j≤n
X 1≤i <k
>2
X
1≤i <k
1
1
+ ··· +
k −i +2
n−i +1
(ln(n − i + 2) − ln(k − i + 2))
X
(ln(n − i + 1) − ln(k − i ))
≥2
1≤i <k
− ln(k − 2 + 2) − ln(k − 1 + 2)
n
≥ 2 ln
− 2 ln(k + 1)
k −1
198 / 400
Durchschnittsanalyse von Quickselect
◮
◮
◮
◮
1. Fall + 2. Fall zusammen ergeben < 2n
Mit kn ≤2n ergibt sich:
n ≤ 2(1 + ln(2))n
Q(n) < 2 1 + ln k−1
n :
Im Fall k = ⌈n/2⌉ folgt mit n1 2n ≤ ⌈n/2⌉
Q(n) > 2 (1 + ln(2)) n − c log(n) für ein geeignetes c
Das heißt, Q(n) = 2(1 + ln(2))n − log -Terme füer k = ⌈n/2⌉
199 / 400
Verwaltung von Mengen
Union-Find–Algorithmen
Definition: Eine Partition P einer Menge M ist eine Zerlegung von M
in disjunkte, nicht-leere Teilmengen:
m
M =
[
˙
Pi
i =1
P = {Pi | Pi ∩ Pj = ∅, Pi 6= ∅, 1 ≤ i < j ≤ m}
Beispiel: Sei M = {1, 2, . . . , 12}. Dann ist P = {Q, R, S} mit
Q = {i ∈ M | i ungerade}, R = {i ∈ M | i ≡ 0 mod 4} und
S = {i ∈ M | i ≡ 2 mod 4} eine Partition von M.
200 / 400
Union-Find
◮
◮
Find ist eine Funktion von M nach P, die jedem i ∈ M die
Menge Pj zuordnet, für die i ∈ Pj gilt.
Union verändert die Partition wie folgt: Union(A, B, C ) bedeutet,
dass die Partitionselemente A, B ∈ P zu einer neuen Menge mit
dem Namen C vereinigt werden. Nach Union(A, B, C ) ist die
neue Partition (P \ {A, B}) ∪ {A ∪ B}, wobei das Element A ∪ B
nun C heißt.
201 / 400
Feld von Paaren
1
Q
2
S
3
Q
4
R
5
Q
6
S
7
Q
8
R
9
Q
10
S
11
Q
12
R
Hier gilt z.B. Find(5) = Q und Union(Q, R, T ) ergibt:
1
T
2
S
3
T
4
T
5
T
6
S
7
T
8
T
9
T
10
S
11
T
12
T
202 / 400
Feld: Quadratische Kosten
◮
◮
◮
◮
Kosten: Find–Operation O(1).
Kosten: Union–Operation O(n).
In den Anwendungen werden häufig m Union-Find–Operationen
hintereinander durchgeführt, mit m ∈ Θ(n).
Damit insgesamt: O(n2 ).
203 / 400
Knotenbeschriftete Bäume
Klasse = knotenbeschrifteter Baum, Wurzel = Name der Klasse.
Elemente der Grundmenge: 1, . . . , n. Knoten = {v1 , . . . , vn }
Zugriffsmöglichkeiten:
element: {1, . . . , n} → Knoten
pred: Knoten → Knoten
count: Wurzelknoten → Zahl
name: Wurzelknoten → Klasse
root: Menge → Wurzelknoten
zugehöriger Knoten im Baum
Vorgänger im Baum
Anzahl der Elemente
Name der Klasse
Wurzel des Baumes
204 / 400
Union
Die Initialisierung ist dann:
name(vi ) := i
root(i ) := vi
pred(vi ) := nil
count(vi ) := 1
Die Union-Operation Union(i , j) wird dann so realisiert, dass der
kleinere der beiden Bäume i , j an die Wurzel des größeren angehängt
wird:
205 / 400
Union
Wir werden sehen, dass bei einer n-elementigen Grundmenge die Höhe
eines jeden Baums durch log(n) beschränkt ist. Somit kosten in dieser
Realisierung Find-Operationen O(log(n)) Zeit, während Union in
konstanter Zeit durchgeführt werden kann.
Eine weitere Idee zur Verkürzung der Suchpfade besteht darin, bei
einem Aufruf von Find alle Knoten auf dem Pfad, den man zur Wurzel
hin durchläuft, direkt unter die Wurzel anzuhängen.
206 / 400
Find–Implementierung mit Pfadverkürzung
Algorithmus 2.7
function Find(i : Element): Element;
var u, v , root: Knoten;
begin
u := element[i ];
while pred(u) 6= nil do u := pred(u);
endwhile
root := u; u := element[i ];
while pred(u) 6= nil do v := pred(u); pred(u) := root; u := v
endwhile
return name(root);
endfunction
207 / 400
Find mit Pfadverkürzung
p
p
s
q
q
r
r
s
Baum nach Find(s)
Baum vor Find(s)
208 / 400
Minimale Aufspannende Bäume
Definition 2.5
Ein Graph G = (V , E ) heißt zusammenhängend, wenn je zwei Knoten
durch einen Pfad verbunden sind.
Definition 2.6
Ein Baum ist ein zusammenhängender, kreisfreier, ungerichteter
Graph.
Bemerkung 2.7
Jeder Baum mit n Knoten besitzt genau n − 1 Kanten.
Definition 2.8
Ein minimaler aufspannender Baum (minimal spanning tree, MSB) zu
einem gewichteten Graphen G = (V , E , γ) ist ein Baum
B = (V , P
F , γ|F ) mit F ⊆ E mit minimalem Gewicht
γ(B) := e∈F γ(e).
209 / 400
Algorithmus 2.8 Kruskal–Algorithmus zur Bestimmung des MSB
function kruskal(G = (V , E , γ): kantengew. Graph): Kantenmenge;
(∗ G sei zusammenhängend ∗)
(∗ E = {e1 , e2 , . . . , em } sei nach Gewichten aufsteigend sortiert. ∗)
var m, i : integer;
x, y : Knoten;
T : Kantenmenge;
(∗ des MSB ∗)
begin
m := |E |; T := ∅;
forall v ∈ V do [v ] := {v } endfor;
for i := 1 to m do
xy := ei ;
if Find(x) 6= Find(y ) then
Union([x] , [y ]); T := T ∪ {xy }
endif
endfor return T endfunction
210 / 400
8. Vorlesung 15.11.2011
Thema:
Zeitanalyse für Union-Find
211 / 400
Der Rang eines Knotens
Definition 2.9
Der Rang eines Knotens v , Rang(v ), ist die Höhe des Knotens in dem
Wald, der ohne Pfadverkürzung erzeugt wurde.
Lemma 2.10
Sei v ein Knoten mit Rang(v ) = r . Dann hat der Teilbaum mit der
Wurzel v mindestens 2r Elemente.
Beweis:
Für r = 0 ist die Behauptung klar.
Betrachte den Zeitpunkt, in dem v den Rang r (v ) = r erhält. Dann
hat v ein Kind v ′ und im Teilbaum von v ′ sind 2r −1 Elemente, vorher
waren im Teilbaum von v bereits 2r −1 Elemente, also hat der
Teilbaum von v jetzt 2 · 2r −1 = 2r Elemente.
Zwei Knoten mit gleichem Rang sind nicht Vorgänger voneinander.
Damit folgt:
Korollar 2.11
Es gibt höchstens n/2r Knoten mit Rang r .
212 / 400
Zeitanalyse für Union-Find
Dieser Abschnitt beschreibt die Analyse nach Robert E. Tarjan (1983)
für m Union-Find-Operationen.
Wir verwenden die folgende Ackermannfunktion:
A0 (r ) = r + 1,
Ak+1 (r ) = Ark (r ).
(Hierbei bezeichnet Ark (r ) die r -fache Anwendung von Ak auf r )
Es ist Ak (1) = 2; daher ist sinnvoll:
α(n) = min{k | Ak (2) > n}.
A1 (x) = 2x, A2 (x) = x2x , A3 (2) = 2048, also
A4 (2) = A23 (2) = A3 (2048) = A2048
(2048): ein Turm von mehr als
2
2000 Potenzen der 2. Daher ist α(n) ≤ 4 = α(2048) für alle
denkbaren Werte.
213 / 400
Analyse von m Union-Find-Operationen
Wir betrachten n Knoten und die folgende zeitabhängige Funktion
part (x):
Definition 2.12
Sei x ein Knoten mit Rang r . Dann bezeichnet part (x) seinen
Vorgänger (englisch: parent) zum Zeitpunkt t.
Definition 2.13
Ein Knoten x mit Rang r ≥ 2 gehört im Zeitabschnitt t zur Klasse k,
falls k maximal ist mit
r (part (x)) ≥ Ak (r (x)).
214 / 400
Analyse von m Union-Find-Operationen
◮
Wurzeln oder Knoten vom Rang 0 oder 1 gehören in keine Klasse.
Ein innerer Knoten mit Rang 2 gehört mindestens in die Klasse 0.
◮
Es gibt höchstens α(n) verschiedene Klassen.
◮
Realistisch: nur die Klassen 0, 1, 2 und 3.
◮
Eine Klasse kann als Maß für den Abstand eines Knotens zu
seinem Vaterknoten angesehen werden. Damit ist auch klar: Die
Klassennummern, die man auf dem Pfad zur Wurzel antrifft,
müssen nicht wie die Ränge monoton steigen.
215 / 400
Die Lebensgeschichte eines Knotens
◮
Ein Knoten x beginnt als Wurzel.
◮
Wenn er den Wurzelstatus verliert, erhält er seinen Rang r (x).
◮
Bei Rang 0 oder 1, bleibt x für immer klassenlos.
◮
Bei Rang 2 oder mehr betritt x eine Klasse k ≥ 0.
◮
◮
Ab jetzt geht es in der Klassengesellschaft nur aufwärts.
Realistischer Weise sollte man nur auf Klasse 3 hoffen.
216 / 400
Analyse von m Union-Find-Operationen
Erinnerung: Teuer sind nur die Find-Operationen.
Bei einem Find(i ) verteilen wir auf dem Pfad Gold- und Eisenstücke
auf die Knoten. Wir unterscheiden α(n) Klassen von Eisenstücken, so
dass es insgesamt 1 + α(n) verschiedene Währungen gibt.
Goldverteilung: Wenn x auf dem Pfad zur Wurzel besucht wird, gibt
es ein Goldstück, falls
x keiner Klasse angehört (d.h. x ist Wurzel oder vom Rang 0 oder 1)
oder x letzter seiner Klasse ist.
Korollar 2.14
Insgesamt werden nicht mehr als (3 + α(n))m Goldstücke verteilt.
217 / 400
Analyse von m Union-Find-Operationen
Eisenverteilung: Wenn x zur Zeit t auf dem Pfad zur Wurzel besucht
wird, erhält x ein Eisenstück der Klasse k, falls x zu dieser Zeit zur
Klasse k gehört und nicht letzter seiner Klasse ist.
Beobachtung: Erhält x zur Zeit t ein Eisenstück der Klasse k, so
gibt es auf dem Pfad zur Wurzel Knoten x, part (x), y , part (y ) mit:
Ak (r (x)) ≤ r (part (x)) ≤ r (y ),
Ak (r ((y )) ≤ r (part (y )).
Satz 2.15
Sei i ≥ 1 mit r (part (x)) ≥ Aik (r (x)). Dann gilt zur Zeit t + 1 die
Beziehung:
r (part+1 (x)) ≥ Aik+1 (r (x)).
218 / 400
Beweis
Wähle auf dem Pfad x, part (x), y , part (y ) mit:
Aik (r (x)) ≤ r (part (x)) ≤ r (y ), Ak (r (y )) ≤ r (part (y )).
Dann folgt:
r (part+1 (x)) ≥
≥
≥
≥
=
r (part (y ))
Ak (r (y ))
Ak (r (part (x)))
Ak (Aik (r (x)))
Aik+1 (r (x)).
219 / 400
Korollare
Ein Knoten vom Rang r erhält maximal r Eisenstücke der Klasse k.
Beweis: Nach r Find-Operationen in der Klasse k gilt:
r (pars (x)) ≥ Ark (r ) = Ak+1 (r )
und der Knoten steigt in die Klasse k + 1 auf.
Korollar 2.16
Es werden insgesamt maximal 3n
2 ≤ 2n Eisenstücke der Klasse k
verteilt.
Beweis: Es gibt höchstens n/2r Knoten mit Rang r und jeder erhält
höchstens r Eisenstücke der Klasse k. Damit:
X rn
3n
=
r
2
2
r ≥2
Korollar 2.17
Es werden insgesamt maximal 2α(n)n Eisenstücke verteilt.
Beweis: α(n) Klassen und pro Klasse 2n Eisenstücke.
220 / 400
Abschätzung für die Gesamtkosten
Alle Finds zusammen kosten O 3m + α(n)(2n + m) Zeit.
Beweis:
(3 + α(n))m Gold + 2α(n)n Eisen.
Übung: Für α(n) < 4 dominiert die Konstante 6m im O-Term, da
m > n. Wie erreicht man 5m?
221 / 400
Gemessene Laufzeitkosten
Die folgenden Diagramme zeigen experimentell gemessene Werte für
die Laufzeitkosten, die die Union-Find-Implementierung mit
Pfadverkürzung beim Kruskal-Algorithmus verursacht. Gemessen
wurde jeweils, wie viele Knoten alle Find-Operationen zusammen
besuchen mussten. Der zugrunde gelegte Graph wurde stets zufällig
erstellt und hatte n Knoten sowie m Kanten, wie es in den
Diagrammen jeweils zu sehen ist.
Die Diagramme zeigen die Ergebnisse jeweils für drei verschiedene
Strategien der Implementierung:
Normal Die normale Strategie, bei einem Union stets den
kleineren Baum unter den größeren zu hängen
Invers Die umgekehrte Strategie, bei einem Union stets den
größeren Baum unter den kleineren zu hängen
Zufall Bei jedem Union wird gewürfelt, ob nun der kleinere
Baum unter den größeren gehängt werden soll oder
umgekehrt
222 / 400
Experimente
223 / 400
Experimente
224 / 400
9. Vorlesung 21.11.2011
Themen:
1.) Äquivalenztest deterministischer endlicher Automaten nach
Hopcroft und Karp 1971: A linear algorithm for testing
equivalence of finite automata.
◮
Tatsächlich ist der Algorithmus nur linear in der Zahl der
Union-Find-Operationen.
225 / 400
Äquivalenz endlicher Automaten nach Hopcroft und Karp
1971
Definition 2.18
Ein endlicher, deterministischer Automat über dem Alphabet Σ ist ein
5-Tupel A = (Q, Σ, δ, q0 , F ) mit:
◮
Q = Zustände,
◮
q0 ∈ Q = ist Startzustand,
◮
◮
δ : Q × Σ −→ Q = Übergangsfunktion,
F ⊆ Q = Endzustände.
226 / 400
Äquivalenz endlicher Automaten
Schreibe statt δ(q, w ) für q ∈ Q und w ∈ Σ∗ , vereinfacht qw .
L(A) := {w ∈ Σ∗ | q0 w ∈ F }
L(q) := {w ∈ Σ∗ | qw ∈ F }.
Gegeben: A = (Q, Σ, δ, q0 , F ) und A′ = (Q ′ , Σ, δ′ , q0′ , F ′ ).
Frage: L(A) = L(A′ )?
227 / 400
Äquivalenztest DEA
Q̃ := disjunkte Vereinigung von Q und Q ′
R ⊆ Q̃ × Q̃ die kleinste Äquivalenzrelation mit den Eigenschaften:
1. (q0 , q0′ ) ∈ R,
2. (q, q ′ ) ∈ R, a ∈ Σ, q ∈ Q, q ′ ∈ Q ′ =⇒ (qa, q ′ a) ∈ R
Lemma: Es gilt: L(A) = L(A′ ) gdw.
R ∩ (F × (Q ′ \ F ′ )) ∪ ((Q \ F ) × F ′ ) = ∅.
228 / 400
Äquivalenztest DEA
Algorithmus 2.9
function Äquivalenztest-DFA(A, A′ : DFA) : boolean
begin
L := {(q0 , q0′ )};
while L 6= ∅ do
wähle ein (q, q ′ ) ∈ L;
L := L \ {(q, q ′ )}
if Find(q) 6= Find(q ′ ) then
if (q, q ′ ) ∈ [(F × (Q ′ \ F ′ )) ∪ ((Q \ F ) × F ′ )] then
return false
(∗ L(q0 ) 6= L(q0′ ) ∗)
else
Union(q, q ′ );
forall a ∈ Σ do
L := L ∪ {(qa, q ′ a)} endfor endif endif endwhile
return true endfunction
229 / 400
Zeitanalyse
Terminierung
Es sind maximal |Q| + |Q ′ | =: n = |Q̃| Union-Operationen möglich.
Damit werden maximal |Σ| · n Elemente zu L hinzugefügt. In jedem
Schleifendurchlauf wird ein Element aus L entfernt. Daher ergibt sich
die Termination nach maximal |Σ| · n Schleifendurchläufen.
|Q| + |Q ′ | =: n = |Q̃| Union–Operationen
|Σ| · n Find–Operationen
230 / 400
10. Vorlesung 22.11.2011
Themen:
1.) Fibonacci-Heaps
2.) Amortisierte Zeitanalyse
231 / 400
Fibonacci Heaps
Fibonacci-Heap H = Liste von Bäumen (also ein Wald):
Die Bäume sind knotenbeschriftet.
Alle Knoten, bis auf die Wurzel, können zusätzlich eine Marke tragen.
V = Knotenmenge.
key : V → N Beschriftung.
root bezeichnet stets eine der Wurzeln.
Heap-Bedingung:
∀x ∈ V : ist y ein Kind von x, so gilt: key (x) ≤ key (y )
232 / 400
Fibonacci-Heaps
◮
Die Eltern-Kind-Beziehung wird durch Zeiger realisiert, da die
Bäume unbalanciert sein werden.
◮
Im Gegensatz zu einem Standard-Heap müssen Indexrechnungen
also durch aufwendige Zeigeroperationen ersetzt werden.
◮
Operationen:
◮
1. merge,
◮
2. insert,
◮
3. delete min,
◮
4. decrease key.
233 / 400
Fibonacci-Heaps
◮
merge: Konkatenation zweier Listen — konstante Zeit.
◮
insert: Spezialfall von merge — konstante Zeit.
◮
merge und insert können (eventuell sehr lange) Listen
einelementiger Bäume entstehen.
◮
Jede solche Liste ist ein Fibonacci-Heap.
234 / 400
delete min
◮
Sei H ein Fibonacci-Heap aus T Bäumen und n Elementen.
◮
Für einen Knoten x sei rank (x) = die Anzahl der Kinder von x.
◮
Für einen Baum B sei rank (B) = Rang der Wurzel von B.
◮
Sei rmax (n) der maximale Rang, der in einem Fibonacci-Heap mit
n Elementen auftreten kann.
◮
rmax (n) ≤ n.
◮
Später zeigen wir rmax (n) ∈ O(log n).
235 / 400
procedure delete min
1. Suche minimalen Schlüssel als Wurzel in einem Baum. Sei r der
Rang dieses Baumes. Zeit: O(T ). Alternativ gibt es einen Zeiger
auf das kleinste Element.
2. Trenne die Wurzel ab und ersetze den Baum durch die r
Teilbäume. Evtl. Marken entfernen. Zeit: O(T + r ).
3. Definiere Feld L[0, . . . , rmax (n)] mit L[i ] = die Liste der Bäume
von Rang i . Zeit: O(T + rmax (n)).
4. for i := 0 to rmax (n) − 1 do
while |L[i ]| ≥ 2 do
Entnehme zwei Bäume aus L[i ].
Hänge den Baum mit dem größeren Schlüsselwert an der
Wurzel direkt unter die Wurzel des anderen Baumes und
füge diesen neuen Baum in L[i + 1] an.
endwhile
endfor
delete min erfordert also Zeit O(T + rmax (n)).
236 / 400
Bemerkung
Am Ende der Prozedur delete min gibt es für jeden möglichen Rang
höchstens einen Baum.
Insbesondere ist die Zahl der Bäume durch rmax (n) beschränkt.
237 / 400
decrease key
Kaskadenartige Schnitte: Sei x der Knoten, dessen Schlüssel
verkleinert werden soll.
1. Ist x die Wurzel, so kann der Schlüsselwert verkleinert werden.
Sei also x keine Wurzel und x = y0 , y1 , . . . , yk , . . . , root der Pfad
von x zur Wurzel. Für ein k ≥ 1 sei yk der erste (von x
verschiedene) Knoten, der keine Marke trägt.
2. Für 0 ≤ i < k trenne jetzt yi vom Elternknoten yi +1 ab und
entferne dabei die Marke von yi . (Für y0 = x ist evtl. eine Marke
vorhanden gewesen.) Damit wird yi für 0 ≤ i < k zu einer
unmarkierten Wurzel eines eigenen Baumes.
3. Falls yk keine Wurzel ist, markiere yk . (Kind verloren!)
238 / 400
decrease key
Es wird O(k + 1) Zeit benötigt. Beachte: Die Zahl der Marken hat
sich mindestens um k − 2 verringert, k ≥ 1. Die Anzahl der Bäume ist
nach der Prozedur decrease key durch T + k begrenzt.
239 / 400
decrease key
Definition 2.19
Ein Fibonacci-Heap ist eine Liste von Bäumen wie eingangs
beschrieben, die aus der leeren Liste unter Anwendung der
Operationen merge, insert, delete min und decrease key entstanden
ist.
Fibonacci-Heaps-Lemma
1. Sei x ein Knoten in einem Fibonacci-Heap, und ci das i -t älteste
Kind von x (d.h., c1 ist der erste Knoten, der Kind von x
geworden ist). Dann hat ci mindestens den Rang i − 2.
2. Hat x mindestens den Rang k, k ≥ 0, so enthält die Teilmenge
der Wurzel x zusammen mit den Teilbäumen von k Kindern
mindestens Fk+2 Knoten. Hierbei ist Fk+2 die (k + 2)-te
Fibonacci-Zahl (F0 = 0, F1 = 1, Fk+2 = Fk + Fk+1 für k ≥ 0).
240 / 400
Beweis Lemma
1. Sei c1 , c2 , . . . , ck die dem Alter nach absteigend geordnete Folge
der Kinder von x, und 1 ≤ i ≤ k. Dann ist c1 älter als c2 , c2 älter als
c3 , u.s.w. Zu dem Zeitpunkt, als ci unter den Knoten x (der zu diesem
Zeitpunkt eine Wurzel war) gehängt wurde, existierten also bereits die
Kinder c1 , . . . , ci −1 . Der Rang von x war also mindestens i − 1. Da
nur Bäume vom gleichen Rang zu einem Baum vereinigt werden,
hatte ci zu diesem Zeitpunkt mindestens den Rang i − 1. Der Knoten
ci kann inzwischen maximal ein Kind verloren haben. (In diesem Fall
wäre ci markiert.) Der Verlust eines weiteren Kindes hätte die
Abtrennung ci von x zur Folge gehabt. Es folgt die erste Behauptung
rank (ci ) ≥ i − 2 .
Die zweite Behauptung folgt dann mit Induktion und einem Bild.
241 / 400
Fibonacci-Zahlen
x k+1 = x k + x k−1 ⇐⇒ x k−1 (x 2 − x − 1) = 0
√
√
Die Zahlen 1+2 5 , 1−2 5 sind die beiden Lösungen der quadratischen
Gleichung x 2 − x − 1 = 0. Dies führt zu dem Ansatz:
√ !k
1+ 5
+b
2
Fk = a
√ !k
1− 5
2
Wegen F0 = 0 und F1 = 1 muss für a, b gelten:
a
Es folgt a =
√1 ,
5
b=
√
1+ 5
2
−1
√
.
5
a + b = 0,
√ + b 1−2 5
= 1.
242 / 400
Fibonacci-Zahlen
√ !k
1  1+ 5
Fk = √
−
2
5

Die Fibonacci-Zahlen wachsen exponentiell.
Also rmax (n) ∈ O(log n).

√ !k
1− 5 
2
243 / 400
Zusammenfassung der Zeitabschätzungen
1,2. merge, insert: in konstanter Zeit
3. delete min: O(T + log n), wobei T die Zahl der Bäume ist. Der
Summand log n ergibt sich aus rmax (n) ∈ O(log n).
4. decrease key: O(1) + O(k), wobei k ≥ 0 die Zahl der Marken sei,
um die sich der Fibonacci-Heap verringert hat.
244 / 400
Amortisierte Zeiten
Für die weitere Analyse einer Sequenz von Operationen betrachten wir
die gegen eine Potentialfunktion amortisierte Zeit.
Definition 2.20
Für einen Fibonacci-Heap H sei das Potential pot (H) gegeben durch
pot (H) := T + 2M ,
wobei T die Zahl der Bäume und M die Zahl der Marken seien.
Für eine Operation op sei ∆pot (op ) die Differenz des Potentials vor
und nach der Ausführung:
∆pot (op ) = pot (Heap nach op ) − pot (Heap vor op ) .
Die amortisierte Zeit einer Operation op sei
tamort (op ) = t(op ) + ∆pot (op ) .
245 / 400
Potentialfunktion
Die Potentialfunktion erfüllt:
◮
pot (H) ≥ 0,
pot (∅) = 0.
Sei jetzt op 1 , op 2 , op 3 , . . . , op m eine Sequenz von m Operationen auf
einem zunächst leeren Fibonacci-Heap.
Dann gilt:
m
m
X
X
tamort (op i ) .
t(op i ) ≤
◮
i =1
i =1
Bemerkung: Die Differenz ist gerade das Potential des erzeugten
Heaps.
Wegen pot (H) ∈ O(|H|) gilt
m
X
i =1
tamort (op i ) ∈
m
X
t(op i ) + θ(|H|).
i =1
Es genügt also eine Schranke für tamort (op ) zu bestimmen.
246 / 400
Bequemlichkeit
Für die weitere Rechnung ist es bequemer, sich bei der tatsächlich
verbrauchten Zeit zunächst von der O-Notation zu befreien. Durch die
Multiplikation mit einer geeigneten Konstanten können wir annehmen,
dass sich merge und insert in einem Schritt realisieren lassen. Die
Operation delete min benötigt höchstens T + log n und die Operation
decrease key höchstens k + 1 Zeitschritte. Die Wahl der Basis für den
Logarithmus ist in der Zeitanalyse unerheblich.
247 / 400
delete min
◮
tamort (merge) = t(merge) = 1. Denn das Potential der
konkatenierten Liste ist die Summe der Potentiale der
Einzellisten.
◮
tamort (insert) = t(insert)+∆pot (op ) = 1 + 1 = 2.
◮
Für delete min gilt t(delete min) ≤ T + log n, wobei T die Zahl
der Bäume zuvor und rmax (n) bis auf einen konstanten Faktor die
maximale Zahl der Bäume danach ist.
Die Zahl der Marken kann nur kleiner werden. Aus
∆pot (op ) ≤ −T + rmax (n) folgt daher
tamort (delete min) ≤ T + log n − T + rmax (n) ∈ O(log n) .
248 / 400
delete min
◮
Für decrease key gilt nach der obigen Bemerkung
t(decrease key) ≤ k + 1. Dabei verliert der Heap mindestens
k − 2 Marken, k ≥ 1, und erhält höchstens k neue Bäume.
∆pot (op ) = ∆(T ) + 2∆(M)
≤ k + 2 · (2 − k)
= 4−k
Also gilt tamort (decrease key) ≤ k + 1 + 4 − k = 5 ∈ O(1).
249 / 400
delete min
Satz 2.21
Für einen Fibonacci-Heap gelten die folgenden amortisierten Zeiten:
tamort (merge) ∈ O(1)
tamort (insert) ∈ O(1)
tamort (delete min) ∈ O(log n)
tamort (decrease key) ∈ O(1)
250 / 400
Anwendungen
Anwendung auf den Dijkstra- oder Prim-Algorithmus:
Für den Dijkstra-Algorithmus sei V die Randmenge und key die
ermittelte Distanz der Randknoten zum Quellknoten u (entsprechend
für Prim).
Sei n die Zahl der Knoten und e die Zahl der Kanten. Maximal
werden n insert–, e decrease key– und n delete min–Operationen
durchgeführt.
251 / 400
Anwendungen
tDijkstra ≤
n · tamort (insert)
+ e · tamort (decrease key)
+ n · tamort (delete min)
∈ O(n + e + n log n)
= O(e + n log n)
Man beachte, dass sich für die Analyse des schlechtesten Falles keine
Verbesserung ergibt. Asymptotisch ist O(e + n log n) jedoch
mindestens genauso gut wie min(e · log n, n2 ). In vielen Fällen
n2
(n log n ≤ e ≤ log
n ) ist O(e + n log n) um den Faktor log n besser.
252 / 400
11. Vorlesung, 29.11.11
Thema: Quick-Heapsort oder Queapsort
253 / 400
Ultimatives Heapsort
Eine Grundidee des ultimativen Heapsorts ist es, ein (potentiell
schweres) Element von der Wurzel bis zu einem Blatt absinken zu
lassen, ohne es danach durch einen Aufstieg an die korrekte Position
zu bringen.
Wir nehmen also in Kauf, die Heap-Bedingung an einem Blatt zu
verletzen.
254 / 400
Zweischichtenheap
Ein Feld a[1 . . . n] erfüllt die Zweischichten-Heap-Bedingung bzgl.
(L, S), falls
◮
∀i ∈ L ∀j ∈ S : a[i ] ≤ a[j]
◮
∀i ∈ L : a[i ] ≤ min{a[2i ], a[2i + 1]}
(d.h., (a[i ] ≤ a[2i ] oder 2i > n) und (a[i ] ≤ a[2i + 1] oder
2i + 1 > n))
◮
∀j ∈ S : 2j und 2j + 1 sind schwarz,
(d.h., (2j ∈ S oder 2j > n) und (2j + 1 ∈ S oder 2j + 1 > n))
255 / 400
Lemma:
Sei a[1 . . . n] ein Zweischichten-Heap bzgl. (L, S) mit v |L| ≥ c und
{n − c + 1, . . . , n} ⊆ S, 1 ≤ c ≤ n2 . Dann kann der Heap durch reines
Absinken um c Elemente abgebaut werden, ohne die
Zweischichten-Heap-Bedingung zu verletzen. Dabei werden nur weiße
Elemente in sortierter Reihenfolge entnommen.
256 / 400
Ultimatives Heapsort
Algorithmus 2.10
procedure ult-heap(a[1 . . . n])
begin
if n klein“ then sortiere direkt
”
else finde den Median von a[1 . . . n];
benutze Median
als
Pivot-Element, danach:
• L = 1, . . . , n2 ,
• max a[1], . . . , a[ n2 ] ≤ Median,
• S = n2 + 1, . . . , n ,
• min a[ n2 + 1], . . . , a[n] ≥ Median;
(∗ Θ(n) Vergleiche ∗)
(∗ Θ(n) Vergleiche
n
Stelle Heap-Eigenschaft für a[1 . . . 2 ] her (Θ(n) Vergleiche) ∗)
(∗
1 do
for i = ⌈ n4 ⌉ downto
n
reheap(i , 2 ])
endfor
∗)
257 / 400
Ultimatives Heapsort, 2. Teil
Algorithmus 2.11
Baue den Heap um n2 Elemente ab:
for j = n downto ⌊ n2 ⌋ + 1 do
swap(1, j);
down(1, j − 1)
endfor
(∗ Das Restfeld a[1 . . . ⌊ n2 ⌋] ist unsortiert ∗)
(∗ Rekursion ∗)
ult-heap(a[1 . . . ⌊ n2 ⌋])
endif
(∗ Das Feld a[1 . . . n] ist in umgekehrter Reihenfolge sortiert ∗)
endprocedure
258 / 400
Absinken eines Element
Algorithmus 2.12 Absinken eines Elements
procedure down(i , j):
if 2i > j then
skip
elsif 2i = j then
swap(i , j)
elsif a[2i ] < a[2i + 1] then
swap(i , 2i );
down(2i , j)
else
swap(i , 2i + 1);
down(2i + 1, j)
endif
endprocedure
259 / 400
Anzahl der Vergleiche
n · log2 n + Θ(n)
Ultimatives Heapsort ist ein internes Sortierverfahren, das nur auf
Schlüsselvergleichen beruht und bis auf evtl. Verbesserungen im
linearen Term optimal ist. Asymptotisch sind keine weiteren
Verbesserungen möglich.
Aufgrund der hohen Konstanten, die sich in dem linearen Term
verstecken, ist dieses Verfahren nur theoretischen Interesse.
Soll Heapsort in der Praxis verwendet werden, so ist reines
Bottom-Up-Heapsort oder Quick-Heapsort die geeignete Variante.
260 / 400
Quick-Heapsort
Idee: Anstatt den Median als Pivot-Element zu verwenden, nehme
einfach ein zufällig gewähltes Element oder den Median aus r (n)
2
zufälligen
q Elementen mit r (n) ∈ o(n) (z.B. r (n) = log (n) oder
r (n) = logn n ).
261 / 400
Quick-Heapsort
Algorithmus 2.13
procedure quick-heap(A[1 . . . n])
begin
if n klein“ then sortiere direkt
”
else
pivotIndex := wähle Pivot;
k := partitioniere-umgekehrt(A[1 . . . n], pivotIndex);
if k ≤ n/2 then
zwei-S-Max-Heap(A[1 . . . n], k);
(∗
quick-heap(A[1 . . . n − k]);
(∗
else
zwei-S-Min-Heap((A[1 . . . n], n − k + 1);
(∗
quick-heap(A[n − k + 2 . . . n]);
(∗
endif
endif
endprocedure
L = {1 . . . k}
Rekursion
∗)
L = {k . . . n}
Rekursion
∗)
∗)
∗)
262 / 400
Quick-Heapsort
Algorithmus 2.14
procedure zwei-S-Max-Heap(A[1 . . . n], k)
begin
(∗ L = {1, . . . , k}, S = {k + 1, . . . , n} ∗)
(∗ d.h. A[i] ≥ A[j] für alle i ∈ L, j ∈ S ∗)
(∗ Stelle Heap-Eigenschaft für A[1 . . . k] her (max. 2k Vergleiche) ∗)
for i = ⌈ k2 ⌉ downto 1 do
reheap-max(A[1 . . . k], i );
endfor
(∗ Baue den Heap um k Elemente ab (max. k ⌊log k⌋ Vergleiche): ∗)
for i = 1 to k do
swap(A[n − i + 1], A[1]);
down-max(1, k);
endfor
endprocedure
263 / 400
Quick-Heapsort
Algorithmus 2.15 Absinken eines Elements
procedure down-max(i , j):
begin
if 2i > j then
skip
elsif 2i = j then
swap(i , j);
elsif a[2i ] < a[2i + 1] then
swap(i , 2i );
down-max(2i , j);
else
swap(i , 2i + 1);
down-max(2i + 1, j);
endif
endprocedure
264 / 400
Quick-Heapsort
Algorithmus 2.16
procedure zwei-S-Min-Heap(A[1 . . . n], l )
begin
(∗ Stelle Heap-Eigenschaft für A[n − l + 1 . . . n] her (ca. 2l Vergleiche) ∗)
for i = n − ⌈ 2l ⌉ to n do
reheap-min(A[n − l + 1 . . . n], i );
endfor
(∗ Baue den Heap um l Elemente ab: ∗)
for i = 1 to l do
swap(A[i ], A[n]);
down-min(1, l );
endfor
endprocedure
265 / 400
Partitionieren
Algorithmus 2.17 Partitionieren
function partitioniere-umgekehrt(A[ℓ . . . r ],pivotIndex)
begin
pivot := A[pivotIndex];
swap(A[r ], A[pivotIndex]);
i := ℓ;
for j = ℓ to r − 1 do
if A[j] ≥ pivot then
swap(A[i ], A[j]);
i := i + 1;
endif
endfor
swap(A[r ], A[i ]);
return i ;
endprocedure
266 / 400
Analyse von Quick-Heapsort: worst case
worst case: Im Fall r (n) = 1 (also der Verwendung eines zufälligen
Elementes als Pivot) wie bei Quicksort: Wird immer das kleinste oder
größte Element wird als Pivotelement gewählt, so werden Θ(n2 )
Vergleiche benötigt.
Aber: Wahrscheinlichkeit, dass “viele” Vergleiche benötigt werden ist
nicht größer als bei Quicksort, denn jede Wahl von Pivotelementen die
bei Quick-Heapsort zu langen Laufzeiten führt, führt auch bei
Quicksort zu langen Laufzeiten.
Für r (n) ∈ ω(1), worst-case Laufzeit etwas besser und sehr
unwahrscheinlich, dass ein “worst-case” auftritt.
267 / 400
Analyse von Quick-Heapsort: average case
average case: Da im Gegensatz zu Quicksort immer nur ein rekursiver
Aufruf erfolgt, wird die mittlere Laufzeit nicht von den rekursiven
Aufrufen, sondern von der Zeit zum Abbau der Heaps (O(n log n))
bestimmt, vorausgesetzt, dass das Array immer ungefähr mittig
augeteilt wirt.
Sei Q(n) die mittlere Laufzeit von Quick-Heapsort. Die exakte
Bestimmung von Q ist sehr schwierig (zunächst ist eine exakte
Durchschnittsanalyse von Heapsort notwendig). Wir können Q(n)
jedoch nach oben durch eine Funktion T (n) abschätzen. T (n) sei
durch folgende Rekursionsgleichung gegeben:
n 1X
T (max(k − 1, n − k))
T (n) = n − 1 +
n
k=1
+ (log(min(k, n − k + 1) + 2) min(k, n − k + 1)
268 / 400
Analyse von Quick-Heapsort: average case
Wir zeigen:
T (n) ≤ n log n + (c + ǫ)n + dǫ
für beliebiges ǫ > 0 (beachte, dass dǫ von ǫ abhängt) also
T (n) ≤ n log n + cn + o(n).
n 1X
T (max(k − 1, n − k))
T (n) = n − 1 +
n
k=1
+ (log(min(k, n − k + 1)) + 2) min(k, n − k + 1)
!
⌈ n2 ⌉
n−1
X
X
1
T (k) + 2
=n−1+
(log(k) + 2)k
2
n
k=1
k=⌊ n2 ⌋
269 / 400
Analyse von Quick-Heapsort: average case
n−1
lnm
2 X
≤ n−1+
k log k + n log
n
2
I .V .
k=1
+
n−1
X
⌈ n2 ⌉
X
2k
((c + ǫ)k + dǫ ) +
k=1
k=⌊ ⌋
n
2
1 2
1 2
n log n −
n + bn log n
2
4 ln 2
!
n 2
n 2 1
1
1
+
(c + ǫ) + dǫ + 2
n2 −
2
2
2
2 2
≤n−1+
2
n
wobei b eine Konstante (unabhängig von n und ǫ) ist, die sich aus den
Fehlern durch Weglassen einzelner Summenglieder zusammensetzt.
270 / 400
Analyse von Quick-Heapsort: average case
1
3
3
= n log n + −
+ (c + ǫ) +
n + b log n + dǫ
2 ln 2 4
2
≤ n log n(c + ǫ)n + dǫ
mit c = 2(3 − ln12 ) ≤ 3.12 und geeignetem dǫ , denn für n groß genug,
ist b log n ≤ 4ǫ n. Für die endlich vielen Werte von n, wo die
Abschätzung nicht gilt, kann dǫ > T (n) gewählt werde.
Numerische Berechnungen legen nahe, dass gilt:
T (n) = n log n + cn − b log n mit b zwischen 5 und 6.
Zum Vergleich bei Quicksort: Q(n) = 1, 38n log n − 2, 8n. Für n > 216
ist damit Quick-Heapsort schneller.
271 / 400
Analyse von Quick-Heapsort: average case
Verbesserungsmöglichkeiten:
◮
Genauere Abschätzung (für Heapabbau)
◮
Heapaufbau bottom-up
◮
Merken der schwarzen und weißen Elemente in einem Bit-Vektor
◮
Mehr Elemente zur Pivotbestimmung verwenden
272 / 400
12. Vorlesung, 05.12.11
Thema: Minimale Schnitte
Das Ziel ist die Berechnung eines Schnittes C mit minimalem
Gewicht. Der nachfolgende Algorithmus zur Berechnung eines
minimalen Schnittes stammt von M. Stoer und F. Wagner (1994).
273 / 400
Minimale Schnitte
Sei G = (V , E , γ) ein ungerichteter Graph
γ : E → N das Kantengewicht, durch Null fortgesetzt.
Ṡ
Ein Schnitt C = (C1 , C2 ) ist eine Partition: V = C1 C2 .
Das Gewicht g (C ) von C ist die Summe der Gewichte der Kanten, die
die beiden Teilmengen trennen:
g (C ) =
X
γ(vw )
v ∈C1 ,w ∈C2
Ein s-t-Schnitt ist ein Schnitt (C1 , CP
2 ) mit s ∈ C1 und t ∈ C2 .
Für A ⊆ V , v ∈ V setze g (A, v ) = a∈A γ(av ).
Oder einheitlich:
X
g (A, B) =
γ(ab)
a∈A,b∈B
274 / 400
Phasen
Der Algorithmus arbeitet in Phasen.
1) Berechne für zwei Punkte s, t ∈ V , s 6= t einen minimalen
s-t-Schnitt Cphase .
Achtung: Die Punkte s und t liegen vorher nicht fest!
2) Verschmelze s und t zu einem neuen Punkt {s, t}:
Die Kanten zwischen s und t werden gelöscht und für x ∈
/ {s, t} setzt
man γ({s, t}x) = γ(sx) + γ(tx).
Der neue Graph heiße G /(s = t).
3) Rekursion: Sei C ′ der rekursiv berechnete Schnitt in G /(s = t).
Dann ist C ′ ein minimaler Schnitt von G unter der Nebenbedingung,
dass C ′ die Knoten s und t nicht trennt.
Der minimale Schnitt ist das Minimum von g (Cphase ) und g (C ′ ).
275 / 400
Minimale s-t-Schnitte
Es kommt also im Wesentlichen darauf an, den minimalen s-t-Schnitt
für gewisse Knoten s, t ∈ V , s 6= t zu berechnen. Diese Phase verläuft
analog zum Prim-Algorithmus.
Zu Beginn der Phase gelte B = {v1 } und R = V \ {v1 }. Die Knoten
aus B werden Baumknoten und die Knoten aus R Randknoten
genannt.
P
Für einen Randknoten v ∈ R sei stets g (v ) = x∈B γ(xv ).
(Gibt es keine Kante zwischen B und v , so hat diese Summe den
Wert Null. Damit ist der Wert g (v ) = γ(v1 v ) in der Zeit O(|R|) für
den Rand initialisiert.)
276 / 400
while |R| > 1 do
Zu Beginn jeder Schleife wird ein delete max ausgeführt. Dies
bestimmt einen Knoten v ∈ R mit g (v ) ≥ g (w ) ∀w ∈ R und entfernt
v aus R.
Es wird B um v ergänzt (v wird also ein Baumknoten) und die
Schlüsselwerte werden mit increase key vergrößert:
Für w ∈ R \ {v } setzt man g (w ) := g (w ) + γ(vw ). Eine Invariante
ist also
∀w ∈ R : g (w ) = g (B, w )
277 / 400
Termination
Die Schleife wird solange durchlaufen,
P bis der Rand nur noch einen
Knoten t enthält. Dann gilt g (t) = vt∈E γ(vt), d.h. g (t) ist die
Summe der Gewichte aller von t ausgehenden Kanten. Sei s der
Knoten, der unmittelbar vor t zum Baumknoten wurde. Dann
definiert die Zerlegung (V \ {t}, {t}) einen s-t-Schnitt Cphase mit
dem Gewicht g (t).
278 / 400
Korrektheitslemma
Lemma 2.22
Der Schnitt Cphase ist ein minimaler s-t-Schnitt im Eingabegraphen
der Phase.
Beweis:
Sei v1 , v2 , . . . , vn eine Anordnung der Knoten in der Reihenfolge, in
der sie zu Baumknoten wurden. Es gilt vn−1 = s, vn = t. Sei
C = (C1 , C2 ) ein minimaler s-t-Schnitt mit s ∈ C1 , t ∈ C2 .
Wir zeigen:
g (Cphase ) ≤ g (C )
279 / 400
g (Cphase) ≤ g (C )
In der Folge v1 , . . . , vn−1 , vn wird ein Knoten vi aktiv genannt, falls C
ein vi −1 -vi -Schnitt ist.
Da C ein s-t-Schnitt ist, ist insbesondere vn = t aktiv.
Sei vi aktiv; setze B(i ) = {v1 , . . . , vi −1 } und betrachte den durch
B(i ) ∪ {vi } induzierten Untergraphen. Durch Einschränkung definiert
C auf diesem Untergraphen einen Schnitt. Diesen bezeichnen wir mit
C (i ) und das Gewicht mit g (C (i )).
Da t = vn aktiv ist und g (Cphase ) = g (B(n), t) sowie C (n) = C
gelten, genügt es, die Behauptung g (B(i ), vi ) ≤ g (C (i )) für alle
aktiven Knoten vi zu zeigen.
Für den bzgl. der obigen Folge ersten aktiven Knoten vi gilt
g (B(i ), vi ) = g (C (i )), denn die durch C induzierte Partition ist
({v1 , . . . , vi −1 }, {vi }). Dies ist die Induktionsverankerung.
280 / 400
Fortsetzung: Beweis
Sei jetzt vi ein aktiver Knoten für den die Behauptung richtig ist und
vj der nächste auf vi folgende aktive Knoten. Dann gilt 1 < i < j ≤ n.
Zu zeigen ist: g (B(j), vj ) ≤ g (C (j)).
Es gilt zunächst:
g (B(j), vj ) = g (B(i ), vj ) + g ({vi , . . . , vj−1 }, vj )
Da der Knoten vi vor vj gewählt wurde, gilt g (B(i ), vj ) ≤ g (B(i ), vi )
und nach Induktion gilt g (B(i ), vi ) ≤ g (C (i )). Alle Kanten zwischen
{vi , . . . , vj−1 } und vj sind Schnittkanten und tragen somit zum
Gewicht von C (j) bei. Daher
g (C (i )) + g ({vi , . . . , vj−1 }, vj ) ≤ g (C (j)), und
insgesamt g (B(j), vj ) ≤ g (C (j)).
281 / 400
Laufzeit Phase
In jeder Phase wird also ein minimaler s-t-Schnitt berechnet. Die
Laufzeitanalyse einer Phase ist identisch zum Prim-Algorithmus:
Wird der Rand R als ein Feld oder als eine Liste verwaltet, so ergibt
sich die Zeit n · O(n) + e · O(1) = O(n2 ).
Bei Verwendung eines Heaps erhalten wir
n · O(log n) + e · O(log n) = O(e log n).
Mit Fibonacci-Heaps ergibt die beste Abschätzung:
n · O(log n) + e · O(1) = O(e + n log n)
282 / 400
Gesamtlaufzeit
Nach jeder Phase wird der Graph durch Verschmelzen der beiden
Knoten s und t verkleinert. Dies ist eine O(n)-Operation. Das
Gewicht des jeweils aktuellen minimalen Schnittes ergibt sich in O(1)
durch einen direkten Gewichtsvergleich. Die Aktualisierung der
Partition (C1 , C2 ) kostet dann O(n) Schritte. Der Algorithmus
terminiert, wenn der Graph nur noch einen Punkt enthält, d. h., es
werden (n − 1) Phasen durchlaufen. Dies ergibt die Gesamtzeit O(n3 )
für Felder oder Listen, O(ne log n) für Heaps und O(ne + n2 log n) für
Fibonacci-Heaps.
283 / 400
Phase
Algorithmus 2.18
function Phase (G = (V , E , γ) || |V | ≥ 2; v1 ∈ V );
Die Ausgabe besteht aus zwei Knoten s, t mit dem Gewicht gphase
*)
var
B : Knotenmenge;
R : Knotenmenge;
s, t, v , w : V ;
begin
B := {v1 }; R := V \ {v1 };
forall v ∈ R do
g (v ) := γ(v1 v );
endfor
s := v1 ;
eines minimalen s-t-Schnittes.
(∗ Baumknoten ∗)
(∗ Randknoten ∗)
(∗ Initialisierungen ∗)
284 / 400
Berechnung eines minimalen Schnittes
Algorithmus 2.19 Fortsetzung
while |R| > 1 do
s := Knoten v ∈ R mit g (v ) ≥ g (w )∀w ∈ R;
R := R \ {s};
B := B ∪ {s};
forall sw ∈ E , w ∈ R do
g (w ) := g (w ) + γ(sw )
endfor
endwhile
t := der letzte Knoten in R
return (s, t, g (t))
endfunction
(∗ begin delete max ∗)
(∗ end delete max ∗)
(∗ increase key ∗)
285 / 400
Berechnung eines minimalen Schnittes
Algorithmus 2.20
function Min Cut( G = (V , E , γ), |V | ≥ 2)
(Partition C1 , C2 mit minimalem Schnitt g );
var
C1 , C2 : Knotenmenge;
(∗ Partition von V ∗)
s, t : V ; g , gphase : integer;
begin
wähle ein v1 ∈ V ; (s, t, gphase ) := Phase(G , v1 );
verschmelze s und t; der neue Graph sei G /(s = t);
if |V | = 2 then C1 := {s}; C2 := {t}; g := gphase ;
else (C1 , C2 , g ) := Min Cut (G /(s = t));
endif
(∗ Die Partition (C1 , C2 ) von G /(s = t) ist auch ein Schnitt von G . ∗)
286 / 400
Berechnung eines minimalen Schnittes
Algorithmus 2.21 Fortsetzung und Ende von Min Cut
if gphase ≤ g then
C1 := V \ {t};
C2 := {t};
g := gphase ;
endif
return (C1 , C2 , g )
endfunction
287 / 400
Beispiel: Berechnung eines minimalen Schnittes
2
1
3
2
5
3
2
2
3
6
2
1
4
3
4
2
7
2
3
8
Abbildung: Ein Graph G = (V , E ) mit Kantengewichten γ : E → N. Der
Startknoten ist Knoten 2.
288 / 400
Beispiel: Berechnung eines minimalen Schnittes
t
2
1
3
a
2
2
5
s
2
3
6
f
b
3
3
2
1
4
c
4
2
7
d
2
3
8
e
Abbildung: Der Graph nach der ersten Phase mit Startknoten 2. Die Knoten
wurden in der Reihenfolge a, b, c, d, e, f , s, t besucht. Der gefundene Schnitt
ist die Partition {1} , {2, 3, 4, 5, 6, 7, 8} mit Gewicht g = 5.
289 / 400
Beispiel: Berechnung eines minimalen Schnittes
a
2
4
1,5
b
2
3
6
c
d
3
3
2
1
4
c
4
2
7
s
2
3
8
t
Abbildung: Der Graph nach der zweiten Phase. Die Knoten wurden in der
Reihenfolge a, b, c, d, e, s, t besucht. Der gefundene Schnitt ist die Partition
{8} , {1, 2, 3, 4, 5, 6, 7} mit Gewicht g = 5.
290 / 400
Beispiel: Berechnung eines minimalen Schnittes
a
2
4
1,5
b
6
c
3
2
2
3
d
3
1
4
s
4
4
7,8
t
Abbildung: Nach der dritten Phase. Der gefundene Schnitt ist die Partition
{7, 8} , {1, 2, 3, 4, 5, 6} mit Gewicht g = 7.
291 / 400
Beispiel: Berechnung eines minimalen Schnittes
a
3
2
4
1,5
b
s
3
6
2
3
6
c
1
4,7,8
t
Abbildung: Nach der vierten Phase. Der gefundene Schnitt ist die Partition
{4, 7, 8} , {1, 2, 3, 5, 6} mit Gewicht g = 7.
292 / 400
Beispiel: Berechnung eines minimalen Schnittes
t
a
3
2
4
1,5
b
2
3
3,4,7,8
1
6
s
Abbildung: Nach der fünften Phase. Der gefundene Schnitt ist die Partition
{3, 4, 7, 8} , {1, 2, 5, 6} mit Gewicht g = 4.
293 / 400
Beispiel: Berechnung eines minimalen Schnittes
a
2
5
4
1,5
t
3
3,4,6,7,8
s
Abbildung: Nach der sechsten Phase. Der gefundene Schnitt ist die Partition
{1, 5} , {2, 3, 4, 6, 7, 8} mit Gewicht g = 7.
294 / 400
Beispiel: Berechnung eines minimalen Schnittes
s
2
9
V \2
t
Abbildung: Nach der siebten Phase. Der gefundene Schnitt ist die Partition
{2} , {1, 3, 4, 5, 6, 7, 8} mit Gewicht g = 9.
295 / 400
Übung: Wie verhält sich der Algorithmus bei negativen
Kantengewichten, bleibt er korrekt?
296 / 400
Antwort:
Bei negativen Kantengewichten arbeitet der Algorithmus im
Allgemeinen nicht korrekt:
1
3
2
−2
2
4
1
5
3
Abbildung: Beispiel für einen Graphen, bei dem der Algorithmus mit
Startknoten 1 kein korrektes Ergebnis erzielt: In der ersten Phase werden die
Knoten in der Reihenfolge 1, 2, 3, 4 besucht und es wird der Schnitt
{4} , {1, 2, 3} gefunden (g = 3). Dann werden die Knoten 3 und 4
verschmolzen. Der minimale Schnitt (g = 2) ist jedoch {1, 4} , {2, 3}.
297 / 400
13/14. Vorlesung, 20./21.12.11
Thema: Schnelle Multiplikation großer Zahlen nach
Schönhage-Strassen
Satz 2.23
Zwei Binärzahlen der Länge n lassen sich in der Zeit
O(n · log n · log log n) multplizieren.
Nach Martin Führer kann der log log n-Faktor durch 2log
werden. Dies kann man als eine Konstante ansehen.
∗
n
ersetzt
298 / 400
15. Vorlesung, 09.01.12
Parallel Algorithms and NC
299 / 400
Parallel Algorithms and NC
1. Introduction to Parallel Architectures
2. The Class NC and Parallel Matrix Multiplication
3. Parallel Prefix and the Hypercube
4. Integer Addition, Multiplication, and Division
300 / 400
Introduction to Parallel Architectures
◮
Parallel Random Access Machines (PRAM)
◮
◮
◮
◮
Vector machines
◮
◮
CRCW (Concurrent Read Concurrent Write)
EREW (Exclusive Read Exclusive Write)
CREW (Concurrent Read Exclusive Write)
SIMD or MIMD
Boolean and arithmetic circuits
◮
◮
dag with input and output nodes
basic bit and arithmetic operation nodes
301 / 400
The Class NC
Problems which are “efficiently parallelizable”.
NC is called Nick’s Class (after Nick Pippenger).
Definition(s):
◮
Problems solvable on a PRAM in (log n)O(1) time with nO(1)
Processors.
◮
Problems solvable on a boolean circuit of depth (log n)O(1) and
nO(1) size.
The class is robust to minor changes of the machine model.
?
The question NC = P is still open.
There are P-complete problems. (e.g. circuit value problem)
So we think that these problems do not belong to NC .
If G is context-free, then L(G ) belongs to NC , but
{ G | G is context-free with L(G ) 6= ∅ } is P-complete.
302 / 400
Computing the sum s =
Pn
i=0 xi
x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
s
s=
Pn
i =0 xi
computable on n processors in log n time.
303 / 400
Parallel Matrix Multiplication
Let A and B be two n × n matrices, compute:
(AB)ij =
n
X
Aik Bkj
k=1
Solvable with n3 Processors in time 1 + log n.
◮
With n3 processors compute all n3 products.
◮
For each of the n2 sums assign n processors.
◮
In log n steps compute all n2 sums.
304 / 400
Parallel Prefix
The Problem:
◮
◮
Input: xi (0 6 i 6 n − 1)
P
Output: yi = ij=0 xj (0 6 i 6 n − 1)
To get the idea: compute just the sum yn−1 =
Pn−1
j=0
xj
305 / 400
Parallel Prefix
x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
y0 y1 y2 y3 y4 y5 y6 y7 y8 y9 y10 y11 y12 y13 y14 y15
306 / 400
Parallel Prefix
Let xi = 0 for all i < 0.
x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
yi =
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
yi =
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
yi =
Pi
j=i −1 xj
Pi
j=i −3 xj
i
X
xj
j=i −7
i
X
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕
⊕ yi =
xj
y0 y1 y2 y3 y4 y5 y6 y7 y8 y9 y10 y11 y12 y13 y14 y15
j=i −15
307 / 400
Integer Addition in NC
Two n bit binary numbers may be added in log n time with n
processors.
Input an−1 . . . a3 a2 a1 a0 and bn−1 . . . b3 b2 b1 b0
Compute
 cn cn−1 . . . c3 c2 c1 c0 the carry propagate string:
 0 ai −1 = bi −1 = 0 ∨ i = 0
ci =
1 ai −1 = bi −1 = 1

p else
Compute carryi from ci with parallel prefix using the following binary
operation:
0·x = 0
1·x = 1
p·x = x
308 / 400
Integer Addition in NC
Two n bit binary number may be added in log n time with n
processors.
Example 1:
c = 1p01010p1ppp0p10
carry = 1001010110000110
a = 100101011101011
b = 110101001010001
sum = 1011010100111100
309 / 400
Integer Addition in NC
Two n bit binary number may be added in log n time with n
processors.
Example 2:
c = 1p0101001ppp0110
carry = 1001010010000110
a = 100101001101011
b = 110101001010011
sum = 1011010010111110
310 / 400
Integer Multiplication in NC
Goal:
Two n bit binary numbers can be multiplied in O(log n) time with
O(n2 ) processors.
311 / 400
Integer Multiplication in NC
Straightforward:Two n bit binary numbers may be multiplied in
O(log n)2 time with On2 processors.
The addition of three n bit binary numbers may be done by adding
two n + 1 bit binary numbers.
100111
011100
+111101
10
01
11
111101
10
+000110
10
+10
312 / 400
Integer Multiplication in NC
Two n bit binary numbers may be multiplied in O(log n) time with
2 · n2 processors.
In constant time compute from each block of three numbers a new
block of two numbers with one bit more.
Repeat this until just two binary numbers remain. This takes O(log n)
time.
Add the remaining two numbers in O(log n) time.
313 / 400
Integer Division in NC
Goal:
For given binary numbers s, t > 0 of 6 n bits, determine the unique
numbers q and r such that s = qt + r and 0 6 r < t.
We will do this using time O((log n)2 ) with O(n4 ) processors.
Main tool: Newton’s method for approximating roots.
Let f : R → R.
Guess an initial value x0 and compute a sequence (xi )i >0 using the
recurrence
f (xi )
, where f ′ = df /dx
xi +1 = xi − ′
f (xi )
If we are lucky, then the sequence (xi )i >0 converges to a root of f .
314 / 400
Integer Division in NC
Take f (x) = t − x1 , thus the unique root of f is 1t .
f ′ (x) =
1
,
x2
thus Newton’s recurrence becomes
xi +1
t − x1i
f (xi )
= xi − 1 = 2xi − txi2
= xi − ′
f (xi )
x2
i
Choose the starting value x0 as the unique number of the form
(j > 0) in the interval ( 2t1 , 1t ].
1
2j
We determine x0 in time O(log n) using n processors as follows: find
the unique power of 2 in the interval [t, 2t). Say this is 1000, then
x0 = 0.001 i.e., reverse the order of the bits, and place a binary point
after the first 0.
315 / 400
Integer Division in NC
Lemma 6.1
The unique sequence (xi )i >0 that is obtained from xi +1 = 2xi − txi2
(with x0 being the unique number of the form 21j (j > 0) in the
interval ( 2t1 , 1t ]) satisfies 0 6 1 − t · xi < (21i ) .
2
Proof: Induction on i :
By definition
1
2t
< x0 6 1t , i.e., 0 6 1 − tx0 < 12 .
For i > 0, we have
1 − t · xi +1 = 1 − t(2 · xi − t · xi2 ) = (1 − t · xi )2
Thus 0 6 1 − t · xi +1 <
1
i
2(2 )
2
=
1
i +1 .
2(2 )
316 / 400
Integer Division in NC
From the previous lemma, we obtain for k = ⌈log(log(s))⌉:
0 6 1 − t · xk < 1s 6 st .
Thus, 0 6
s
t
− s · xk < 1.
It follows, that the integer part q of st is either ⌈s · xk ⌉ or ⌊s · xk ⌋ (the
true value can be found by testing, i.e., a single multiplication).
The remainder r can be found by r = s − qt.
317 / 400
Integer Division in NC
Estimation of the running time: The number of bits of
xi +1 = 2 · xi − t · xi2 is at most 2 times the number of bits of xi ).
Thus, the number of bits of xk (and all xi with i < k) is at most
2⌈log(log(s))⌉ n which is bounded by O(n2 ).
Hence, the calculation of xi +1 = 2 · xi − t · xi2 from xi takes time
O(log(n)) with O(n4 ) processors.
Since, k ∈ O(log(n)), the total running time is O((log n)2 ).
The result is not optimal, since IntegerDivision is in uniform TC0 .
318 / 400
16. Vorlesung, 10.01.12
Thema: Parallele Erkennung kontextfreier Sprachen
319 / 400
Erkennung kontextfreier Sprachen
Gegeben sei eine kontextfreie Sprache als Grammatik G in
Chomsky-Normalform (d.h. nur Regeln A → BC und A → a). Zu
einem gegebenen Wort w der Länge n soll nun die Frage beantwortet
werden, ob w ∈ L(G ).
Standard:
1. L(G ) ∈ DTIME(n3 ) mittels CYK.
2. L(G ) ∈ DTIME(nlog2 7 ) mittels Valiant und Strassen.
Satz 7.1 (Lewis, Stearns, Hartmanis, 1965)
Sei L eine kontextfreie Sprache, dann gilt L ∈ DSPACE (log2 n).
Wir zeigen die folgende Verbesserung:
Satz 7.2
Sei L eine kontextfreie Sprache, dann gilt L ∈ SAC 1 .
320 / 400
Von der Idee zum Bauplan
∗
Falls w ∈ L(G ), dann gibt es eine Ableitung S ⇒ w . Da G in
Chomsky-Normalform vorliegt, können wir uns diese Ableitung als
Binärbaum vorstellen (Blätter = Terminale, Buchstaben von w ; innere
Knoten = Nichtterminale):
S
w1
···
wn
Die Idee ist nun: Versuche, Teilbäume für Teilworte von w zu finden
(und diese mit Regeln der Grammatik zu einem Baum zusammen zu
setzen, der uns den Schaltkreis liefert).
321 / 400
Sei dazu wi ,j das Teilwort von w , das die Buchstaben an den
Positionen i + 1 bis j umfasst. Mit dieser Notation lautet unsere Frage
also:
∗
Gibt es eine Ableitung S ⇒ w0,n , und entsprechend für die Suche
∗
nach Teilworten z.B. A ⇒ wi ,j ?
∗
Ist j = i + 1, dann gilt |wi ,j | = 1 und daher ist A ⇒ wi ,j die Frage, ob
es die Regel A → wj gibt.
Damit erzeugen wir Eingangsgatter (A, i , i + 1) die wahr oder falsch
liefern, je nachdem, ob es die Regel A → wj gibt.
Die Zahl der Eingangsgatter ist linear in n.
322 / 400
Interessant sind also nur die Fälle mit |wi ,j | > 2.
Das nutzen wir für einen rekursiven Lösungsansatz aus: Statt eine
∗
Ableitung A ⇒ wi ,j direkt zu suchen, suchen wir stattdessen eine
∗
∗
Ableitung B ⇒ wi ′ ,j ′ und eine der Form A ⇒ wi ,i ′ Bwj ′ ,j .
Wir probieren dazu alle möglichen i 6 i ′ < j ′ 6 j aus, die der weiter
unten beschriebenen 2/3-Einschränkung genügen.
Dies reflektiert sich im unbegrenzten Fan-in der Oder-Gatter
Der Sachverhalt sich schön in Bildern darstellen:
A
A
B
=
i
···
j
i · · · i′
B
+
j ′· · · j
i′
···
j′
323 / 400
Und-Oder-level
Nochmals:
A
A
B
=
i
···
i · · · i′
j
B
+
j ′· · · j
i′
···
j′
Dies bedeutet, ein Gatter (A; i , j) ist ein Oder-Gatter mit quadratisch
vielen Eingängen für alle Paare (i ′ , j ′ ).
Die Eingänge führen zu Und-Gattern, die der rechten Seite
entsprechen und zu den Gattern (A, B; i , i ′ , j, j ′ ) und (B; i ′ , j ′ ) führen.
(A; i , j) =
_
B,i ′ ,j ′
(A, B; i , i ′ , j ′ , j) ∧ (B; i ′ , j ′ )
324 / 400
Zusammensetzung des Schaltkreises
Wir erlauben beliebigen Fan-in bei den Oder-Gattern, konstanten
Fan-in bei den Und-Gattern. Es gibt keine inneren Negationen.
Die inneren Oder-Gatter tragen eindeutige Namen von Typ
A1 , . . . , Ac ; i1 , . . . , i2c
Hierbei ist c konstant (c 6 3),
Aj sind Variablen der Grammatik,
und ij sind Indizes.
Jeder Index ist ein Wert zwischen 0 und n und benötigt somit log n
Bits.
Die Und-Gatter haben keinen eigenen Namen, aber konstanten Fan-In.
Insgesamt gibt es nur polynomiell viele Gatter.
Wichtig: Begrenze die Schaltkreistiefe auf O(log n).
325 / 400
Platzbedarf für Indizes in einem Schritt
Wir sehen in der Bilder-Gleichung oben, dass wir für den Baum mit
Wurzel B schlicht Rekursion anwenden können, bis wir bei den
Blättern ankommen. Aber wir haben auch einen zweiten Baum
erhalten, und dieser hat einen Zacken“; diesen Baum müssen wir
”
anders behandeln.
Wir können hierfür das Spielchen fortsetzen und erhalten entweder:
A
A
B
i · · ·i ′
B
=
j′
···
j
i · · ·i ′
C
j ′· · · k
+
ℓ· · · j
C
k ···
ℓ
326 / 400
Oder:
A
A
C
B
i
···
i′
C
=
j′ · · ·
j
B
+
i ··· k
ℓ··· j
k· · ·i ′
j ′· · · ℓ
Dieser zweite Fall ist jedoch nur eine (zweifache) Rekursion, bringt
also nichts Neues.
Im ersten Fall aber ist auch ein Baum mit zwei Zacken entstanden.
Würden wir auch für diesen das Spielchen fortsetzen, dann würden wir
immer mehr Zacken erhalten.
Daher schieben wir für solche Bäume einen Vereinfachungsschritt ein.
Es muss hier eine Regel der Form D → EF geben, die wir für folgende
Gleichung ausnutzen:
327 / 400
A
A
D
B
i · · ·i ′
C
j ′· · ·k
E
=
ℓ· · · j
B
+
i ··· x
z ··· j
F
x· · ·i ′
C
+
j ′· · ·y
y· · ·k
ℓ· · ·z
Ergebnis: Nur Bäume mit einem Zacken, d.h. wir haben insgesamt die
Anzahl der Zacken auf maximal zwei und damit auch die Anzahl der
Indizes auf eine konstante Zahl beschränkt.
328 / 400
Aber was ist mit der Schaltkreisstiefe?
Wünschenswert für eine logarithmische Tiefe wäre es, wenn wir die
Höhe der rekursiv weiter zu verarbeitenden Bäume jeweils halbieren
könnten.
Dies wird nicht gelingen. Aber wir sind auch mit einer 2/3 Aufteilung
zufrieden.
Dies erreichen wir wie folgt:
Definiere ein Gewicht
|A1 , . . . , Ac ; i1 , . . . , i2c | = c + (ij − i2 ) + · · · + (i2c−1 − i2c )
329 / 400
Bis auf den Vereinfachungsschritt greifen die Oder-Gatter nur auf
Gatter zurück, die jeweils höchstens ein 2/3 Gewicht haben.
Der Vereinfachungsschritt liefert möglicherweise keine
Gewichtsverkleinerung, aber er wird höchstens in jedem zweiten
Schritt eingeschoben. Dies beweist den Satz.
330 / 400
17. Vorlesung, 16.01.12
Thema: Luby’s Algorithm
331 / 400
Luby’s Algorithm
Recall that an independent set of an undirected graph G = (V , E ) is
a subset I ⊆ V such that (u, v ) 6∈ E for all u, v ∈ I .
Goal: For a given undirected graph G = (V , E ), find an independent
set I ⊆ V of G , which is maximal under inclusion, i.e., if I ⊆ J for an
independent set J then I = J.
Note: The maximum problem is NP-complete.
We want to do this in NC, i.e., in polylogarithmic time using
polynomially many processors.
Note: There is a sequential linear time algorithm.
Our first solution will be a randomized NC-algorithm.
For a set U ⊆ V of nodes let N(U) = {v ∈ V | ∃u ∈ U : (u, v ) ∈ E }
be the set of neighbors of U.
332 / 400
Luby’s Algorithm
Luby’s algorithm works in rounds. In every round we calculate an
independent set I in the current graph G and remove I ∪ N(I ) (and
all edges that are incident with a node from I ∪ N(I )) from G .
We repeat this until the graph is empty. The calculated independent
set is the union of the independent sets calculated in the rounds.
A single round, where d(v ) = |N(v )| for v ∈ V :
1
◮ In parallel: for every node v ∈ V , put v with probability
2d(v )
into a set S (isolated nodes can be put into S into a
preprocessing
step),Qindependently from the other nodes (i.e.,
V
Pr( ki=1 vi ∈ S) = ki=1 Pr(vi ∈ S)).
◮ Pairwise independence is enough!
Pr(u ∈ S ∧ v ∈ S) = Pr(u ∈ S) · Pr(v ∈ S)
◮
In parallel: For every (u, v ) ∈ E with u, v ∈ S, remove from S
the node with the smaller degree (break ties arbitrarily). Call the
remaining set I ; it is an independent set.
333 / 400
Luby’s Algorithm
◮
A single round can be done in constant time using O(|V |2 )
processors.
◮
We will show that the expected value of the number of rounds is
in O(log |E |).
◮
First step: We show that the expected number of edges that are
1
of the total number of edges.
deleted in every round is at least 72
334 / 400
Luby’s Algorithm
Lemma 8.1
For every node v : Pr(v ∈ I ) >
1
4d(v )
Proof: We will show that
Pr(v 6∈ I | v ∈ S) 6
1
2
Then we obtain:
Pr(v ∈ I ) = Pr(v ∈ I | v ∈ S) · Pr(v ∈ S)
1
1
>
· Pr(v ∈ S) =
.
2
4d(v )
335 / 400
Luby’s Algorithm
We have
Pr(v 6∈ I | v ∈ S) 6 Pr(∃u ∈ L(v ) : u ∈ S | v ∈ S)
where L(v ) = {u ∈ N(v ) | d(u) > d(v )} .
Thus:
Pr(v 6∈ I | v ∈ S) 6
=
X
u∈L(v )
X
u∈L(v )
=
X
u∈L(v )
6
X
u∈L(v )
Pr(u ∈ S | v ∈ S)
Pr(u ∈ S)
(pairwise independence)
1
2d(u)
1
1
6 , since L(v ) ⊆ N(v ).
2d(v )
2
336 / 400
Luby’s Algorithm
Definition: A node v ∈ V is good, if
X
u∈N(v )
1
1
>
2d(u)
6
(intuition: many neighbors with small degree), otherwise v is bad.
An edge (u, v ) ∈ E is good, if u or v is good, otherwise it is bad.
Lemma 8.2
For a good node v ∈ V we have Pr(v ∈ N(I )) >
1
36 .
Proof:
1
Case 1: ∃u ∈ N(v ) : 2d(u)
> 16 .
Then, by the previous lemma,
Pr(v ∈ N(I )) > Pr(u ∈ I ) >
1
1
1
>
> .
4d(u)
12
36
337 / 400
Luby’s Algorithm
Case 2: ∀u ∈ N(v ) :
1
2d(u)
6 16 .
Then there exists M(v ) ⊆ N(v ) with
Thus
1
6
6
P
1
u∈M(v ) 2d(u)
Pr(v ∈ N(I )) > Pr(∃u ∈ M(v ) : u ∈ I )
X
X
>
Pr(u ∈ I ) −
u∈M(v )
>
X
u∈M(v )
1
−
4d(u)
u,w ∈M(v ),u6=w
X
u,w ∈M(v ),u6=w
6 31 .
Pr(u ∈ I ∧ w ∈ I )
Pr(u ∈ S ∧ w ∈ S)
(pairwise independence)
X
X
1
1
1
>
−
·
4d(u)
2d(u) 2d(w )
u∈M(v )
u,w ∈M(v )


X
X
1
1  1 1
1 1
−
> · =
=
2d(u) 2
2d(w )
6 6
36
u∈M(v )
w ∈M(v )
338 / 400
Luby’s Algorithm
By the previous lemma, a good edge will be deleted with probability
at least 1/36.
Lemma 8.3
At least half of all edges are good.
Proof: Direct every edge towards its endpoint of higher degree,
breaking ties arbitrarily.
Claim: For every bad node v ∈ V , there are at least twice as many
outgoing edges than incoming edges.
Proof of the claim: Let N1 be the set of predecessors of v after
|N1 |
1
directing the edges. If d(v
) > 3 , then
X
u∈N(v )
1
2d(u)
>
X
u∈N1
1 |N1 |
1
1
= ·
> ,
2d(v )
2 d(v )
6
i.e., v would be good — a contradiction.
339 / 400
Luby’s Algorithm
Thus,
|N1 |
d(v )
< 13 , i.e., |N1 | < 12 (d(v ) − |N1 |), which proves the claim.
Hence, to every bad edge e (for which both endpoints are bad) we
can assign a set P(e) = {e1 , e2 } of two edges e1 6= e2 such that
e 6= f ⇒ P(e) ∩ P(f ) = ∅.
This proves the lemma.
340 / 400
Luby’s Algorithm
Theorem 8.4
Let X be the number of edges that are deleted (in a certain round).
Then for the expected value E(X ) of X we have
E(X ) >
|E |
.
72
Proof.
Let Xe = 1, if e is deleted, otherwise Xe = 0.
Then we have:
X
X
E(X ) =
E(Xe ) >
E(Xe )
e∈E
>
e good
X 1
|E | 1
>
·
36
2 36
e good
341 / 400
Luby’s Algorithm
Let m be the total number of edges in our graph. For i > 0 we define
◮
◮
Si = number of edges that were removed in round 1 · · · i .
Xi = number of edges that are removed in round i .
Thus, S0 = 0, Si 6 m, and Si +1 = Si + Xi +1 .
The statement of the previous theorem can be restated as follows,
1
:
where ε = 72
E(Xi +1 | Si = ℓ) > ε(m − ℓ)
342 / 400
Luby’s Algorithm
Lemma 8.5
E(Xi +1 ) > ε · m − ε · E(Si )
343 / 400
Luby’s Algorithm
Proof.
E(Xi +1 ) =
=
X
k∈N
k · Pr(Xi +1 = k)
X
k,ℓ∈N
=
X
k,ℓ∈N
=
X
k · Pr(Xi +1 = k ∧ Si = ℓ)
k · Pr(Xi +1 = k | Si = ℓ) · Pr(Si = ℓ)
Pr(Si = ℓ)
ℓ∈N
=
X
ℓ∈N
>
X
ℓ∈N
X
k∈N
k · Pr(Xi +1 = k | Si = ℓ)
Pr(Si = ℓ) · E(Xi +1 | Si = ℓ)
Pr(Si = ℓ) · ε(m − ℓ)
= ε·m−ε·
X
ℓ · Pr(Si = ℓ) = ε · m − ε · E(Si )
344 / 400
Luby’s Algorithm
Lemma 8.6
E(Si ) > m(1 − (1 − ε)i ).
Proof.
Induction on i .
The case i = 0 is clear.
For i + 1 we obtain:
E(Si +1 ) = E(Si ) + E(Xi +1 )
> E(Si ) + εm − ε · E(Si ) by the lemma above
= εm + (1 − ε)E(Si )
> εm + m(1 − ε)(1 − (1 − ε)i )
= m(1 − (1 − ε)i +1 )
345 / 400
Luby’s Algorithm
Lemma 8.7
E(Si ) 6 m − 1 + Pr(Si = m)
Proof:
E(Si ) =
6
m
X
j=0
j · Pr(Si = j)
m−1
X
j=0
(m − 1) · Pr(Si = j) + m · Pr(Si = m)
= m · Pr(Si = m) + (m − 1)(1 − Pr(Si = m))
= m − 1 + Pr(Si = m)
346 / 400
Luby’s Algorithm
By the two previous lemmas we have Pr(Si = m) > 1 − m(1 − ε)i .
Thus, Pr(Si < m) 6 m(1 − ε)i .
Choose k ∈ O(log m) such that m(1 − ε)k 6 1.
Then, for i > k we have Pr(Si < m) 6 m(1 − ε)i 6 (1 − ε)i −k .
Define f : N → {0, 1} by
f (x) =
(
1 if x < m
0 otherwise.
Thus, E(f (Si )) = Pr(Si < m) 6 (1 − ε)i −k for i > k.
347 / 400
Luby’s Algorithm
The random variable R = f (S0 ) + f (S1 ) + f (S2 ) + · · · counts the
number of rounds in Luby’s algorithm.
We have
E(R) =
X
i >0
6 k+
E(f (Si )) 6 k +
X
i >k
X
i >k
E(f (Si ))
(1 − ε)i −k = k +
1
∈ O(log m)
ε
We have shown
Theorem 8.8
The expected number of rounds in Luby’s algorithm is in O(log m).
348 / 400
Luby’s Algorithm
In the current version of Luby’s algorithm we put a node v into S
1
with probability 2d(v
).
For this we have to flip n = |V | many biased coins (with
1
1
Pr(head) = 2d(v
) and Pr(tail) = 1 − 2d(v ) ) independently.
It can be shown that nΩ(1) many truely random bits (fair coin flips)
are necessary (and sufficient) in order to approximate these n
independent biased coin flips sufficiently good.
But: In the analysis of Luby’s algorithm, we only used pairwise
independence.
We will show that Ω(log(n)) many random bits suffice in order to
1
generate n = |V | biased coin flips (Pr(head) ≈ 2d(v
) ) that are
pairwise independent.
349 / 400
Luby’s Algorithm
This leads to a derandomized version of Luby’s algorithm:
Assume that α log(n) random bits suffice in order to generate n biased
coin flips, where α is a constant. Let R = {0, 1}α log(n) , thus |R| = nα .
A single round in Luby’s algorithm is replaced by the following
procedure:
for all s = a1 · · · aα log(n) ∈ R do in parallel
simulate the next round of Luby’s algorithm deterministically
with ai = the i -th random bit
endfor
choose that simulation that removes the largest number of edges and
go with the resulting graph to the next round
350 / 400
Luby’s Algorithm
For every v ∈ V let δ(v ) ∈ R such that
7
1
1
1
1
1
·
=
−
6
6
9 2d(v )
2d(v ) 9d(v )
2δ(v )
2d(v )
First, we check that the analysis of Luby’s algorithm also works when
we replace d(v ) by δ(v ) everywhere (in particular,
1
Pr(v ∈ S) := 2δ(v
) ).
Lemma 8.1 (∀v ∈ V : Pr(v ∈ I ) >
1
4δ(v ) ):
✓
Lemma 8.2 (v good ⇒ Pr(v ∈ N(I )) > 1/36): ✓
P
Recall that v is good if u∈N(v )
one of its endpoints is good.
1
2δ(u)
>
1
6
and that an edge is good if
351 / 400
Luby’s Algorithm
Instead of showing that at least half of the edges are good
(Lemma 8.3), we will prove that at least 1/4 of all edges are good.
Again, we direct every edge towards its endpoint with greater δ-value.
Lemma 8.9
Let n1 = |N1 | be the number of incoming edges of a node v . If
7n1
1
18d(v ) > 6 then v is good.
Proof
X
u∈N(v )
1
2δ(u)
>
X
u∈N1
>
n1
1
=
2δ(v )
2δ(v )
7
n1
1
·
>
9 2d(v )
6
352 / 400
Luby’s Algorithm
Thus, if v is bad then
We obtain n1 6
7
n1
1
·
6 .
18 d(v )
6
3
· d(v ).
7
7
4
Thus, d(v ) − n1 > n1 − n1 = n1 .
3
3
Therefore, n1 6
3
(d(v ) − n1 ), i.e., at least 1/4 of all edges is good.
4
If X is the number of edges that are removed (in a certain round),
then we obtain
1
1 |E |
·
=
|E |
E(X ) >
36 4
144
353 / 400
Luby’s Algorithm
We have shown that Luby’s algorithm works with δ(v ) instead of d(v )
as well.
7
1
1
1
Recall δ only has to satisfy
∈
·
,
.
2δ(v )
9 2d(v ) 2d(v )
Now choose a prime number p with 9n 6 p 6 18n — such a prime
exists by Betrand’s postulat. We may identify V with a subset of
Fp = {0, . . . , p − 1}.
1
2d(v )
form
−
av
9n
7
9
·
h
i
1
1
2d(v ) , 2d(v ) has size
1
1
1
1
2d(v ) = 9d(v ) > 9n > p , thus
The interval
7
9
·
there exists a number of the
in this interval for some av ∈ N. We can set
1
2δ(v )
=
av
p .
Determine a subset Av ⊆ Fp with |Av | = av , where
7
av
1
1
1
9 · 2d(v ) 6 p = 2δ(v ) 6 2d(v )
354 / 400
Luby’s Algorithm
Now choose (x, y ) ∈ Fp × Fp randomly (using O(log(n)) many
random bits) and put v into S if and only if x + vy ∈ Av .
Since for every y , z ∈ Fp there is exactly one x ∈ Fp with x + vy = z,
namely x = z − vy , we have
Pr(v ∈ S) =
=
=
1
|{(x, y ) | x + vy ∈ Av }|
p2
1 X
|{(x, y ) | x + vy = z}|
p2
z∈Av
1 X
p
p2
z∈Av
=
av
p
355 / 400
Luby’s Algorithm
Finally, we have to show pairwise independence: Let u 6= v be two
different nodes. Then
Pr(u ∈ S ∧ v ∈ S) =
=
1
p2
X
(zu ,zv )∈Au ×Av
1
|{(x, y ) | x + uy ∈ Au ∧ x + vy ∈ Av }|
p2
x
zu
(x, y ) | 1 u
=
1 v
y
zv
356 / 400
Luby’s Algorithm
The matrix has an inverse (the determinant is v − u 6= 0), thus for
every (zu , zv ) there is exactly one (x, y ) ∈ Fp × Fp for
zu
x
1 u
=
y
zv
1 v
We obtain
Pr(u ∈ S ∧ v ∈ S) =
1
au av
au av =
= Pr(u ∈ S) · Pr(v ∈ S).
2
p
p p
We have shown pairwise independence.
357 / 400
18./19. Vorlesung, 17.01.12
Thema: Pattern matching using fingerprints
358 / 400
Pattern matching using fingerprints
Let T = a1 a2 · · · an be a text and P = b1 b2 · · · bm be a pattern
(ai , bj ∈ Σ for some finite alphabet Σ), m 6 n.
Goal:Find all occurrences of P in T , i.e., all positions
1 6 i 6 n − m + 1 such that T [i , i + m − 1] := ai ai +1 · · · ai +m−1 = P.
The algorithm of Knuth, Morris and Pratt achieves this in sequential
time O(m + n) = O(n).
Pattern Matching is AC0 ,
but then we need (n + m)3 gates (processors).
Here we want to develop a simple randomized parallel algorithm with
n processors and time O(log n).
359 / 400
Σ∗ ⊆ SL2(Z)
We assume in the following that Σ = {0, 1}.
Define f : Σ → Z2×2 by
1 0
1 1
f (0) =
f (1) =
1 1
0 1
We extend f to a homomorphism f : Σ∗ → SL2 (Z)
a1 a2 a ∈ Z, a1 a4 − a2 a3 = 1
SL2 (Z) =
a3 a4 i
360 / 400
Σ∗ ⊆ SL2(Z)
Lemma 9.1
1.) The homomorphism f is injective.
2.)
∗
f (Σ ) =
a1 a2
a3 a4
ai ∈ N, a1 a4 − a2 a3 = 1
a1 a2
and |w | = ℓ,
a3 a4
then ai 6 Fℓ+1 , where Fℓ is the ℓ-th Fibonacci number.
3.) If f (w ) =
F0 = 0, F1 = 1, Fn+1 = Fn + Fn−1
361 / 400
Σ∗ = SL2(N)
Proof: Obviously,
a1 a2
∗
f (Σ ) ⊆
a3 a4
and
ai ∈ N, a1 a4 − a2 a3 = 1 =: SL2 (N)
1 0
a1 a2
a1
a2
=
1 1
a3 a4
a1 + a3 a2 + a4
1 1
a1 + a3 a2 + a4
a1 a2
=
a3
a4
0 1
a3 a4
a1 a2
∈ SL2 (N) and a1 > a3 and a2 < a4 . Then
Assume
a3 a4
1 = a1 a4 − a2 a3 > (a3 + 1)(a2 + 1) − a2 a3 = a2 + a3 + 1. Thus,
a1 = a4 = 1, a2 = a3 = 0.
362 / 400
Σ∗ = SL2(N)
This gives a unique decoding and shows:
a1 a2 ∗
a
∈
N,
a
a
−
a
a
=
1
Σ =
1 4
2 3
a3 a4 i
363 / 400
Size is bounded by Fibonacci numbers
The statement concerning the Fibonacci numbers follows easily by
induction on |w |.
1 0
a1 a2
a1
a2
=
1 1
a3 a4
a1 + a3 a2 + a4
and
1 1
a1 a2
a1 + a3 a2 + a4
=
0 1
a3 a4
a3
a4
Invariant:
1.) min {a1 , a3 } 6 Fℓ
2.) max {a1 , a3 } 6 Fℓ+1
364 / 400
Pattern matching using fingerprints
First idea for pattern matching: Compare f (P) with f (T [i , i + m − 1])
for all 1 6 i 6 n − m + 1.
Problem: f (P) may have entries of size Fm+1 which need O(m) many
bits. Thus, comparison of f (P) with f (T [i , i + m − 1]) needs time
O(m), and we gain nothing against directly comparing P and
T [i , i + m − 1].
Solution: Calculate modulo sufficiently large prime numbers.
For a word w ∈ {0, 1}∗ and a prime number p let
a1 a2
a1 mod p a2 mod p
, where f (w ) =
fp (w ) =
a3 mod p a4 mod p
a3 a4
The matrix fp (w ) is called a fingerprint of the string w (with respect
to the prime p).
365 / 400
Pattern matching using fingerprints
Let p be prime and let X , Y ∈ {0, 1}∗ be two strings.
Then p leads to a false match for (X , Y ), if X 6= Y (i.e.,
f (X ) 6= f (Y ) but fp (X ) = fp (Y )
For any integer k, π(k) denotes the number of primes in the interval
{1, . . . , k}.
6 π(k) 6 3 logk(k)
◮
k
log2 (k)
◮
This is a weak form of Prime Number Theorem.
◮
The product over the primes smaller than k is larger than 2k , if k
is sufficiently large (k > 29).
◮
We use a slightly
weaker statement, where 2k is replaced by ϕk
√
with ϕ = 1+2 5 .
2
366 / 400
Pattern matching using fingerprints
Lemma 9.2
For 1 6 i 6 t let (Xi , Yi ) be a pair of strings of length m. Then a
randomly chosen prime p from the interval {1, . . . , M} leads to a false
match for some of the pairs (Xi , Yi ) with probability at most π(5·m·t)
π(M)
if m is sufficiently large.
367 / 400
Pattern matching using fingerprints
Proof: For 1 6 i 6 t let
bi ,1 bi ,2
ai ,1 ai ,2
.
and f (Yi ) =
f (Xi ) =
bi ,3 bi ,4
ai ,3 ai ,4
∃1 6 i 6 t : p leads to false match for (Xi , Yi )
⇐⇒
∃1 6 i 6 t : f (Xi ) 6= f (Yi ) and fp (Xi ) = fp (Yi )
=⇒
Q p divides the product
{|ai ,j − bi ,j | | 1 6 i 6 t, 1 6 j 6 4, ai ,j 6= bi ,j }.
4t
=⇒ p divides a number u 6 Fm+1
< ϕ4t(m+1)
Recall that Fm+1 6 ϕm+1 where ϕ =
√
1+ 5
2 .
368 / 400
Pattern matching using fingerprints
Since the number of distinct prime divisors of ϕ4t(m+1) is at most
π(4t(m + 1))) 6 π(5 · m · t) (if m is sufficiently large), the probability
for a false match is at most
π(5 · m · t)
π(M)
369 / 400
Pattern matching using fingerprints
Lemma 9.3
For 1 6 i 6 t let (Xi , Yi ) be a pair of strings of length m. Let
M = mt k for some constant k ∈ R with k > 1. Then a randomly
chosen prime p from the interval {1, . . . , M} leads to a false match
for some of the pairs (Xi , Yi ) with probability at most t15k
k−1 if m is
sufficiently large.
Proof: We have π(m · t) 6
15m·t
log2 (mt)
and
π(M) = π(mt k ) >
mt k
log2 (mt k )
Thus,
Pr(false match) 6
15mt log2 (mt k )
15mtk log2 (mt)
15k
6
= k−1
log2 (mt) · m · t k
log2 (mt) · m · t k
t
370 / 400
Pattern matching using fingerprints
For k = 2. Then for t = 106 = 1 Million the probability of a false
match is less than
15k
6 3/100000.
t
For this we need a prime with less than 64 bits
371 / 400
Pattern matching using fingerprints
Recall that T = a1 a2 · · · an is the text and P = b1 b2 · · · bm is the
pattern.
For a given prime p ∈ nO(1) we can calculate the fingerprints
fp (T [i , i + m − 1]) of all substrings of T of length m in time
O(log(n)) using n processors:
For this we calculate all products
Ri = fp (T [1, i ]) = fp (a1 )fp (a2 ) · · · fp (ai ) and
Ri−1 = fp (ai )−1 fp (ai −1 )−1 · · · fp (a1 )−1 (1 6 i 6 n) in time O(log(n))
with n processors using the prefix-sum algorithm.
Note: Since we calculate modulo a prime of size nO(1) all integers that
occur in the calculation have only O(log(n)) many bits.
Finally, calculate fp (T [i , i + m − 1]) = Ri−1
−1 · Ri +m−1 in time O(1)
with n processors.
372 / 400
Pattern matching using fingerprints
Theorem 9.4
For every constant k, there exists a randomized algorithm that runs in
time O(log(n)) with O(n) processors and that calculates an array
MATCH[1, . . . , n] such that:
◮
◮
If T [i , i + m − 1] = P, then MATCH[i ] = 1 with probability 1.
If T [i , i + m − 1] 6=
P, then MATCH[i ] = 1 with probability
bounded by O n1k .
373 / 400
Pattern matching using fingerprints
Proof:
1. Let M = m · nk+1 6 nk+2 .
2. Choose randomly a prime p ∈ {1, . . . , M}.
3. Compute fp (P) in time O(log(m)) using m 6 n processors.
4. For 1 6 i 6 n − m + 1 in parallel, compute
Li := fp (T [i , i + m − 1]) using the algorithm from the previous
slide.
5. For 1 6 i 6 n − m + 1 in parallel, set MATCH[i ] = 1 if
Li = fp (P), otherwise set MATCH[i ] = 0.
By the lemma abbve, the probability
that some entry MATCH[i ] is
not correct is bounded by O n1k .
Moreover, every 0 entry is correct!
374 / 400
20. Vorlesung, 24.01.12
Thema: Splay Trees Data structure for binary search.
375 / 400
Splay Trees
The notion of a Splay Tree is due to Turing award winner Tarjan. The
laudatio mentions splay trees as one of his great contributions.
A search tree for a totally ordered set (X , ≺) is a binary (not
necessarily balanced) rooted tree with node set X such that:
For every node i with left (resp. right) subtree Tℓ (resp. Tr ):
∀x ∈ Tℓ : x ≺ i and ∀y ∈ Tr : i ≺ y .
left-child(i ) (resp. right-child(i )) denotes the left (resp. right) child of
a node (might be nil).
A splay tree is a binary search tree that supports the following
operations, where S, S ′ are search trees and i ∈ X :
◮ member(i , S)
◮ insert(i , S)
◮ delete(i , S)
◮ join(S, S ′ ), if ∀x ∈ S ∀y ∈ S ′ : x ≺ y
376 / 400
Splay Trees
These operations will be implemented using a single additional
operation: splay(i , S)
splay(i , S) reorganizes the tree (nodes are not removed or added).
After splay(i , S) the tree has the form
S1
S2
where ∀x ∈ S1 : x ≺ i ∧ ∀y ∈ S2 : i ≺ y
If i ∈ S, then i is the root after splay(i , S). If i 6∈ S, then after
splay(i , S) the root is min{k ∈ S | k > i } or max{k ∈ S | k < i }.
377 / 400
Splay Trees
function member(i , S) : boolean
splay(i , S);
return i = root(S);
endfunction
?
i = root
S1
S2
378 / 400
Splay Trees
procedure insert(i , S)
splay(i , S); j :=root(S);
if i < j then
left-child(i ) := left-child(j);
right-child(i ) := j;
left-child(j) := nil;
elsif i > j then
right-child(i ) := right-child(j);
left-child(i ) := j;
right-child(j) := nil;
endif
root(S) := i
endprocedure
379 / 400
Splay Trees
i
j
Sℓ
Sr
i
j
Sr
Sℓ
380 / 400
Splay Trees
procedure join(S, S ′ )
splay(+∞, S);
(∗ after this splay-operation, the right subtree of S is empty ∗)
right-child(root(S)) := root(S ′ );
(∗ make S ′ the right subtree of S ∗)
endprocedure
381 / 400
Splay Trees
procedure delete(i , S)
if member(i , S) then
(∗ now, i must be the root of S ∗)
join(left subtree of S, right subtree of S);
endif
endprocedure
382 / 400
Splay Trees
splay(i , S) will be implemented using elementary rotate-operations:
rotate(x):
rotate(x)
−−−−−→
y
x
A
C
B
x
y
A
B
C
383 / 400
Splay Trees
splay(i , S) will be implemented using elementary rotate-operations:
rotate(y ):
rotate(y )
←−−−−−
y
x
A
C
B
x
y
A
B
C
384 / 400
Splay Trees
Case: par(y ) does not exist
y
x
A
x
C
B
y
A
B
C
385 / 400
Splay Trees
Case: x is left child of y = par(x), y is left child of z = par(y )
y
z
y
x
A
x
D
C
A
z
B
C
D
B
386 / 400
Splay Trees
Case: x is left child of y = par(x), y is left child of z = par(y ) (cont.)
y
x
x
A
z
B
C
y
A
D
z
B
C
D
387 / 400
Splay Trees
Case: x = is left child of y = par(x), y is right child of z = par(y )
z
z
y
A
x
B
D
C
x
A
y
B
C
D
388 / 400
Splay Trees
Case: x is left child of y = par(x), y is right child of z = par(y )
(cont.)
z
x
x
A
y
B
C
y
z
A
B
C
D
D
389 / 400
Splay Trees
procedure splay(i , S)
x := root(S);
while x 6= i ∧ x 6= nil do
y := x;
if i < x then x := left-child(x) else x := right-child(x) endif
endwhile
if x = nil then x := y endif
while x 6= root do
y := par(x);
if par(y ) does not exist then rotate(x)
elsif x = left-child(y ) and y = left-child(par(y )) or
x = right-child(y ) and y = right-child(par(y )) then
rotate(y ); rotate(x)
elsif x = left-child(y ) and y = right-child(par(y )) or
x = right-child(y ) and y = left-child(par(y ))
rotate(x); rotate(x)
endprocedure
390 / 400
Splay Trees
Using an amortized analysis, we show that a sequence of m operations
on splay trees with at most n elements requires time O(m log(n)).
For a node x in a splay tree let S(x) be the subtree rooted at x.
Let µ(x) = ⌊log2 |S(x)|⌋ 6 log(n) be the potential of node x.
Idea: For each node x we have an account, where we can pay in and
pay out $.
For each low level operation (e.g. a pointer manipulation or a
comparison), we have to pay one $. (Recall — O-notation)
Credit invariant: Each node x has at least µ(x) $ on his account.
391 / 400
Splay Trees
Lemma 10.1
i.) Let x ∈ S, then for splay(x, S) with x ∈ S we have to pay in total
at most
3(µ(root(S)) − µ(x)) + 1 ∈ O(log(n))$
in order to maintain the credit invariant.
ii.) If x 6∈ S, then x can be replaced by the element that is rotated to
the top in order to compute the costs for splay(x, S).
392 / 400
Splay Trees
Proof: ii) is clear.
i.): We have to show:
splay(x, S) with x ∈ S costs at most
3(µ(root(S)) − µ(x)) + 1$.
Let x be the node that is rotated (either one or two levels in direction
to the root).
Let µ (resp. µ′ ) be the potential function before (resp. after) that
iteration of the second while loop.
Strategy: We will show that in the last iteration of the while loop it
suffices to pay 3(µ′ (x) − µ(x)) + 1 $, whereas for every other iteration
it suffices to pay 3(µ′ (x) − µ(x)) $ (→ telescoping sum).
393 / 400
Splay Trees
Let y = par(x).
Case 1: par(y ) does not exist (last rotate in the splay-operation).
µ′ (x) = µ(y ) and
µ′ (y ) 6 µ′ (x)
In order to maintain the credit invariant, we need to spend:
µ′ (x) + µ′ (y ) − µ(x) − µ(y ) = µ′ (y ) − µ(x) 6 µ′ (x) − µ(x)
We are generous and pay 3(µ′ (x) − µ(x)) + 1 $ for this and the
pointer manipulations.
394 / 400
Splay Trees
Case 2: z = par(y ) exists, and x and y are both left children of their
parent nodes (analogously for “right”).
µ′ (x) = µ(z),
µ′ (z) 6 µ′ (y ) 6 µ′ (x),
and µ(x) 6 µ(y )
In order to maintain the credit invariant, we need to spend:
∆ := µ′ (x) + µ′ (y ) + µ′ (z) − µ(x) − µ(y ) − µ(z)
= µ′ (y ) + µ′ (z) − µ(x) − µ(y )
= (µ′ (y ) − µ(x)) + (µ′ (z) − µ(y ))
6 (µ′ (x) − µ(x)) + (µ′ (x) − µ(x)) = 2(µ′ (x) − µ(x))
Claim: If µ′ (x) = µ(x), then ∆ < 0.
395 / 400
Splay Trees
Assume that µ′ (x) = µ(x) and ∆ > 0.
µ(x) = µ′ (x) = µ(z) and µ(x) 6 µ(y ) 6 µ(z) implies
µ(x) = µ(y ) = µ(z).
Thus,
µ′ (x) + µ′ (y ) + µ′ (z) > µ(x) + µ(y ) + µ(z)
(∆ > 0)
′
= 3µ(z) = 3µ (x)
i.e., µ′ (y ) + µ′ (z) > 2µ′ (x).
Because µ′ (z) 6 µ′ (y ) 6 µ′ (x), it follows µ′ (x) = µ′ (y ) = µ′ (z).
Since µ′ (x) = µ(z) we have
µ(x) = µ(y ) = µ(z) = µ′ (x) = µ′ (y ) = µ′ (z).
396 / 400
Splay Trees
Let a be the size of the subtree rooted at x before the iteration of the
second while loop.
Let b be the size of the subtree rooted at z after the iteration of the
second while loop.
Then µ(x) = µ(y ) = µ(z) = µ′ (x) = µ′ (y ) = µ′ (z) implies
⌊log a⌋ = ⌊log(a + b + 1)⌋ = ⌊log b⌋
Assume w.l.o.g. b > a. Then
⌊log(a + b + 1)⌋ > ⌊log(2a)⌋ = 1 + ⌊log a⌋ > ⌊log a⌋
a contradiction.
This proves the claim µ′ (x) = µ(x) ⇒ ∆ < 0.
397 / 400
Splay Trees
Recall that we have to pay ∆ 6 2(µ′ (x) − µ(x)) $ in order to
maintain the credit invariant.
Thus, either
◮
µ′ (x) > µ(x) and we can use additional µ′ (x) − µ(x) > 1 $
(note: µ(x), µ′ (x) ∈ N) in order to pay for the pointer
manipulations, or
◮
∆ < 0, i.e., ∆ 6 −1, and we can take off from the credit
accounts −∆ > 1 $ in order to pay for the pointer manipulations.
Thus, 3(µ′ (x) − µ(x)) $ suffice in any case.
398 / 400
Splay Trees
Case 3: z = par(y ) exists, x is the left children of y , and y is the right
children of z.
Analogously to case 2, we can show that again 3(µ′ (x) − µ(x)) $
suffice in order to maintain the credit invariant and to perform the all
pointer operations.
Summary: In case 2 or 3 we need 3(µ′ (x) − µ(x)) $, in case 1 (last
rotation) we need 3(µ′ (x) − µ(x)) + 1 $.
By summing up (telescoping sum), it follows that the whole
splay(x, S)-operation needs 3(µ(root(S)) − µ(x)) + 1 ∈ O(log(n)) $.
Remark: For the operations in the first while loop of splay (searching
for the element that is rotated to the root) we pay, when we move the
path up to the root in the second while loop.
399 / 400
Splay Trees
Theorem 10.2
A sequence of m operations on n elements, stored in a splay tree,
requires time O(m log(n)).
Proof: The total execution time is bounded by the amount of $ we
have to pay.
◮
member(i , S): O(log(n)) $ for the splay-operation plus O(1) $
for the test, whether the root is i .
◮
insert(i , S): O(log(n)) $ for the splay-operation plus O(1) $ for
the pointer operations plus log(n) $ that are paid in the new
credit account for i .
◮
delete(i , S), join(i , S): similar
400 / 400
Herunterladen