uebung10 - uebung10

Werbung
Datenstrukturen und
Algorithmen SS07
Datum: 4.6.2007
Michael Belfrage
[email protected]
belfrage.net/eth
Programm von Heute
• Minimaler Spannbaum (MST)
• Challenge der Woche
• Fibonacci Heap
Minimaler Spannbaum
• Einführendes Beispiel:
1’100 Fr.
1’200
-
Fr.
r.
0F
20
40
0F
r.
50
0F
.
Fr
Fr.
0
0
7
r.
0
00
1’
1700 F
r.
40
0F
r.
900 Fr.
900 Fr.
800
Fr.
Es hat eine Anzahl Häuser, und man möchte diese alle miteinander vernetzen.
(z.B. wegen einem LAN, elektrische Leitungen, etc.) Die Möglichen Verbindungen zwischen zwei Häusern ist oben abgebildet, zusammen mit den Kosten in
[Fr.]. Bestimme die Menge von direkten Verbindungen, sodass alle miteinander direkt oder indirekt - verbunden sind, und die Kosten minimal sind.
Minimaler Spannbaum
• Diese Menge mit minimalen Kosten
im vorherigen Beispiel kann man
effizient bestimmen mithilfe Ideen
aus der Graphentheorie:
– Insbesondere mithilfe des minimalen
Spannbaums
• Es folgen einige Definitionen …
Spannbaum
• Definitionen:
– Ein Spannbaum T eines ungerichteten
und zusammenhängenden Graphen G,
ist ein Baum, welcher aus allen Knoten
und einigen (oder auch allen) Kanten
von G besteht.
– Ein minimaler Spannbaum (engl. MST =
Minimal Spanning Tree) ist ein Spannbaum,
dessen summierte Kantengewichte
kleiner oder gleich deren aller anderen
Spannbäume ist.
Spannbaum
• Informell:
– Ein Spannbaum eines Graphen ensteht, wenn
man einige Kanten auswählt, sodass man von
jedem Knoten aus alle anderen erreichen
kann. Es soll jedoch nicht möglich sein im
Kreis zu laufen.
• Beispiel:
E
B
F
A
C
D
G
C
Spannbaum
• Ein Graph kann mehrere Spannbäume haben, der
folgende hat z.B. 16 verschiedene:
Minimaler Spannbaum
• MST
– Ist der Graph gewichtet, kann man einen
minimalen Spannbaum bestimmen. Die Summe
der Kantengewichte soll dann minimal sein
über alle möglichen Spannbäume
• Beispiel:
11
9
B
2
F
7
17
C
4
D
12
4
10
A
E
9
5
8
G
C
Minimaler Spannbaum
• Schnellste Methoden zur Bestimmung
eines minimalen Spannbaums:
– Randomisierter Algorithmus
• Karger, Klein, Tarjan [1995]
• Erwartet lineare Laufzeit
– Falls Gewichte kleine natürliche Zahlen
• Fredman, Willard [1990]
• lineare Laufzeit
– Sonst existieren bisher nur Algorithmen die
fast linear sind:
• Chazelle [1999]
• O(|E| · a(|E|, |V |)) (a
ist die inverse Ackermann-Funktion)
Minimaler Spannbaum
• Oft sind diese „schnellen“ Algorithmen aber sehr
aufwändig zu implementieren und lohnen sich
erst dann, wenn die Laufzeit ein Problem wird.
• Die klassischen Algorithmen sind im Vergleich
um einiges einfacher zu verstehen und zu
implementieren. (Hinzu kommt, dass viele der
modernen Algorithmen auf Ideen der klassischen
beruhen)
Minimaler Spannbaum
• Klassische Methoden zur Bestimmung
eines minimalen Spannbaums:
– Boruvka‘s Algorithmus
• Otakar Borůvka, 1926
– Prim‘s Algorithmus
• Vojtěch Jarník [1926], Robert C. Prim [1957]
• Wiederentdeckt von Edsger W. Dijkstra [1959]
– Kruskal‘s Algorithmus
• Joseph Kruskal [1956]
Minimaler Spannbaum
• Eine naive Methode wäre z.B. Alle Spannbäume
zu bestimmen, dann würde man sicher auch den
minimalen finden.
• Jedoch hat es meist sehr
10
16
17
14
viele Spannbäume. Diese
Methode ist darum wohl
19
12
16
9
sehr ineffizient.
5
2
1
8
15
6
20
15
18
12
11
14
9
3
Kruskal‘s Algorithmus
• Um das Problem effizient von Hand zu
lösen, eignet sich Kruskal‘s Algorithmus:
INPUT: gewichteter Graph G=(V,E,w)
OUTPUT: Minimaler Spannbaum T=(V,E‘)
Algorithmus (vereinfacht):
T := G \ E
sortiere E {aufsteigend nach Gewicht w}
for all e=(u,v) in E do
if {es gibt keinen Weg von u nach v} then
e einfügen in T
end
Nebenbei: Kruskal‘s Algorithmus ist ein Greedy
end
Algorithmus, d.h. dass er jeweils die beste lokale
return T
Auswahl trifft in der Hoffnung das globale Optimum zu erreichen. Oft sind Greedy Algorithmen
jedoch falsch, also aufpassen!!
Kruskal‘s Algorithmus
• Beispiel:
11
9
B
2
F
7
17
C
4
D
12
4
10
A
E
9
5
8
G
C
Hier werden z.B. nacheinander eingefügt:
(A, D), (B, D), (C, E), (F, G), (C, F ), (C, D)
Kruskal‘s Algorithmus
•
Wieso ist dieser Ansatz korrekt?
–
Nehme an Kantengewichte seien versch.
•
•
–
–
Lemma: Wenn wir die Menge der Knoten V in zwei disjunkte Teilmengen V1 und V2 aufteilen, so ist die
kleinste Kante e welche V1 und V2 miteinander verbindet Teil des minimalen Spannbaums.
Beweis: Nehme an e sei nicht Teil des Spannbaums. V1 und V2 müssen durch eine Kante verbunden sein.
Falls dies nicht e ist, dann kann diese Kante f nur noch schwerer sein, womit der Spannbaum schwerer
wird. Widerspruch.
In jedem Schritt fügen wir immer die kleinste Kante hinzu welche zwei disjunkte Teilmengen
V1 und V2 verbindet. Nach obigem Lemma sind diese Kanten also jeweils Teil des minimalen
Spannbaumes. qed.
Will man auch gleiche Kantengewichte zulassen, wird der Beweis einwenig mühsamer, aber
die Idee bleibt dieselbe.
V1 = {A, B, D}
11
14
2
E
B
F
7
17
C
4
D
12
5
10
A
V2 = {C, E, F, G}
9
e
6
8
G
C
Kruskal‘s Algorithmus
• Laufzeit
O(|E| log |V |),
da |V | − 1 ≤ |E| ≤ |V |(|V | + 1)/2
Algorithmus:
T := G \ E
sortiere E {aufsteigend nach Gewicht w}
for all e=(u,v) in E do
if {es gibt keinen Weg von u nach v} then
e einfügen in T
end
end
union-find Datenstrukturen können dies
return T
meist in konstanter Zeit beantworten
(naiver Ansatz würde O(|V | · |E|) benötigen)
• Fazit:
O(|E| log |V |)
Prim‘s Algorithmus
• Prim baut den minimalen Spannbaum
auf, indem er einen Knoten nach dem
anderen einfügt.
INPUT: gewichteter Graph G=(V,E,w)
OUTPUT: Minimaler Spannbaum T=(V,E‘)
Algorithmus:
T := ein bel. Knoten von G
while {T weniger als |V| Knoten hat} do
e := kleinste Kante welche T mit G\T verbindet
e einfügen in T
end
return T
Prim‘s Algorithmus
• Beispiel:
11
9
B
2
F
7
17
C
4
D
12
4
10
A
E
9
5
8
G
C
Hier werden z.B. nacheinander eingefügt:
(A, D), (D, B), (D, C), (C, E), (C, F ), (G, F )
Prim‘s Algorithmus
• Korrektheit folgt aus dem gleichen Lemma wie
schon bei Kruskal.
• Langsamster Teil des Algorithmus ist dieser
Algorithmus:
T := ein bel. Knoten von G
while {T weniger als |V| Knoten hat} do
e := kleinste Kante welche T mit G\T verbindet
e einfügen in T
end
return T
• Wir brauchen also eine Datenstruktur, die effizient
das Minimum bestimmen/löschen kann
• Eine solche kennengelernte Datenstruktur ist ein
Heap. Besonders effzient ist ein Fibonacci-Heap.
•
Siehe auch http://de.wikipedia.org/wiki/Algorithmus_von_Prim für eine
mögliche Heap-Implementation.
Prim‘s Algorithmus
• Laufzeiten
(ohne Begründung):
– Mit Adjazenzmatrix
2
O(|V | )
– Mit Adjazenzliste und (normalem) Heap
O((|V | + |E|) log |V |)
– Mit Adjazenzliste und Fibonacci-Heap
O(|E| + |V | log |V |)
Boruvkas‘s Algorithmus
• Ähnlich wie Prim, einfach ohne komplizierte Datenstruktur.
• Baue MST auf indem man mehrere Bäume vereinigt, bis nur
noch ein grosser vorhanden ist.
INPUT: gewichteter Graph G=(V,E,w)
OUTPUT: Minimaler Spannbaum T=(V,E‘)
Algorithmus:
T := {} //MST
L := {} //Liste von Bäumen
Teile G auf in |V| Teilbäume bestehend aus einem einzigen Knoten und
füge diese in L hinein.
while {L mehr als einen Baum hat} do
for all Bäume S in L do
Finde die kleinste Kante e welche S mit G\S verbindet.
Füge e in T ein.
end
Verschmelze verbundene Bäume und aktualisiere L.
end
return T
Boruvka‘s Algorithmus
• Beispiel:
11
9
B
2
F
7
17
C
4
D
12
4
10
A
E
9
5
8
G
C
L := {{A}, {B}, {C}, {D}, {E}, {F }}
Boruvka‘s Algorithmus
• Beispiel:
11
9
B
2
F
7
17
C
4
D
12
4
10
A
E
9
5
8
G
C
L := {{A, B, D}, {C, E}, {F, G}}
Boruvka‘s Algorithmus
• Beispiel:
11
9
B
2
F
7
17
C
4
D
12
4
10
A
E
9
5
8
G
C
L := {{A, B, C, D, E, F, G}}
Boruvka‘s Algorithmus
• Korrektheit folgt wiederum aus dem
gleichen Lemma wie schon bei Kruskal
und Prim.
• Laufzeit:
O(|E| log |V |)
• Begründung
– Anzahl Durchgänge O(log |V |) , da Bäume
immer mind. doppelt so gross werden.
– In jedem Durchgang jeweils alle Knoten und
alle Kanten ansehen: O(|V | + |E|) = O(|E|) hier,
da |V | − 1 ≤ |E| ≤ |V |(|V | + 1)/2
Maximalen Spannbaum
• Jetzt seit ihr gefragt:
– Wie konstruiert man einen maximalen
Spannbaum?
– Aufgabe 10.3a
• Durch Modifikation von Kruskal
– Aufgabe 10.3c
• Durch Modifikation des Graphen
Uralte Challenge
• Zu zeigen:
– Wenn n die Anzahl Wände sind im
Museum, dann braucht es höchstens
n/3 [abgerundet] Wachen.
Alte Challenge der Woche
• Ein sehr beliebtes Problem...
• Schreibt ein Programm in Eiffel, welches
seinen eigenen Quellcode ausgibt, i.e. mit
io.put_string(“...“)
• Dabei ist nicht eine Datei schon irgendwo
abgespeichert auf der man zugreifen kann
• Brauchen kann man (ohne zu modifizieren) alle Klassen der Eiffel-Base
• Der Quellcode kann irgendetwas sein,
Hauptsache das Programm gibt ihn aus
• Und ja... keinen leeren Quellcode ;)
The Challenge
• Gegeben:
– Einen Zeiger zu einem mittleren
Knoten einer einfach verketten Liste
• Aufgabe:
– Lösche diesen Knoten
a
b
a
b
c
d
e
f
d
e
f
Fibonacci-Heap
• Fredman, Tarjan [1984]
• amort. konstante Laufzeit
für praktisch alle Operationen bis auf deleteMin (i.e. das
Minimum löschen)
Fibonacci-Heap
• Ein Fibonacci-Heap ist eine Sammlung von
Bäumen, wobei der Schlüssel eines Kindes immer
grösser oder gleich dem Elternschlüssel ist.
• Somit ist das Minimum die Wurzel irgend eines
dieser Bäume.
7
1
2
10
9
15
6
4
7
9
5
14
17
9
Fibonacci-Heap
• Der Name Fibonacci kommt daher, da die
Grösse eines Teilbaums mit Wurzel vom Grad k
mindestens Fk+2 beträgt (wobei Fk die k-te Fibonacci Zahl ist)
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584,
4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811…
Fibonacci-Heap
• Bäume vom Grad 1 enstehen aus zwei
Bäumen vom Grad 0:
4
2
2
4
Fibonacci-Heap
• Bäume vom Grad 2 enstehen aus zwei
Bäumen vom Grad 1:
2
3
4
8
2
3
8
4
Fibonacci-Heap
• Bäume vom Grad 3 enstehen aus zwei
Bäumen vom Grad 2:
4
2
3
8
4
2
11
8
8
11
4
3
7
8
4
7
Fibonacci-Heap
• Baum vom Grad 5:
2
5
3
12
18
21
14
13
17
18
14
28
29
24
23
27
28
14
18
24
14
13
17
18
14
8
11
4
3
7
8
4
Fibonacci-Heap
• Minimum bestimmen:
– Minimum Zeiger zeigt immer darauf.
• Laufzeit somit konstant
7
10
6
4
9
7
9
15
1
2
5
14
17
9
4
Fibonacci-Heap
• Einfügen:
– Neuen Knoten in die Wurzelliste
einfügen. (Minimum Zeiger ändern falls nötig)
7
7
10
6
4
9
7
9
15
1
2
5
14
17
9
4
Fibonacci-Heap
• Minimum entfernen (3 Phasen)
1. die Kinder des minimalen Knotens abschneiden und
zur Wurzelliste hinzufügen. Danach minimalen Knoten
entfernen.
7
10
6
4
9
7
9
15
1
2
5
14
17
9
4
Fibonacci-Heap
• Minimum entfernen (3 Phasen)
2. Verbinde Wurzeln vom selben Grad. (mithilfe eines
Arrays, wo wir jeweils von links nach rechts die Wurzelknoten durchgehen, und bei
einem leeren k-ten Eintrag (falls der aktuelle Wurzelknoten Grad k hat) einen Zeiger machen
zum aktuellen Wurzelknoten, und bei einem vorhandenen Zeiger diesem folgen und die
Wurzeln verbinden.)
7
2
10
6
4
9
7
9
15
5
4
14
17
9
Fibonacci-Heap
• Minimum entfernen (3 Phasen)
3. Suche das neue Minimum unter den Wurzelknoten
- maximal O(log(n)) Wurzelknoten
9
15
6
4
7
9
2
4
5
9
7
14
17
10
Fibonacci-Heap
• Einen Schlüssel verkleinern
– Verkleinere den Schlüssel des Knotens zuerst.
– Ist der neue Schlüssel grösser/gleich dem Schlüssel des
Elternknoten, dann muss nichts mehr gemacht werden.
9
15
6
4
7
9
2
4
5
9
7
14
17
8
10
Fibonacci-Heap
• Einen Schlüssel verkleinern
– Verkleinere den Schlüssel des Knotens zuerst.
– Ist der neue Schlüssel hingegen kleiner als der Schlüssel
des Elternknoten, dann schneidet man den Knoten ab
und markiert den Elternknoten. Ist dieser auch schon
markiert, schneidet man diesen auch ab. (dies geht so weiter bis
man zur Wurzel kommt oder einen unmarkierten Knoten erreicht. Jeweils immer minimum
Aktualiseren falls nötig)
9
15
6
4
7
9
2
4
5
9
7
14
17
3
10
Fibonacci-Heap
• Einen Schlüssel löschen
– Verkleinere den Schlüssel zuerst auf – ∞
– Lösche nun das Minimum
6
4
7
9
-∞
9
15
2
4
5
9
7
14
17
10
Fibonacci-Heap
• Frage an euch:
– Aufgabe 10.4b
• Warum können nicht alle Heap-Operationen
eine konstante amortisierte Laufzeit haben
(wenn man nur Vergleichsbasierte Operationen zulässt)?
Fibonacci-Heap
• Frage an euch:
– Aufgabe 10.4a
• Kann ein Fibonacci-Heap zu einer Struktur
degenerieren, die genau noch einen Knoten
in der Wurzelliste hat, an dem eine lineare
Liste hängt?
4
9
11
13
15
Ende der Stunde.
Questions?
Herunterladen