Effiziente Algorithmen - Greedy-Algorithmen für

Werbung
Effiziente Algorithmen
Greedy-Algorithmen für Graphprobleme
Vorlesender: Martin Aumüller
(nach Folien von Prof. Martin Dietzfelbinger)
Mai/Juni 2012
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
1
Dijkstra-Algorithmus
Allgemeines
4.1 Kürzeste Wege mit einem Startknoten:
Der Algorithmus von Dijkstra
Definition 4.1.1
1. Ein gewichteter Digraph ist ein Tripel G = (V , E , c), wo (V , E ) ein
Digraph und c : E → R+
0 ist.
c steht für cost“; c(v , w ) kann als Kosten“ oder Länge“ der
”
”
”
Kante (v , w ) interpretiert werden.
2. Ein gerichteter Weg p = (v0 , v1 , . . . , vk ) in G hat
Kosten/Länge
k
X
c(p) =
c(vi−1 , vi ).
i=1
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
2
Dijkstra-Algorithmus
Allgemeines
Definition 4.1.1 (Fortsetzung)
3. Der (gerichtete) Abstand von v , w ∈ V ist
d(v , w ) := min{c(p) | p Weg von v nach w }
(= ∞, falls kein Weg von v nach w existiert).
Klar: d(v , v ) = 0. (Weg der Länge 0.)
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
3
Dijkstra-Algorithmus
Allgemeines
Beispiel:
s
2
h
1
3
3
a
1
k
3
3
b
1
2
1
l
2
c((s, a, b, c)) = 6,
d(s, e) = ∞.
FG KTuEA, TU Ilmenau
c((s, a, k, b, c)) = 5,
c
1
5
d
2
1
g
2
f
2
3
e
d(s, c) = 5,
Effiziente Algorithmen – Sommersemester 2012
4
Dijkstra-Algorithmus
Allgemeines
Hier betrachten wir einen Algorithmus für das Problem
Single-Source-Shortest-Paths“
”
Kürzeste Wege von einem Startknoten aus.
Gegeben: gewichteter Digraph G = (V , E , c)
mit nichtnegativen Kantenkosten und s ∈ V .
Gesucht ist für jedes v ∈ V der Abstand d(s, v ) und im Fall d(s, v ) < ∞
ein Weg von s nach v der Länge d(s, v ).
Der Algorithmus von Dijkstra löst dieses Problem.
(Aussprache: Deiks-tra“.)
”
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
5
Dijkstra-Algorithmus
Algorithmusidee
(Zündende) Idee:
Eine Kante (v , w ) wird als Einbahnstraßen-Zündschnur“ der Länge
”
c(v , w ) gedacht.
Die Zündschnüre brennen mit konstanter Geschwindigkeit 1 pro Sekunde.
Zum Zeitpunkt t0 = 0 halten wir ein Zündholz an den Knoten s.
Alle Zündschnüre, die Kanten (s, v ) entsprechen, beginnen zu brennen.
Das nächste interessante Ereignis:
Zum Zeitpunkt t = min{c(s, v ) | (s, v ) ∈ E }
erreicht das Feuer (mindestens) einen Knoten v .
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
6
Dijkstra-Algorithmus
Algorithmusidee
Vereinbarung: Wenn das Feuer einen neuen Knoten v erreicht, beginnen
ohne Verzögerung alle Zündschnüre zu brennen, die zu Kanten (v , w )
gehören.
Wenn später das Feuer über andere Schnüre nochmals bei v ankommt,
passiert nichts mehr.
Veranschaulichung: Spätere Folien. Ignoriere Notationen.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
7
Dijkstra-Algorithmus
Algorithmusidee
Ablauf des Algorithmus von Dijkstra
s
2
h
1
3
3
a
1
k
3
g
3
b
1
2
1
l
2
2
c
1
5
d
2
1
f
2
3
e
Der Ausgangsgraph.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
8
Dijkstra-Algorithmus
Algorithmusidee
Ablauf des Algorithmus von Dijkstra
3
3
1
2
1
2
1
2
1
5
8
3
1
3
8
2
3
1
8
2
1
8
0
2
3
8
8
8
2
s = 0 angezündet, brennende Schnüre.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
8
Dijkstra-Algorithmus
Algorithmusidee
Ablauf des Algorithmus von Dijkstra
2
3
1
2
3
3
4
1
2
1
2
1
2
1
5
8
3
1
8
2
1
8
0
2
3
8
8
8
2
Zeitpunkt 1, neuer Knoten erreicht, neue Schnüre.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
8
Dijkstra-Algorithmus
Algorithmusidee
Ablauf des Algorithmus von Dijkstra
2
3
3
1
1
2
3
3
3
1
2
1
2
1
2
3
1
5
8
2
1
8
0
2
3
8
8
8
2
Zeitpunkt 2, zwei neue Knoten erreicht, neue Schnüre.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
8
Dijkstra-Algorithmus
Algorithmusidee
Ablauf des Algorithmus von Dijkstra
0
2
2
1
3
3
1
1
2
3
3
1
2
5
1
2
1
2
3
1
5
8
2
3
8
8
5
3
2
Zeitpunkt 3, zwei neue Knoten erreicht, neue Schnüre.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
8
Dijkstra-Algorithmus
Algorithmusidee
Ablauf des Algorithmus von Dijkstra
0
2
2
1
3
3
1
1
2
3
3
1
2
5
1
2
1
2
3
1
5
6
2
3
8
8
5
3
2
Zeitpunkt 5, zwei neue Knoten erreicht, neue Schnüre.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
8
Dijkstra-Algorithmus
Algorithmusidee
Ablauf des Algorithmus von Dijkstra
0
2
2
1
3
3
1
1
2
3
3
1
2
5
1
2
1
2
3
1
5
6
2
3
8
8
5
3
2
Zeitpunkt 6, neuer Knoten erreicht, keine neue Schnur.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
9
Dijkstra-Algorithmus
Algorithmusidee
Klar“:
”
Das Feuer erreicht den Knoten v genau zum Zeitpunkt d(s, v ).
Denn: Der Zeitraum [0, d(s, v )] genügt genau, damit das Feuer einen
kürzesten Weg durchwandern kann.
Wir bilden diese Idee algorithmisch nach. Dabei bemerken wir, dass nur die
n Zeitpunkte d(s, v ), v ∈ V , wirklich interessant sind.
Daher: n Runden.
Dazwischen wandert das Feuer irgendwelche Kanten entlang, ohne dass im
Prinzip viel passiert (selbst wenn ein schon erreichter Knoten nochmals
erreicht wird).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
9
Dijkstra-Algorithmus
Algorithmusidee
O.B.d.A.: V = {1, . . . , n}.
Der Algorithmus arbeitet in n Runden, eine für jeden Knoten, (in der
Reihenfolge der d(s, v )!).
Der Algorithmus erzeugt eine (Daten-)Struktur und hält sie durch die
Runden aufrecht.
V ist in zwei disjunkte Mengen S und V − S zerlegt (veränderlich).
(Die Knoten in S wurden schon vom Feuer erreicht, die Knoten in V − S
noch nicht.)
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
10
Dijkstra-Algorithmus
Algorithmusidee
Beispiel:
2
1
3
3
1
2
3
3
3
1
2
1
2
1
2
3
1
5
8
2
1
8
0
2
3
8
8
8
2
Während des Ablaufs wird pro Runde ein Knoten zu S hinzugefügt. (Der
nächste Knoten, der vom Feuer erreicht wird.)
Runde 0: Anfangs ist S = {s}, man hat also stets s ∈ S.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
11
Dijkstra-Algorithmus
Algorithmusidee
Ein Hilfsarray speichert Informationen zur Verwaltung der Abstände:
dist[1..n]: speichert eine obere Schranke für d(s, v ).
Genauere Idee:
dist[v ] ist die Länge eines kürzesten S-Weges von s nach v .
Definition 4.1.2
Ein S-Weg nach v ist ein Weg
p = (s = v0 , v1 , . . . , vt−1 , vt = v ) mit v0 , v1 , . . . , vt−1 ∈ S.
Wenn es keinen S-Weg nach v gibt, ist dist[v ] = ∞.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
12
Dijkstra-Algorithmus
Algorithmusidee
Aus Sicht von v ∈ S:
Das Feuer hat v schon erreicht; es gilt dist[v ]= d(s, v ).
Aus Sicht von v ∈ V − S, mit dist[v ] = ∞:
Das Feuer hat noch keinen Knoten w erreicht, so dass (w , v ) eine
Kante ist.
Aus Sicht von v ∈ V − S, mit dist[v ] < ∞:
Das Feuer hat v noch nicht erreicht; es gibt aber einen Knoten w , so
dass die Zündschnur von w nach v schon brennt.
Wann wird das Feuer v spätestens erreichen?
dist[v ] = min{dist[w ] + c(w , v ) | w ∈ S, (w , v ) ∈ E }.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
13
Dijkstra-Algorithmus
Algorithmusidee
Wenn dist[v ] für alle v ∈ V − S die richtige Bedeutung hat, kann man
ablesen, welchen Knoten das Feuer als nächstes erreicht, nämlich
einen Knoten u ∈ V − S, der dist[v ], v ∈ V − S, minimiert.
(Es könnte auch mehrere solche Knoten geben. Man wählt einen davon.)
Wir fügen Knoten u zu S hinzu (er brennt an),
und notieren den aktuellen Wert dist[u].
Was ist noch zu tun?
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
14
Dijkstra-Algorithmus
Algorithmusidee
Beispiel:
2
1
3
3
1
2
3
3
3
1
2
1
2
1
2
3
1
5
8
2
1
8
0
2
3
8
8
8
2
Ab jetzt können S-Wege auch den Knoten u benutzen.
Wir werden später sehen, dass es nur auf neue S-Wege ankommt, bei denen
u der letzte Knoten in S ist.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
15
Dijkstra-Algorithmus
Algorithmusidee
Beispiel:
2
1
3
2
3
1
1
2
3
3
1
2
1
2
1
2
3
1
5
8
2
3
8
8
5
3
8
0
2
Das heißt, es sind nur Knoten v ∈ V − S betroffen, die von u aus über
eine Kante (u, v ) erreichbar sind.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
16
Dijkstra-Algorithmus
Algorithmusidee
Für solche Knoten müssen wir eventuell den dist-Wert aktualisieren:
dist[v ] ← min{dist[v ], dist[u] + c(u, v )}.
(Das Feuer wandert jetzt auch entlang der Kante (u, v ).)
Der bisher entwickelte Algorithmus berechnet schon die Länge der
kürzesten Wege
(also die Zeitpunkte, zu denen das Feuer jeden Knoten erreicht).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
17
Dijkstra-Algorithmus
Algorithmusidee
Algorithmus Dijkstra-Distanzen(G , s) (Rohfassung)
Eingabe: gewichteter Digraph G = (V , E , c), Startknoten s
Ausgabe: Länge der kürzesten Wege von s zu den Knoten in G
(1) S ← {s};
(2) dist[s] ← 0;
(3) for v ∈ V − {s} do dist[v] ← ∞;
(4) for v ∈ V − {s} mit (s, v) ∈ E do
(5)
dist[v] ← c(s, v);
(6) while ∃u ∈ V − S: dist[u] < ∞ do
(7)
u ← ein solcher Knoten u mit minimalem dist[u];
(8)
S ← S ∪ {u};
(9)
for v ∈ V − S mit (u, v) ∈ E do
(10)
dist[v] ← min{dist[v], dist[u] + c(u, v)};
(11) Ausgabe: das Array dist[1..n].
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
18
Dijkstra-Algorithmus
Algorithmusidee
Ablauf des Algorithmus von Dijkstra
s
2
h
1
3
3
a
1
k
3
g
3
b
1
2
1
l
2
2
c
1
5
d
2
1
f
2
3
e
Der Ausgangsgraph.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
19
Dijkstra-Algorithmus
Algorithmusidee
Ablauf des Algorithmus von Dijkstra
3
3
1
2
1
2
1
2
1
5
8
3
1
3
8
2
3
1
8
2
1
8
0
2
3
8
8
8
2
Nach der Initialisierung (Zeilen (1)–(5))
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
19
Dijkstra-Algorithmus
Algorithmusidee
Ablauf des Algorithmus von Dijkstra
2
3
3
1
1
2
3
3
3
1
2
1
2
1
2
3
1
5
8
2
1
8
0
2
3
8
8
8
2
u = l, Zeile (8)
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
19
Dijkstra-Algorithmus
Algorithmusidee
Ablauf des Algorithmus von Dijkstra
2
2
1
3
3
1
1
2
3
3
1
2
1
2
1
2
3
1
5
8
2
3
8
8
5
3
8
0
2
u = l, Zeilen (9)–(10d)
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
19
Dijkstra-Algorithmus
Algorithmusidee
Ablauf des Algorithmus von Dijkstra
0
2
2
1
3
3
1
1
2
3
3
1
2
5
1
2
1
2
3
1
5
6
2
3
8
8
5
3
2
Alle v ∈ V − S erfüllen dist[v ] = ∞: Ende.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
19
Dijkstra-Algorithmus
Korrektheitsbeweis
Behauptung: Der Algorithmus gibt in dist[v ] für jeden Knoten v ∈ V
den Wert d(s, v ) aus.
Beweis: Wir zeigen durch Induktion über Runden die folgende Invariante:
(I1) Für v ∈ S ist dist[v ] = d(s, v ).
(I2) Für v ∈
/ S ist dist[v ] Länge eines kürzesten S-Weges von s nach v ,
falls ein solcher Weg existiert;
sonst ist dist[v ] = ∞.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
20
Dijkstra-Algorithmus
Korrektheitsbeweis
I.A.: Nach Runde 1 (Zeilen (1)–(5)) gilt (I1) ∧ (I2).
I.V.: t ≥ 2 und nach Runde t − 1 gilt (I1) ∧ (I2).
Schritt: Es folgt Durchlauf Nummer t durch die while-Schleife.
1. Fall: Für alle Knoten v ∈ V − S gilt dist[v ] = ∞.
⇒ es gibt keinen Durchlauf t; die Datenstruktur ändert sich nicht mehr.
Es gibt aber auch keine Kante (w , v ) mehr, die von einem Knoten w ∈ S
zu einem Knoten v ∈ V − S führt.
D. h.: Die Knoten v ∈ V − S sind von s aus nicht erreichbar, es gilt
d(s, v ) = ∞ für diese Knoten.
Für alle v ∈ S gilt dist[v ] = d(s, v ) nach I.V.
⇒
(I1) und (I2) gelten.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
21
Dijkstra-Algorithmus
Korrektheitsbeweis
2. Fall: In Zeile (7) wird ein Knoten u ∈ V − S gewählt. Dieses u soll zu S
hinzugefügt werden.
Wir müssen (I1) für u zeigen, d. h.: dist[u] = d(s, u).
Nach (I2) ist dist[u] < ∞ Länge eines S-Weges (s, . . . , w , u) von s nach
u, also gilt bestimmt dist[u] ≥ d(s, u).
Sei nun p = (s = v0 , v1 , . . . , vt = u) irgendein Weg von s nach u.
p beginnt in S und endet in V − S, also gibt es ein r mit:
s = v0 , v1 , . . . , vr −1 ∈ S, vr ∈
/ S. Dies ist ein S-Weg p 0 von s nach vr mit
Länge
c(p 0 ) = c(v0 , v1 ) + · · · + c(vr −1 , vr )
(I1)
≥ d(s, vr −1 ) + c(vr −1 , vr ) = dist[vr −1 ] + c(vr −1 , vr ).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
22
Dijkstra-Algorithmus
Korrektheitsbeweis
Die letzte Summe wird aber in Zeile (7) bei der Suche nach dem Minimum
berücksichtigt – das heißt:
c(p) ≥ c(p 0 ) ≥ dist[vr −1 ] + c(vr −1 , vr ) ≥ dist[u].
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
23
Dijkstra-Algorithmus
Korrektheitsbeweis
Achtung:
Wir benutzen hier, dass die Kantenlängen nicht negativ sind.
Das heißt, dass der S-Weg von s nach u keinesfalls länger ist als p.
Da p beliebig war, hat der S-Weg von s nach u mit Länge dist[u]
kleinstmögliche Länge, und dist[u] = d(s, u).
Damit gilt Aussage (I1) auch für den Knoten u, der neu zu S hinzukommt.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
24
Dijkstra-Algorithmus
Korrektheitsbeweis
Aussage (I2):
Wir überlegen: Für v ∈
/ S ∪ {u} und (u, v ) ∈
/ E endet jeder kürzeste
(S ∪ {u})-Weg nach v mit einer Kante (w , v ), mit w ∈ S. Nach (I1) aus
der I.V. gilt (I2) für (S ∪ {u}) und v .
Nun sei v ∈
/ S ∪ {u} und (u, v ) ∈ E .
Es könnte nun neue (S ∪ {u})-Wege nach v geben, die (u, v ) als letzte
Kante haben.
Zeilen (9) und (10) testen, ob hierdurch ein kürzerer (S ∪ {u})-Weg
entsteht, und ersetzen dist[v ] durch den besseren Wert, falls nötig.
Dadurch bleibt auch Invariante (I2) erhalten.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
25
Dijkstra-Algorithmus
Korrektheitsbeweis
Wir wollen aber eigentlich nicht nur die Distanzen d(s, v ), sondern
kürzeste Wege berechnen.
Idee: für jeden Knoten v merken wir uns, über welche Kante (w , v ) das
Feuer den Knoten v erreicht hat.
Wenn wir diese Vorgänger“-Information benutzen und von v immer
”
weiter rückwärts laufen, erhalten wir einen kürzesten Weg.
Am einfachsten lässt sich diese Information erstellen, wenn man auch
gleich für jeden Knoten mit dist[v ] < ∞ einen Knoten w ∈ S notiert,
der der letzte S-Knoten auf einem kürzesten S-Weg nach v ist.
Datenstruktur:
p[1..n]: speichert Vorgänger für v in S (falls es einen gibt).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
26
Dijkstra-Algorithmus
Korrektheitsbeweis
Notwendige Ergänzungen: p[s] ← −2;
(3b) for v ∈ V − {s} do dist[v] ← ∞; p[v] ← −1;
(4) for v ∈ V − {s} mit (s, v) ∈ E do
(5a)
dist[v] ← c(s, v);
(5b) p[v] ← s;
Aktualisierung in den späteren Runden:
(9) for v ∈ V − S mit (u, v) ∈ E do
(10a) dd ← dist[u] + c(u, v);
(10b) if dd < dist[v] then
(10c)
dist[v] ← dd;
(10d)
p[v] ← u;
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
27
Dijkstra-Algorithmus
Korrektheitsbeweis
Algorithmus Dijkstra(G , s)
Eingabe: gewichteter Digraph G = (V , E , c), Startknoten s
Ausgabe: Länge d(s, v ) der kürzesten Wege, Vorgängerknoten p(v )
(1)
S ← {s}; p[s] ← −2;
(2)
dist[s] ← 0;
(3)
for v ∈ V − {s} do dist[v] ← ∞; p[v] ← −1;
(4)
for v ∈ V − {s} mit (s, v) ∈ E do
(5)
dist[v] ← c(s, v); p[v] ← s;
(6)
while ∃u ∈ V − S: dist[u] < ∞ do
(7)
u ← ein solcher Knoten u mit minimalem dist[u];
(8)
S ← S ∪ {u};
(9)
for v ∈ V − S mit (u, v) ∈ E do
(10a)
dd ← dist[u] + c(u, v);
(10b)
if dd < dist[v] then
(10c)
dist[v] ← dd;
(10d)
p[v] ← u;
(11) Ausgabe: dist[1..n] und p[1..n].
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
28
Dijkstra-Algorithmus
Korrektheitsbeweis
Nach dem Algorithmus ist klar, dass p[v ] 6= − 1 ( undefiniert“) genau
”
dann gilt, wenn dist[v ] < ∞.
p[v ] = −2 ( Startknoten, hat keinen Vorgänger“) gilt nur für v = s.
”
Behauptung: Zusätzlich zu (I1) und (I2) erfüllt der Algorithmus von
Dijkstra die folgende Invarianten:
(I3) Wenn v ∈ S, dann ist p[v ] 6= − 1 und für v 6= s ist p[v ] letzter
Knoten auf einem kürzesten Weg von s nach v ; ein solcher verläuft
vollständig in S.
(I4) Wenn v ∈
/ S, dann gilt: p[v ] 6= − 1 genau dann wenn dist[v ] < ∞
gilt.
In diesem Fall: p[v ] ∈ S und p[v ] ist letzter S-Knoten auf einem S-Weg
von s nach v kürzester Länge dist[v ].
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
29
Dijkstra-Algorithmus
Korrektheitsbeweis
Beweis von (I3) und (I4): Induktion über Runden.
Nach Runde 1 sind nur s und die Knoten v mit (s, v ) ∈ E betroffen; die
Aussagen sind klar.
Nun betrachten wir Runde t durch die while-Schleife.
1. Fall: Es gibt keinen Knoten v ∈ V − S mit dist[v ] < ∞.
⇒ es gibt keinen Durchlauf t; die Datenstruktur ändert sich nicht mehr.
(I3) und (I4) gelten nach I.V.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
30
Dijkstra-Algorithmus
Korrektheitsbeweis
2. Fall: Es gibt einen Knoten v ∈ V − S mit dist[v ] < ∞.
Der Algorithmus wählt ein solches v mit minimalem dist[v ], genannt u.
Nach I.V. (I4) ist w = p[u] ∈ S letzter Knoten auf einem S-Weg
kürzester Länge dist[u]. Nach (I1), angewendet auf u, ist dies ein
kürzester Weg von s nach u.
Damit gilt (I3) auch für u.
Auch (I4) folgert man aus der I.V. und (I2) (Übung).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
31
Dijkstra-Algorithmus
Korrektheitsbeweis
Ergebnis:
Wenn der Algorithmus von Dijkstra anhält, führen die p[v ]-Verweise von
jedem Knoten v aus entlang eines kürzesten Weges (zurück) zu s.
Da die p[v ]-Verweise keinen Kreis bilden können (mit dem Schritt
S ← S ∪ {u} wird der Verweis vom neuen S-Knoten u auf den S-Knoten
p[u] endgültig fixiert), bilden die Kanten (p[v ], v ) einen Baum, den
sogenannten
Baum der kürzesten Wege.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
32
Dijkstra-Algorithmus
Korrektheitsbeweis
Beispiel:
0
2
2
1
3
3
1
1
2
3
1
2
5
1
2
1
2
3
1
5
6
2
3
8
FG KTuEA, TU Ilmenau
3
8
5
3
2
Effiziente Algorithmen – Sommersemester 2012
33
Dijkstra-Algorithmus
Implementierungsdetails
Wieso steht der Algorithmus von Dijkstra unter der Überschrift
Greedy-Algorithmen“?
”
Er hält eine sehr geschickte Datenstruktur bereit,
wählt dann aber in jeder Runde nach dem Greedy-Muster als nächsten
einzubeziehenden Knoten einen, dessen dist[v ]-Wert (also Länge des
besten S-Weges) minimal ist.
Zum Glück: Der kürzeste S-Weg ist dann auch ein kürzester Weg im
gesamten Graphen.
Weiter unten lernen wir den Algorithmus von Jarnı́k/Prim kennen, der ein
Paradebeispiel für einen Greedy“-Algorithmus ist und sich nur minimal
”
vom Algorithmus von Dijkstra unterscheidet.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
34
Dijkstra-Algorithmus
Implementierungsdetails
Implementierungsdetails im Algorithmus von Dijkstra:
Wir nehmen an, dass G = (V , E , c) mit V = {1, . . . , n} in
Adjazenzlistendarstellung gegeben ist.
Die Kantengewichte c(e), e ∈ E , stehen in den Adjazenzlisten bei den
Kanten.
Für jeden Knoten v ∈ V − S wollen wir immer wissen:
1) die Länge dist[v ] des (bzw. eines) kürzesten S-Weges von s nach
v , falls ein solcher existiert (sonst: dist[v ] = ∞ )
2) den (bzw. einen) (Vorgänger-)Knoten p(v ) = u ∈ S mit
dist[v ] = dist[u] + c(u, v ), falls ein solcher existiert.
p: array[1..n]: Vorgänger, in eigenem Array.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
35
Dijkstra-Algorithmus
Implementierungsdetails
Verwalte die Knoten v ∈ V − S mit Werten dist[v ] < ∞ mit den
dist[v ]-Werten als Schlüssel in einer
Priority-Queue PQ.
Wenn dist[v ] = ∞, ist v (noch) nicht in der PQ.
inS: array[1..n] of Boolean (Knoten in S).
id: array[1..n] of entry (für PQ-Identitäten).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
36
Dijkstra-Algorithmus
Implementierungsdetails
Dijkstra-mit-Priority-Queue(G , s)
Eingabe: gewichteter Digraph G = (V , E , c), V = {1, . . . , n}, Startknoten s;
Ausgabe: Länge d(s, v ) der kürzesten Wege, Vorgängerknoten p(v )
Hilfsstrukturen: PQ: eine (leere) Priority-Queue; inS, p, id: s.o.
(1)
for v from 1 to n do inS[v] ← false; p[v] ← −1;
(2)
inS[s] ← true; p[s] ← −2;
(3)
dist[s] ← 0;
(4)
for Knoten v mit (s, v) ∈ E do
(5)
dist[v] ← c(s, v); p[v] ← s; id[v] ← PQ.insert(dist[v],v);
(6)
while not PQ.isempty do
(7)
(id, d, u) ← PQ.extractMin; (∗ Identität, Distanz, Knotenname ∗)
(8)
inS[u] ← true;
(9)
for Knoten v mit (u, v) ∈ E and not inS[v] do
(10)
dd ← dist[u] + c(u, v);
(11)
if p[v] ≥ 0 and dd < dist[v] then
(12)
PQ.decreaseKey(id[v],dd); p[v] ← u;
(13)
if p[v] = −1 (∗ v vorher nicht erreicht ∗) then
(14)
dist[v] ← dd; p[v] ← u; id[v] ← PQ.insert(dd,v);
(15) Ausgabe: dist[1..n] und p[1..n].
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
37
Dijkstra-Algorithmus
Implementierungsdetails
Aufwand:
Die Priority-Queue realisieren wir als (binären) Heap (siehe 3.3).
Maximale Anzahl von Einträgen: n − 1.
Initialisierung:
deg(s) Einfügungen, Kosten O(deg(s) · log n).
Es gibt ≤ n − 1 Durchläufe durch die while-Schleife mit
Organisationsaufwand jeweils O(1):
O(n) für die Schleifenorganisation.
In Schleifendurchlauf Nummer t, wo ut zu S hinzugefügt wird:
PQ.extractmin kostet Zeit O(log n).
Durchmustern der deg(ut ) Nachbarn von ut :
Jedes PQ.insert oder PQ.decreaseKey kostet Zeit O(log n).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
38
Dijkstra-Algorithmus
Implementierungsdetails
Insgesamt:
n · O(log n) +
X
O(deg(ut ) · log n)
2≤t≤n
= O n log n + log n ·
X
deg(ut ) = O(n log n + m log n),
2≤t≤n
wobei n = |V | (Knotenzahl), m = |E | (Kantenzahl).
Satz 4.1.3
Der Algorithmus von Dijkstra mit Verwendung einer Priority-Queue, die
als Binärheap realisiert ist, ermittelt kürzeste Wege von Startknoten s aus
in einem Digraphen G = (V , E , c) in Zeit O((n + m) log n).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
39
Dijkstra-Algorithmus
Implementierungsdetails
Mitteilung 4.1.4
Es gibt eine Implementierung der Datenstruktur Priority Queue mit folgenden
Laufzeiten:
empty und isempty (und das Ermitteln des kleinsten Eintrags) kosten konstante
Zeit;
extractMin kostet Zeit O(log n);
n Einfügungen kosten Zeit O(n log n);
m decreaseKey-Operationen benötigen zusammen Zeit O(m).
Fibonacci-Heaps“
”
(fortgeschritten, siehe z. B. das Buch [Cormen et al.])
Resultierende Laufzeit für Algorithmus von Dijkstra: O(m + n log n)
Lineare Laufzeit für Graphen mit m = Ω(n log n) Kanten.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
40
Minimale Spannbäume
Allgemeines
4.2 Minimale Spannbäume:
Der Algorithmus von Jarnı́k/Prim
Definition 4.2.1
(a) Ein Graph G = (V , E ) heißt kreisfrei, wenn er keinen Kreis besitzt.
Beispiel: Ein kreisfreier Graph:
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
41
Minimale Spannbäume
Allgemeines
Definition 4.2.1 (Fortsetzung)
(b) Ein Graph G heißt ein freier Baum (oder nur Baum),
wenn er zusammenhängend und kreisfrei ist.
Beispiel: Ein (freier) Baum:
20 Knoten, 19 Kanten.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
42
Minimale Spannbäume
Allgemeines
Anmerkung: Die Zusammenhangskomponenten
eines kreisfreien Graphen sind freie Bäume.
Kreisfreie Graphen heißen daher auch (freie) Wälder.
Lemma 4.2.2
Sei G = (V , E ) ein Graph mit n = |V | und m = |E |. Dann gilt:
(a) Wenn G kreisfrei ist, gilt m ≤ n − 1.
(b) Wenn G zusammenhängend ist, gilt m ≥ n − 1.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
43
Minimale Spannbäume
Allgemeines
Fundamental-Lemma 4.2.3 über freie Bäume
Wenn G = (V , E ) ein Graph ist, mit Knotenzahl n = |V | und Kantenzahl
m = |E |, dann sind folgende Aussagen äquivalent:
(a) G ist ein Baum.
(b) G ist kreisfrei und m ≥ n − 1.
(c) G ist zusammenhängend und m ≤ n − 1.
(d) Zu jedem Paar u, v von Knoten gibt es genau einen einfachen Weg von
u nach v .
(e) G ist kreisfrei, aber das Hinzufügen einer beliebigen weiteren Kante
erzeugt einen Kreis (G ist maximal kreisfrei“).
”
(f) G ist zusammenhängend, aber das Entfernen einer beliebigen Kante
erzeugt einen nicht zusammenhängenden Restgraphen (G ist minimal
”
zusammenhängend“).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
44
Minimale Spannbäume
Allgemeines
Beweis des Fundamentallemmas: Für Interessierte in einer eigenen Notiz
nachzulesen (siehe Webseite).
Wir benutzen das Fundamental-Lemma über freie Bäume in der folgenden
Weise:
(1) Wenn G ein Baum ist, erfüllt G alle genannten Eigenschaften (a)–(f).
(2) Wenn wir eine der Eigenschaften nachweisen, hat sich G als Baum
erwiesen.
Aussagen, die aus dem Fundamental-Lemma über Bäume folgen:
G sei Baum mit n Knoten. Dann gilt:
(1) G hat n − 1 Kanten.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
45
Minimale Spannbäume
Allgemeines
(2) Wenn man zu G eine Kante (u, w ) hinzufügt, entsteht genau ein Kreis
(aus (u, w ) und dem eindeutigen Weg von u nach w in G ).
u
w
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
46
Minimale Spannbäume
Allgemeines
(3) Wenn man aus G eine Kante (u, w ) streicht, zerfällt der Graph in 2
Komponenten
U = {v ∈ V | v von u aus über Kanten aus E − {(u, w )} erreichbar};
W = {v ∈ V | v von w aus über Kanten aus E − {(u, w )} erreichbar}.
W
U
u
FG KTuEA, TU Ilmenau
w
Effiziente Algorithmen – Sommersemester 2012
47
Minimale Spannbäume
Allgemeines
Definition 4.2.4
Es sei G = (V , E ) ein zusammenhängender Graph.
Eine Menge T ⊆ E von Kanten heißt ein
Spannbaum für G , wenn (V , T ) ein Baum ist.
Beispiel:
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
48
Minimale Spannbäume
Allgemeines
Klar: Jeder zusammenhängende Graph hat einen Spannbaum.
(Iterativer Prozess: Starte mit E . Solange es Kanten gibt, die auf einem
Kreis liegen, entferne eine solche Kreiskante.
Der Zusammenhang bleibt stets erhalten; schließlich muss der Rest-Graph
kreisfrei sein.)
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
49
Minimale Spannbäume
Allgemeines
Definition 4.2.5
Es sei G = (V , E , c) ein gewichteter Graph, d.h. c : E → R ist eine
Gewichtsfunktion“ oder Kostenfunktion“.
”
”
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
2
Kantenkosten modellieren: Herstellungskosten – Leitungsmiete – . . .
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
50
Minimale Spannbäume
Allgemeines
(a) Jeder Kantenmenge E 0 ⊆ E wird durch
X
0
c(e)
c(E ) :=
0
e∈E
ein Gesamtgewicht zugeordnet.
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
2
Gesamtgewicht: 3 + 1 + 4 + 5 + 2 + 4 + 3 + 3 + 3 + 2 = 30.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
51
Minimale Spannbäume
Allgemeines
(b) Sei G zusammenhängend. Ein Spannbaum T ⊆ E für G heißt ein
minimaler Spannbaum, wenn
c(T ) = min{c(T 0 ) | T 0 Spannbaum von G },
d.h. wenn er minimale Kosten unter allen Spannbäumen hat.
Abkürzung: MST ( minimum spanning tree“).
”
3
5
1
1
2
2
3
4
1
5
5
1
1
4
1
2
2
3
3
1
1
2
3
3
1
5
3
3
1
1
4
3
2
2
2
4
2
3
1
2
Zwei minimale Spannbäume, jeweils mit Gesamtgewicht 18.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
52
Minimale Spannbäume
Allgemeines
Klar: Jeder Graph besitzt einen MST.
(Es gibt nur endlich viele Spannbäume.)
Achtung: Es kann mehrere verschiedene MSTs geben (die alle dasselbe
Gesamtgewicht haben).
Aufgabe: Zu gegebenem G = (V , E , c) finde einen MST T .
Hier:
Algorithmus von Jarnı́k/Prim“
”
Algorithmus von Kruskal“
”
Algorithmenparadigma: greedy“ ( gierig“).
”
”
Baue Lösung Schritt für Schritt auf.
(Hier: wähle eine Kante für T nach der anderen.)
Treffe in jedem Schritt die (lokal) günstigste Entscheidung
Algorithmus von Jarnı́k/Prim: Ähnlichkeit zum Dijkstra-Algorithmus.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
53
Minimale Spannbäume
Jarnı́k/Prim
Algorithmus von Jarnı́k/Prim:
S: Menge von Knoten. Enthält die bisher erreichten“ Knoten.
”
R: Menge von Kanten. Enthält die bisher gewählten“ Kanten.
”
(1) Wähle einen beliebigen (Start-)Knoten s ∈ V .
S ← {s}; R ← ∅;
(2) Wiederhole (n − 1)-mal:
Finde w ∈ S und u ∈ V − S, so dass
c(w , u) minimal unter allen Werten
c(w 0 , u 0 ), w 0 ∈ S, u 0 ∈ V − S, ist.
S ← S ∪ {u};
R ← R ∪ {(w , u)};
(3) Ausgabe: R.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
54
Minimale Spannbäume
Jarnı́k/Prim
Beispiel (Jarnı́k/Prim):
1
3
5
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
55
Minimale Spannbäume
Jarnı́k/Prim
Beispiel (Jarnı́k/Prim):
1
3
5
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
55
Minimale Spannbäume
Jarnı́k/Prim
Beispiel (Jarnı́k/Prim):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
55
Minimale Spannbäume
Jarnı́k/Prim
Beispiel (Jarnı́k/Prim):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
55
Minimale Spannbäume
Jarnı́k/Prim
Beispiel (Jarnı́k/Prim):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
55
Minimale Spannbäume
Jarnı́k/Prim
Beispiel (Jarnı́k/Prim):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
55
Minimale Spannbäume
Jarnı́k/Prim
Beispiel (Jarnı́k/Prim):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
55
Minimale Spannbäume
Jarnı́k/Prim
Beispiel (Jarnı́k/Prim):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
55
Minimale Spannbäume
Jarnı́k/Prim
Beispiel (Jarnı́k/Prim):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
55
Minimale Spannbäume
Jarnı́k/Prim
Beispiel (Jarnı́k/Prim):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
55
Minimale Spannbäume
Jarnı́k/Prim
Beispiel (Jarnı́k/Prim):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
55
Minimale Spannbäume
Schnitteigenschaft
Die Schnitteigenschaft
Für den Korrektheitsbeweis des Algorithmus von Jarnı́k/Prim:
Cut property“ – Schnitteigenschaft
”
Eine Partition (S, V − S) mit ∅ =
6 S 6= V heißt ein Schnitt.
V−S
S
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
56
Minimale Spannbäume
Schnitteigenschaft
Die Schnitteigenschaft
Definition 4.2.6
Eine Menge R ⊆ E heißt erweiterbar (zu einem MST), wenn es einen
MST T mit R ⊆ T gibt.
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
2
R ist erweiterbar, denn es gibt einen MST T ⊇ R.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
57
Minimale Spannbäume
Schnitteigenschaft
Die Schnitteigenschaft
Behauptung (Schnitteigenschaft):
Wenn R ⊆ E erweiterbar ist
und (S, V − S) ein Schnitt, so dass
keine Kante aus R von S nach V − S verläuft,
und wenn e = (v , w ), v ∈ S, w ∈ V − S eine Kante ist,
die den Wert c((u, x)), u ∈ S, x ∈ V − S, minimiert,
dann ist auch R ∪ {e} erweiterbar.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
58
Minimale Spannbäume
Schnitteigenschaft
Die Schnitteigenschaft
Beweis der Schnitteigenschaft:
Sei R ⊆ E , sei T ⊇ R ein MST;
sei (S, V − S) ein Schnitt, so dass keine Kante aus R von S nach V − S
verläuft;
sei e ∈ S × (V − S) mit minimalem c(e).
1. Fall: e ∈ T . Dann gilt R ∪ {e} ⊆ T , also ist R ∪ {e} erweiterbar, fertig.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
59
Minimale Spannbäume
Schnitteigenschaft
Die Schnitteigenschaft
2. Fall: e ∈
/ T.
R:
Rest:
S
V−S
Eine erweiterbare Menge R.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
60
Minimale Spannbäume
Schnitteigenschaft
Die Schnitteigenschaft
2. Fall: e ∈
/ T.
R:
Rest:
T−R:
S
V−S
MST T mit R ⊆ T .
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
61
Minimale Spannbäume
Schnitteigenschaft
Die Schnitteigenschaft
2. Fall: e ∈
/ T.
R:
Rest:
T−R:
v
e
w
S
V−S
e = (v , w ) minimiert c((u, x)), u ∈ S, x ∈ V − S.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
62
Minimale Spannbäume
Schnitteigenschaft
Die Schnitteigenschaft
2. Fall: e ∈
/ T.
R:
Rest:
T−R:
v
e
w
e’
S
V−S
Weg in T von v nach w wechselt von S nach V − S bei Kante e 0 .
Es entsteht ein Kreis in T ∪ {e}, auf dem e und e 0 liegen.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
63
Minimale Spannbäume
Schnitteigenschaft
Die Schnitteigenschaft
2. Fall: e ∈
/ T.
R:
Rest:
T−R:
v
e
w
S
V−S
Entferne e 0 , füge e hinzu: Te := (T − {e 0 }) ∪ {e}.
Neuer Spannbaum Te ⊇ R ∪ {e}.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
64
Minimale Spannbäume
Schnitteigenschaft
Die Schnitteigenschaft
2. Fall: e ∈
/ T.
R:
Rest:
T−R:
v
e
w
S
V−S
Weil c(e) ≤ c(e 0 ) (Minimalität von c(e) über den Schnitt (S, V − S)):
c(Te ) ≤ c(T ), also ist Te wieder ein MST.
Also: R ∪ {e} ist erweiterbar.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
65
Minimale Spannbäume
Korrektheit von Jarnı́k/Prim
Korrektheit des Algorithmus von Jarnı́k/Prim:
Ri : Kantenmenge (Größe i), die nach Runde i in R steht.
Si : Knotenmenge (Größe i + 1), die nach Runde i in S steht.
Weil in jeder Runde ein Knoten und eine Kante hinzukommt, und man an
die schon vorhandene Knotenmenge anschließt, ist jedes Ri kreisfrei und
zusammenhängend, also ein Baum. Die Kanten von Ri verbinden genau
die Knoten von Si .
Zu zeigen: Rn−1 ist ein MST für G .
Weil Rn−1 kreisfrei ist und n − 1 Kanten hat, ist Rn−1 Spannbaum.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
66
Minimale Spannbäume
Korrektheit von Jarnı́k/Prim
Induktionsbehauptung IB(i):
Ri ist erweiterbar.
(Dies beweisen wir durch Induktion über i = 0, 1, . . . , n − 1.)
Dann besagt IB(n − 1), dass T ⊇ Rn−1 ist für einen MST T . Weil alle
Spannbäume n − 1 Kanten haben und Rn−1 auch n − 1 Kanten hat, ist
T = Rn−1 , also ist die Ausgabe Rn−1 ein MST.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
67
Minimale Spannbäume
Korrektheit von Jarnı́k/Prim
I.A.: i = 0. – R0 = ∅, und es gibt einen MST T für G .
I.V.: 1 ≤ i ≤ n − 1 und Ri−1 ist erweiterbar.
I.S.: (Si−1 , V − Si−1 ) ist ein Schnitt, und keine Kante von Ri−1 überquert diesen
Schnitt.
3
5
1
1
2
2
3
4
1
5
5
1
1
4
1
2
2
3
3
1
1
2
3
3
1
5
3
3
1
1
4
3
2
2
2
4
2
3
1
2
Der Algorithmus von Jarnı́k/Prim wählt unter allen Kanten, die den Schnitt
überqueren, eine billigste Kante e, und bildet Ri := Ri−1 ∪ {e}.
Mit der Schnitteigenschaft folgt: Ri ist erweiterbar. FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
68
Minimale Spannbäume
Jarnı́k/Prim: Implementierungsdetails
Implementierungsdetails im Algorithmus von Jarnı́k/Prim:
Wir nehmen an, dass G = (V , E , c) mit V = {1, . . . , n} in
Adjazenzlistendarstellung gegeben ist.
Die Kantengewichte c(e) stehen in den Adjazenzlisten bei den Kanten.
Für jeden Knoten v ∈ V − S wollen wir immer wissen:
1) die Länge d(v ) der billigsten Kante (u, v ), u ∈ S, falls eine existiert:
in dist[v ] ( Abstand von S“)
”
2) den (einen) (Vorgänger-)Knoten p(v ) = u ∈ S mit c(u, v ) = d(v ), falls ein
solcher existiert.
Falls es von S keine Kante nach v gibt, setzen wir d(v ) = ∞ und p(v ) = − 1.
Verwalte die Knoten v ∈ V − S mit Werten d(v ) < ∞ mit den d(v )-Werten als
Schlüssel in einer Priority-Queue PQ.
Wenn d(v ) = ∞, ist v (noch) nicht in der PQ.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
69
Minimale Spannbäume
Jarnı́k/Prim: Implementierungsdetails
Jarnik/Prim-mit-Priority-Queue(G , s)
Eingabe: gewichteter Graph G = (V , E , c), V = {1, . . . , n}, Startknoten s;
Ausgabe: Ein MST für G .
Hilfsstrukturen: PQ: eine (leere) Priority-Queue; inS, p, id: wie oben
(1)
for v from 1 to n do inS[v] ← false; p[v] ← −1;
(2)
inS[s] ← true; p[s] ← −2;
(3)
for Knoten v mit (s, v) ∈ E do
(4)
dist[v] ← c(s, v); p[v] ← s; id[v] ← PQ.insert(dist[v],v);
(5)
while not PQ.isempty do
(6)
(id, d, u) ← PQ.extractMin; (∗ Identität, Distanz, Knotenname ∗)
(7)
inS[u] ← true;
(8)
for Knoten v mit (u, v) ∈ E and not inS[v] do
(9)
dd ← c(u, v); (∗ einziger Unterschied zu Dijkstra! ∗)
(10)
if p[v] ≥ 0 and dd < dist[u] then
(11)
PQ.decreaseKey(id[v],dd); p[v] ← u;
(12)
if p[v] = −1 (∗ v vorher nicht erreicht ∗) then
(13)
dist[v] ← dd; p[v] ← u; id[v] ← PQ.insert(dd,v);
(14) Ausgabe: Kantenmenge T = {(v , p[v ]) | v ∈ S − {s}}.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
70
Minimale Spannbäume
Jarnı́k/Prim: Implementierungsdetails
Zeitanalyse: Exakt wie für den Dijkstra-Algorithmus.
Satz 4.2.7
Der Algorithmus von Jarnı́k/Prim mit Verwendung einer Priority-Queue,
die als Binärheap realisiert ist, ermittelt einen minimalen Spannbaum für
G = (V , E , c) in Zeit O((n + m) log n).
Wie beim Algorithmus von Dijkstra: Wenn man Fibonacci-Heaps o.ä. verwendet,
bei denen m decreaseKey-Operationen Zeit O(m) benötigen:
Laufzeit für Jarnı́k/Prim: O(m + n log n).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
71
Minimale Spannbäume
Algorithmus von Kruskal
4.3 Der Algorithmus von Kruskal
Auch dieser Algorithmus löst das MST-Problem.
Anderer Ansatz als bei Jarnı́k/Prim:
Probiere Kanten in aufsteigender Reihenfolge des Kantengewichts, und
wähle eine Kante für den zu bauenden MST, wenn sie die
Kreisfreiheitseigenschaft nicht verletzt.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
72
Minimale Spannbäume
Algorithmus von Kruskal
Algorithmus von Kruskal
1. Schritt: Sortiere die Kanten
e1 , . . . , em nach den Gewichten c(e1 ), . . . , c(em ) aufsteigend. – Also
O.B.d.A.: c(e1 ) ≤ . . . ≤ c(em ).
2. Schritt: Setze R ← ∅.
3. Schritt: Für i = 1, 2, . . . , m tue folgendes:
Falls R ∪ {ei } kreisfrei ist, setze R ← R ∪ {ei }
(∗ sonst, d.h. wenn ei einen Kreis schließt,
bleibt R unverändert ∗)
(∗ Optional: Beende Schleife, wenn |R| = n − 1. ∗)
4. Schritt: Die Ausgabe ist R.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
73
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
75
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Beispiel (Kruskal):
3
5
1
1
2
2
4
1
5
4
3
2
3
1
1
2
3
3
1
FG KTuEA, TU Ilmenau
2
Effiziente Algorithmen – Sommersemester 2012
74
Minimale Spannbäume
Algorithmus von Kruskal
Uns interessieren: 1) Korrektheit; 2) Laufzeit (später)
Korrektheit des Algorithmus von Kruskal:
Ri :
Kantenmenge, die nach Runde i in R steht.
Weil dies im 3. Schritt getestet wird, ist sichergestellt, dass jedes Ri
kreisfrei, also ein Wald ist.
Zu zeigen: Rm ist ein MST für G .
Erinnerung: R ⊆ E heißt erweiterbar, wenn R ⊆ T für einen MST T .
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
75
Minimale Spannbäume
Algorithmus von Kruskal
Induktionsbehauptung IB(i):
Ri ist erweiterbar.
(Beweis gleich, durch Induktion über i = 0, 1, . . . , m.)
Dann besagt IB(m), dass Rm ⊆ T ist für einen MST T .
Beh.: Es gilt auch T ⊆ Rm .
(Also gilt Gleichheit, und Rm ist ein MST.)
(Beweis der Beh.: Sei e ∈ T . Dann ist e = ei für ein i, und ei wurde in
Runde i getestet. Weil Ri−1 ⊆ Rm ⊆ T und ei ∈ T , ist Ri−1 ∪ {ei } ⊆ T ,
also ist Ri−1 ∪ {ei } kreisfrei, also fügt der Algorithmus die Kante ei in
Runde i zu R hinzu, also gilt ei ∈ Rm .)
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
76
Minimale Spannbäume
Algorithmus von Kruskal
Induktionsbehauptung IB(i): Ri ist erweiterbar.
I.A.: R0 = ∅ ist erweiterbar.
I.V.: 1 ≤ i < m und Ri−1 ist erweiterbar.
I.S.: Nun wird Runde i mit Kante ei ausgeführt.
1. Fall: Ri−1 ∪ {ei } enthält einen Kreis. Dann ist Ri−1 = Ri , also Ri
erweiterbar.
2. Fall: Ri−1 ∪ {ei } ist kreisfrei.
Sei ei = (v , w ). Definiere
S := {u ∈ V | im Wald (V , Ri−1 ) ist u von v aus erreichbar}.
(S ist die Zusammenhangskomponente von v in (V , Ri−1 ).)
Weil Ri−1 ∪ {ei } kreisfrei ist, folgt w ∈ V − S.
Klar: Keine Ri−1 -Kante verbindet S und V − S.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
77
Minimale Spannbäume
Algorithmus von Kruskal
Behauptung: c(ei ) ist minimal unter allen c(e 0 )
mit e 0 = (v 0 , w 0 ), v 0 ∈ S, w 0 ∈ V − S.
Beweis indirekt: Annahme: Es gibt e 0 = (v 0 , w 0 ) mit v 0 ∈ S, w 0 ∈ V − S
und c(e 0 ) < c(ei ).
⇒ e 0 = ej mit j < i (weil Kanten nach Kosten aufsteigend sortiert sind)
⇒ e 0 = ej wurde in Runde j < i untersucht.
Weil e 0 zwischen zwei Zusammenhangskomponenten von (V , Ri−1 )
verläuft, ist Ri−1 ∪ {e 0 } kreisfrei.
Rj−1 ⊆Ri−1
=⇒
Rj−1 ∪ {e 0 } ist kreisfrei.
Algo
=⇒ e 0 ∈ Rj ⊆ Ri−1 , Widerspruch.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
78
Minimale Spannbäume
Algorithmus von Kruskal
Gesehen:
c(ei ) ist minimal unter allen c((v , w )), v ∈ S, w ∈ V − S.
Nach der Schnitteigenschaft folgt: Ri = Ri−1 ∪ {ei } ist erweiterbar,
und das ist die Induktionsbehauptung.
Ende des Korrektheitsbeweises für den Algorithmus von Kruskal.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
79
Union-Find
Einführung
4.4 Hilfsstruktur: Union-Find
Union-Find-Datenstrukturen dienen als Hilfsstruktur für verschiedene
Algorithmen, insbesondere für den Algorithmus von Kruskal.
Diese Verfahren haben zudem eine instruktive Analyse.
Eine Zwischenkonfiguration im Algorithmus von Kruskal besteht in einer
Menge R ⊆ E von Kanten, die einen Wald bilden, sowie einer Folge
ei , . . . , em von noch zu verarbeitenden Kanten.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
80
Union-Find
Einführung
4.4.1 Algorithmus von Kruskal mit Union-Find
3
1
5
2
1
5
3
8
1
2
2
6
3
4
1
3
9
1
4
4
5
7
3
1
2
1
3
2
10
2
11
Die im Algorithmus von Kruskal zu lösende Aufgabe: zu zwei Knoten i und
j entscheide, ob es in (V , R) einen Weg von i nach j gibt. (Hier: von 6
nach 2.)
Möglich, aber ungeschickt: Jedesmal Tiefensuche o.ä.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
81
Union-Find
Einführung
Ansatz: Repräsentiere die Knotenmengen, die den
Zusammenhangskomponenten von (V , R) entsprechen.
Im Beispielbild:
{1}, {2, 3, 5, 7, 9}, {4}, {6}, {8, 11}, {10}.
Es soll dann schnell zu ermitteln sein, ob zwei Knoten in derselben
Komponente (Menge) liegen.
Wenn wir einen Schritt des Kruskal-Algorithmus ausführen, bei dem eine
Kante akzeptiert, also in R aufgenommen wird, müssen wir zwei der
disjunkten Mengen vereinigen.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
82
Union-Find
3
1
5
2
1
5
3
8
1
1
2
2
6
3
4
1
3
9
1
4
4
5
7
3
1
2
Einführung
3
2
10
2
11
Neue Mengen:
{1}, {2, 3, 5, 6, 7, 9}, {4}, {8, 11}, {10}.
Auch diese Operation sollte schnell“ durchführbar sein.
”
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
83
Union-Find
Einführung
Abstrakte Aufgabe:
Verwalte eine dynamische Partition“ der Menge {1, 2, . . . , n} unter
”
Operationen
init
union
find
(Anfangsinitialisierung)
(Vereinigung von Klassen)
( In welcher Klasse liegt i?“).
”
Eine Partition ist dabei eine Zerlegung von {1, 2, . . . , n} in Mengen
{1, 2, . . . , n} = S1 ∪ S2 ∪ · · · ∪ S` ,
wobei S1 , S2 , . . . , S` disjunkt sind.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
84
Union-Find
Einführung
Beispiel: n = 12.
1 2
4 5
7 3
9 12
8
6
10
11
Eine Klasse in einer solchen Partition ist eine dieser Mengen S1 , . . . , S` .
Erinnerung: Eine Partition ist gleichbedeutend mit einer
Äquivalenzrelation ∼ über {1, . . . , n}, wobei i ∼ j einfach heißt, dass i, j
in derselben Menge liegen. Die Mengen sind dann genau die
Äquivalenzklassen zu ∼.
(Bitte die Wörter Klasse“ und Partition“ nicht verwechseln, auch wenn
”
”
im Computerjargon bei Festplatten Partition“ im Sinn von ein Teil von
”
”
mehreren“ verwendet wird.)
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
85
Union-Find
Einführung
Wir betrachten dynamische Partitionen, die durch Operationen
veränderbar sind.
In jeder Klasse K der Partition soll ein Repräsentant r ∈ K ausgezeichnet
sein. Dieser fungiert als Name von K . Wir schreiben Kr für die Klasse mit
dem Repräsentanten r .
Beispiel: Für n = 12 bilden die folgenden 5 Klassen eine Partition mit
Repräsentanten:
Name
Klasse
Repräsentant
K4
{1, 2, 4, 5}
4
7 3
1 2
8
K6
{6, 8}
6
11
10
4 5
9 12
6
K7
{3, 7, 9, 12}
7
K4
K7
K 6 K 10 K 11
K10
{10}
10
K11
{11}
11
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
86
Union-Find
Einführung
Eine solche Partition mit Repräsentanten ist mathematisch vollständig
beschrieben durch eine Funktion
r : {1, . . . , n} → {1, . . . , n}
mit folgender Eigenschaft:
für alle i ∈ {1, . . . , n}.
r (r (i)) = r (i),
Das Element i gehört zur Klasse Kr (i) = {j | r (i) = r (j)}; diese hat den
gemeinsamen Wert r (i) als Repräsentanten.
Im Beispiel sieht r folgendermaßen aus:
i
r (i)
FG KTuEA, TU Ilmenau
1
4
2
4
3
7
4
4
5
4
6
6
7
7
8
6
9
7
Effiziente Algorithmen – Sommersemester 2012
10
10
11
11
12
7
87
Union-Find
Einführung
Operationen:
init(n)
find(i)
union(s, t)
Erzeugt zu n ≥ 1
die diskrete
”
Partition“ mit den
n einelementigen
Klassen
{1}, {2}, . . . , {n},
also Ki = {i}.
Gibt zu
i ∈ {1, . . . , n} den
Namen r (i) der
Klasse Kr (i) aus,
in der sich i
(gegenwärtig)
befindet.
Die Argumente s und t
müssen Repräsentanten
verschiedener Klassen
Ks bzw. Kt sein. Die
Operation ersetzt in der
Partition Ks und Kt
durch Ks ∪ Kt . Als
Repräsentant von
Ks ∪ Kt kann entweder s
oder t verwendet
werden.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
88
Union-Find
Einführung
Im obigen Beispiel werden durch union(4, 10) die Klassen K4 = {1, 2, 4, 5}
und K10 = {10} entfernt und eine neue Klasse
0
K10
= {1, 2, 4, 5, 10}
hinzugefügt.
Aus Sicht der r -Funktion entspricht diese Operation der Änderung der
Funktionswerte auf der KlasseKs :
t , falls r (i) = s,
r 0 (i) :=
r (i) , sonst.
Im Beispiel: Die r -Werte in S4 werden von 4 auf 10 geändert, die anderen bleiben
gleich.
i
r 0 (i)
1
10
FG KTuEA, TU Ilmenau
2
10
3
7
4
10
5
10
6
6
7
7
8
6
Effiziente Algorithmen – Sommersemester 2012
9
7
10
10
11
11
12
7
89
Union-Find
Einführung
Nützliche abgeleitete Operationen:
same class(i, j):
Liefert true, wenn find(i) = find(j), false
sonst.
test and union(i, j):
Berechnet s = find(i) und t = find(j) und
führt dann union(s, t) aus, falls s 6= t ist.
Zwei Implementierungsmöglichkeiten:
1) Arrays mit Listen (erlaubt schnelle finds)
2) Wurzelgerichteter Wald (erlaubt schnelle unions)
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
90
Union-Find
Einführung
Algorithmus von Kruskal mit Union-Find
Input: Gewichteter zusammenhängender Graph G = (V , E , c) mit
V = {1, . . . , n}.
1. Schritt: Sortiere die Kanten e1 , . . . , em nach den Gewichten
c1 = c(e1 ), . . . , cm = c(em ) aufsteigend.
Resultat: Sortierte Kantenliste (v1 , w1 , c1 ), . . . , (vm , wm , cm ).
2. Schritt: Initialisiere Union-Find-Struktur für {1, . . . , n}.
3. Schritt: Für i = 1, 2, . . . , m tue folgendes:
s ← find(vi ); t ← find(wi );
if s 6= t then begin R ← R ∪ {ei }; union(s, t) end;
(∗ Optional: Beende Schleife, wenn |R| = n − 1. ∗)
4. Schritt: Die Ausgabe ist R.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
91
Union-Find
Einführung
Satz 4.4.1
(a) Der Algorithmus von Kruskal in der Implementierung mit Union-Find
ist korrekt.
(b) Die Laufzeit des Algorithmus ist O(m log n), wenn man die
Union-Find-Struktur mit einem wurzelgerichteten Wald (mit
Pfadkompression) implementiert.
(c) Die Laufzeit des Algorithmus ist O(m log n), wenn man die
Union-Find-Struktur mit Arrays implementiert.
Bem.: Dieser Satz wird noch präzisiert.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
92
Union-Find
Einführung
Beweis: (a) (Korrektheit) Man zeigt durch Induktion über die Runden,
dass nach i Runden die Klassen der Union-Find-Struktur genau die
Zusammenhangskomponenten des Graphen (V , {e1 , . . . , ei }) sind, und dies
sind die Knotenmengen der Bäume im Wald (V , Ri ) (Inhalt von R nach i
Durchläufen).
Daher testet s ← find(vi ); t ← find(wi ); if s 6= t . . .“ korrekt die
”
Kreiseigenschaft.
(b), (c): Später.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
93
Union-Find
Array-Implementierung
4.4.2 Arrayimplementierung von Union-Find
Array r[1..n] enthält in r[i] den Repräsentanten r (i) der Klasse Kr (i) , in
der i gegenwärtig liegt. In unserem Beispiel sähe r folgendermaßen aus:
1 2 3 4 5 6 7 8 9 10 11 12
r : 4 4 7 4 4 6 7 6 7 10 11
7
Offensichtlich: find(i) in Zeit O(1) ausführbar.
Naiver Ansatz für union(s, t): Durchlaufe das Array r und ändere dabei
alle s“ in t“ (oder umgekehrt). Kosten: Θ(n). – Dies kann man
”
”
verbessern.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
94
Union-Find
Array-Implementierung
Trick 1:
Halte die Elemente jeder Klasse Ks (mit Repräsentant s)
in einer linearen Liste Ls .
Dann muss man bei der Umbenennung von s“ in t“ nur Ls durchlaufen –
”
”
die Kosten betragen Θ(|Ks |). Aus Listen Ls und Lt wird eine neue Liste L0t .
Trick 2:
Bei der Vereinigung von Klassen sollte man den Repräsentanten der
größeren Klasse übernehmen und den der kleineren ändern, da damit die
Zahl der Änderungen kleiner gehalten wird. – Hierzu muss man die
Listenlängen/Klassengrößen in einem zweiten Array size[1..n] halten.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
95
Union-Find
Im Beispiel:
Array-Implementierung
1 2 3 4 5 6 7 8 9 10 11 12
r : 4 4 7 4 4 6 7 6 7 10 11
1
2
3
4
5
6 7
8
9
size: − − − 4 − 2 4 − −
7
10 11 12
1
1
−
Nur die Einträge size[s] mit r (s) = s, also s Repräsentant, sind relevant.
Die anderen Einträge (mit −“ gekennzeichnet) sind unwesentlich.
”
Listen: L4 = (4, 2, 1, 5), L7 = (7, 3, 12, 9), L6 = (6, 8),
L10 = (10), L11 = (11).
Für eine besonders sparsame Implementierung dieser Listen ist es nützlich,
wenn Ls mit dem Repräsentanten s beginnt.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
96
Union-Find
Array-Implementierung
Beispiel:
union(7, 6): Weil size[6] < size[7], werden die Einträge r[i] für i in
L6 auf 7 geändert und L6 = (6, 8) wird unmittelbar nach dem ersten
Element 7 in L7 eingefügt:
1 2 3 4 5 6 7 8 9 10 11 12
r : 4 4 7 4 4 7 7 7 7 10 11
1
2
3
4
5
6
7
8
9
size: − − − 4 − − 6 − −
7
10 11 12
1
1
−
Aus L7 = (7, 3, 12, 9) und L6 = (6, 8) wird die neue Liste
L7 = (7, 6, 8, 3, 12, 9) gebildet.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
97
Union-Find
Array-Implementierung
Zeitaufwand: Θ(Länge der kleineren Liste) für die Umbenennungen im
Array r und O(1) für das Ändern des Eintrags size[t] sowie das
Kombinieren der Listen.
Zur Effizienzverbesserung (um konstanten Faktor) und zur Platzersparnis
können wir noch folgenden Trick benutzen. Offensichtlich haben alle
Listen zusammen stets die Einträge 1, 2, . . . , n. Daher sind keine
dynamisch erzeugten Listenelemente nötig, sondern wir können alle Listen
kompakt in einem Array speichern:
next[1..n]: integer mit
j, falls j Nachfolger von i in einer der Listen Ls ,
next[i] =
0, falls j letzter Eintrag in seiner Liste Lr (j) .
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
98
Union-Find
Array-Implementierung
Beispiel:
1
2
3
4
5
6 7 8 9
10
11
12
next: · · · · · · 12 · · · · · · 8 6 3 0 · · · · · ·
9
Darstellung von L7 = (7, 6, 8, 3, 12, 9).
(Hier sieht man, wieso Ls mit s beginnen muss.)
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
99
Union-Find
Array-Implementierung
Implementierung der Operationen im Detail:
Prozedur init(n) (∗ Initialisierung einer Union-Find-Struktur ∗)
(1) Erzeuge r, size, next: Arrays der Länge n für int-Einträge
(2) for i from 1 to n do
(3)
r[i] ← i;
(4)
size[i] ← 1;
(5)
next[i] ← 0.
Zeitaufwand: Θ(n).
Prozedur find(i)
(1) return r[i].
Zeitaufwand: O(1).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
100
Union-Find
Array-Implementierung
Prozedur union(s, t)
(∗ Ausgangspunkt: s, t sind verschiedene Repräsentanten ∗)
(1) if size[s] > size[t] then vertausche s, t;
(2)
(∗ nun: size[s] ≤ size[t] ∗)
(3) z ← s;
(4) repeat size[s] − 1 times (∗ Durchlauf Ls ∗)
(5)
r[z] ← t;
(6)
z ← next[z];
(7)
(∗ nun: z enthält letztes Element von Ls ; next[z] = 0 ∗)
(8) r[z] ← t;
(9) (∗ Ls nach dem ersten Eintrag in Lt einhängen: ∗)
(10) next[z] ← next[t]; next[t] ← s;
(11) size[t] ← size[t] + size[s].
Aufwand: O(Länge der kürzeren der Listen Ls , Lt ).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
101
Union-Find
Array-Implementierung
Rechenaufwand: init(n) benötigt Zeit Θ(n) und find(i) benötigt Zeit
O(1).
Behauptung: n − 1 union-Aufrufe (mehr kann es nicht geben!) benötigen
Zeit O(n log n).
Wir behaupten nicht, dass jeder einzelne union-Aufruf Zeit O(log n) benötigt
(hierfür kann man leicht Gegenbeispiele konstruieren) – aber in der Summe
entfällt auf jeden solchen Aufruf ein Anteil von O(log n):
Amortisierte Analyse“
”
Wir nummerieren die union-Aufrufe mit p = 1, 2, . . . , n − 1 durch. Die
beiden in Aufruf Nummer p vereinigten Klassen seien Kp,1 und Kp,2 , wobei
|Kp,1 | ≤ |Kp,2 | gelten soll.
Für den p-ten union-Aufruf veranschlagen wir Kosten |Kp,1 |.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
102
Union-Find
Array-Implementierung
Dann ist der Gesamt-Zeitaufwand für alle unions zusammen
X
O(n) + O
|Kp,1 | .
1≤p<n
Wir wollen
P
1≤p<n
FG KTuEA, TU Ilmenau
|Kp,1 | abschätzen.
Effiziente Algorithmen – Sommersemester 2012
103
Union-Find
Array-Implementierung
Trick: Ermittle den Beitrag zu dieser Summe aus Sicht der einzelnen
Elemente i.
Für 1 ≤ i ≤ n, 1 ≤ p < n definiere:
1 falls i ∈ Kp,1 ,
ai,p :=
0 sonst.
Dann erhält man durch Umordnen der Summation:
X
X X
X X
|Kp,1 | =
ai,p =
ai,p .
1≤p<n
FG KTuEA, TU Ilmenau
1≤p<n 1≤i≤n
1≤i≤n 1≤p<n
Effiziente Algorithmen – Sommersemester 2012
104
Union-Find
Array-Implementierung
P
In der letzten inneren Summe ci = 1≤p<n ai,p wird für jedes
i ∈ {1, . . . , n} gezählt, wie oft es bei union-Operationen in der kleineren
Menge Kp,1 enthalten ist (d. h. wie oft insgesamt der Repräsentant seiner
Klasse gewechselt hat).
Bei jeder union-Operation, bei der i in der kleineren Klasse ist, wird die
Größe der Klasse mindestens verdoppelt, startend mit der Klasse {i}. Da
Klassen nicht größer als n werden können, gilt 2ci ≤ n, also ci ≤ log n, also
X
X
ci ≤ n log n.
|Kp,1 | =
1≤p<n
1≤i≤n
Damit ist die Behauptung bewiesen.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
105
Union-Find
Array-Implementierung
Satz 4.4.4
In der Implementierung der Union-Find-Struktur mit
Arrays hat jede find-Operation Laufzeit O(1),
n − 1 union-Operationen haben Laufzeit O(n log n).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
106
Union-Find
Baumimplementierung
4.4.3 Baumimplementierung von Union-Find
Eine alternative Implementierung der Union-Find-Datenstruktur verwendet
einen wurzelgerichteten Wald.
Beispiel: Partition {1, 2, 4, 5}, {3, 7, 9, 12}, {6, 8}, {10}, {11} wird
dargestellt durch:
7
4
2
1
FG KTuEA, TU Ilmenau
9
5
6
12
10
11
8
3
Effiziente Algorithmen – Sommersemester 2012
107
Union-Find
Baumimplementierung
7
4
2
1
9
5
6
12
10
11
8
3
Für jede Klasse Ks gibt es genau einen Baum Bs .
Jedes Element i ∈ Ks ist Knoten im Baum.
Es gibt nur Zeiger in Richtung auf die Wurzel zu: p(i) ist der Vorgänger
von i; die Wurzel ist der Repräsentant s; die Wurzel zeigt auf sich selbst
als Vorgänger“: p(i) = i genau dann wenn i Repräsentant ist.
”
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
108
Union-Find
Baumimplementierung
Leicht einzusehen :
Eine Funktion p : {1, . . . , n} → {1, . . . , n} stellt einen wurzelgerichteten
Wald dar
genau dann wenn
für jedes i die Folge i0 = i, i1 = p(i0 ), . . . , il = p(il−1 ), . . . schließlich ein il
mit p(il ) = il erreicht
(d. h.: die Vorgängerzeiger bilden keine Kreise der Länge > 1).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
109
Union-Find
Baumimplementierung
Eine kostengünstige Darstellung eines solchen wurzelgerichteten Waldes
benutzt nur ein Array p[1..n]: array of int; für Knoten i gibt der
Eintrag p[i] den Vorgängerknoten p(i) an.
Das dem Wald im Beispiel entsprechende Array sieht so aus:
1 2
3
4 5 6 7 8 9 10 11 12
p : 2 4 12 4 2 6 7 6 7 10 11
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
7
110
Union-Find
Baumimplementierung
Implementierung von find(i):
Prozedur find(i)
(1) j ← i;
(∗ verfolge Vorgängerzeiger bis zur Wurzel ∗)
(2) jj ← p[j];
(3) while jj 6= j do
(4)
j ← jj;
(5)
jj ← p[j] ;
(6) return j.
Zeitaufwand: Θ(depth(i)) = Θ(Tiefe von i in seinem Baum).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
111
Union-Find
Baumimplementierung
union(s, t): Gegeben: Verschiedene Repräsentanten s und t.
Man macht einen der Repräsentanten zum Sohn des anderen, setzt also
p(s) := t oder p(t) := s.
s
s
+
?
t
=
t
Welche der beiden Optionen sollte man nehmen?
Ungeschickt: union(i, i + 1), i = 1, . . . , n − 1, dadurch ausführen, dass
man nacheinander p(i) := i + 1 ausführt.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
112
Union-Find
Baumimplementierung
Dies führt zu dem Baum
n
n −1
2
1
find-Operationen sind nun teuer! Ein find(i) für jedes i ∈ {1, . . . , n} hat
Gesamtkosten Θ(n2 )!
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
113
Union-Find
Baumimplementierung
Trick: Führe für jeden Knoten i eine Zahl rank(i) (Rang) mit, in einem
Array rank[1..n]: array of int, mit
rank[i] = Tiefe des Teilbaums mit Wurzel i.
2
2
4
1 6
7
10
0
1 2
0
1
0
0 5
9
1 12
0
8
11
0
0
3
Bei union(s, t) machen wir die Wurzel desjenigen Baums zur Wurzel der
Vereinigung, die den größeren Rang hat ( union by rank“). Bei gleichen
”
Rängen ist die Reihenfolge gleichgültig – (genau) in diesem Fall steigt
der Rang des Wurzelknotens an.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
114
Union-Find
Baumimplementierung
Prozedur init(n) (∗ Initialisierung ∗)
(1) Erzeuge p, rank: Arrays der Länge n für int-Einträge
(2) for i from 1 to n do
(3)
p[i] ← i; rank[i] ← 0;
(∗ n Bäume mit je einem (Wurzel-)Knoten vom Rang 0 ∗)
Zeitaufwand: Θ(n).
Prozedur union(s, t)
(∗ Ausgangspunkt: s, t sind verschiedene Repräsentanten von Klassen ∗)
(∗ D.h.: p[s] = s und p[t] = t ∗)
(1) if rank[s] > rank[t]
(2)
then p[t] ← s
(3) elseif rank[t] > rank[s]
(4)
then p[s] ← t
(5) else p[t] ← s; rank[s] ← rank[s] + 1.
Zeitaufwand: O(1).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
115
Union-Find
Baumimplementierung
Beispiele:
union(4, 7) würde liefern:
3
4
1 6
10
0
1 2
0
1
2
0 5
0
9
8
7
1 12
0
FG KTuEA, TU Ilmenau
union(6, 7) würde liefern:
2
11
2
4
7
10
0
0
1 2
0
0
1
0
0 5
9
1 12
1 6
3
0 8
0
11
0
3
Effiziente Algorithmen – Sommersemester 2012
116
Union-Find
Baumimplementierung
Satz 4.4.2
Die eben beschriebene Implementierung einer Union-Find-Struktur mit
einem wurzelgerichteten Wald hat folgende Eigenschaften:
a) Sie ist korrekt (d. h. hat das vorgesehene
Ein-/Ausgabeverhalten).
b) init(n) benötigt Zeit Θ(n);
find(i) benötigt Zeit O(log n);
union(s, t) benötigt Zeit O(1).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
117
Union-Find
Baumimplementierung
Beweis:
(a) Betrachte eine Operationenfolge Op0 = init(n), Op1 , . . . , Opk , wobei
die letzten k Operationen legale union-Operationen sind.
Man beweist (leicht) durch Induktion über `, dass nach init(n) und nach
jedem Op` gilt:
Das p-Array stellt diejenige Partition als wurzelgerichteten Wald dar, die
von Op0 = init(n), Op1 , . . . , Op` erzeugt wird. Weiter ist size[t] = |Kt |
für jeden Repräsentanten t.
Aus der Behauptung folgt dann auch, dass die find-Operationen immer die
korrekte Ausgabe liefern.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
118
Union-Find
Baumimplementierung
Beweis: (Fortsetzung)
(b) Wir beobachten:
Fakt 1
i 6= p(i) ⇒ rank(i) < rank(p(i)).
Beweis: Wenn eine Wurzel s Kind von t wird, dann ist entweder
rank(s) < rank(t) (und das bleibt so) oder es ist rank(s) = rank(t), und
der Rang von t erhöht sich nun um 1.
Bei Knoten, die keine Wurzeln mehr sind, ändern sich Vorgänger und
Ränge nicht mehr.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
119
Union-Find
Baumimplementierung
Fakt 2
Wenn s Wurzel des Baums Bs ist und h = rank(s) gilt, dann enthält Bs
mindestens 2h Knoten.
Beweis: Ein Knoten vom Rang h entsteht, wenn zwei Bäume vereinigt
werden, deren Wurzeln beide Rang h − 1 haben. Daraus folgt Fakt 2 leicht
durch Induktion über h = 0, 1, . . ..
Fakt 3
Bei n Knoten insgesamt gibt es höchstens n/2h Knoten von Rang h.
Beweis: Wegen Fakt 1 können Knoten mit Rang h nicht Nachfahren
voneinander sein. Also sind alle Unterbäume, deren Wurzeln Rang h
haben, disjunkt. Aus Fakt 2 folgt leicht, dass auch für Knoten i im Inneren
von Bäumen mit rank(i) = h gilt, dass ihr Unterbaum mindestens 2h
Knoten hat. Also kann es nicht mehr als n/2h solche Knoten geben.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
120
Union-Find
Baumimplementierung
Aus Fakt 3 folgt, dass es keine Knoten mit Rang größer als log n geben
kann.
Also hat kein Baum Tiefe größer als log n, also kosten find-Operationen
maximal Zeit O(log n).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
121
Union-Find
Baumimplementierung
Pfadkompression
Eine interessante Variante der Union-Find-Datenstruktur, die mit einem
wurzelgerichteten Wald implementiert ist, ist der Ansatz der
Pfadkompression“ (oder Pfadverkürzung“).
”
”
Bei einem find(i) muss man den ganzen Weg von Knoten i zu seiner
Wurzel r (i) ablaufen; Aufwand O(depth(i)).
Ein gutes Ziel ist also, diese Wege möglichst kurz zu halten.
Idee: Man investiert bei find(i) etwas mehr Arbeit, jedoch immer noch im
Rahmen O(depth(i)), um dabei die Wege zu verkürzen und damit
spätere finds billiger zu machen.
Jeder Knoten i = i0 , i1 , . . . , id−1 auf dem Weg von i zur Wurzel id = r (i)
wird direkt als Kind an die Wurzel gehängt. Kosten pro Knoten/Ebene:
O(1), insgesamt also O(depth(i)).
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
122
Union-Find
Baumimplementierung
Beispiel: d = depth(i) = 4.
r
i3
T4
T3
r
T4
i2
i0
T2
i1
T0
i1
T1
i3
i2
T2
T3
T1
i0
T0
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
123
Union-Find
Baumimplementierung
Die neue Version der find-Operation:
Prozedur find(i) (∗ Pfadkompressions-Version ∗)
(1)
j ← i;
(∗ verfolge Vorgängerzeiger bis zur Wurzel r (i): ∗)
(2)
jj ← p[j];
(3)
while jj 6= j do
(4)
j ← jj;
(5)
jj ← p[j] ;
(6)
r ← j; (∗ r enthält nun Wurzel r (i) ∗)
(∗ Erneuter Lauf zur Wurzel, Vorgängerzeiger umhängen: ∗)
(7)
j ← i;
(8)
jj ← p[j];
(9)
while jj 6= j do
(10)
p[j] ← r;
(11)
j ← jj;
(12)
jj ← p[j];
(13) return r.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
124
Union-Find
Baumimplementierung
Beispiel:
7
7
9
12
4
3
2
1
8
find (8)
6
5
10
2
1
4
9
12
3
11
5
6
8
10
11
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
125
Union-Find
Baumimplementierung
Analyse: Interessant, neue Technik.
Die union-Operationen werden exakt wie bisher durchgeführt.
Beobachtungen: (1) Die Werte im rank-Array werden aktualisiert wie
vorher. Man kann sie aber nicht mehr als Baumtiefen interpretieren.
(2) Nach (1) fällt bei einer union-Operationen die Entscheidung, welcher
Knoten Kind eines anderen wird, exakt wie im Verfahren ohne
Pfadkompression. Daher verändert die Pfadkompression die Knotenmengen
der Bäume (also die Klassen) und die Repräsentanten nicht.
Aus (1) und (2) folgt, dass Fakt 2 weiterhin gilt.
Feinheit: Nicht-Wurzeln vom Rang h können – weil bei der Pfadkompression
Kindknoten abgehängt werden können – weniger als 2h Nachfahren haben.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
126
Union-Find
Baumimplementierung
(3) Wenn ein Knoten i Kind eines anderen wird (also aufhört,
Repräsentant zu sein), dann ändert sich sein Rang, wie in rank[i]
gespeichert, nie mehr. Dies gilt in der Version ohne und in der Version
mit Pfadkompression. Diesen Wert werden wir als den endgültigen“
”
Rang von i ansehen.
Die Rang-Werte der Nicht-Wurzeln sind dann in der Version mit und in
der Version ohne Pfadkompression identisch.
Solange ein Knoten Wurzel ist, ist sein Rang nicht endgültig, aber in beiden
Versionen gleich.
Insbesondere: Fakt 3 gilt weiterhin.
(4) Weder union- noch find-Operationen (mit Pfadkompression) ändern
etwas an Fakt 1: Ränge wachsen strikt von unten nach oben entlang der
Wege in den Bäumen.
(Beweis durch Induktion über ausgeführte Operationen, Übung.)
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
127
Union-Find
Baumimplementierung
F (i) := 2F (i−1) , für i ≥ 1.
Definition: F (0) := 1,
F (i) ist die Zahl, die von einem Zweierpotenzturm“ der Höhe i berechnet
”
wird:
2
F( i ) = 2
2
2
2
i Zweien
Beispielwerte:
i
F (i)
FG KTuEA, TU Ilmenau
0
1
1
2
2
4
3
16
4
5
6
65536
265536
65536
2
2
Effiziente Algorithmen – Sommersemester 2012
128
Union-Find
Baumimplementierung
Definition: log∗ n := min{k | F (k) ≥ n}.
Leicht zu sehen: log∗ n = min{k | log log . . . log n ≤ 1}.
{z
}
|
k
Nun teilen wir die Knoten j mit Rang > 0 (Nicht-Blätter, Nicht-Wurzeln)
in Ranggruppen“ G0 , G1 , G2 , . . . ein:
”
Knoten j gehört zu Ranggruppe Gk ,
wenn F (k − 1) < rank(j) ≤ F (k) gilt.
G0 : Rang 1; G1 : Ränge 2, 3, 4; G2 : Ränge 5, . . . , 16;
G3 : Ränge 17, . . . , 65536; etc.
Wenn j in Ranggruppe Gk ist, folgt (mit Fakt 3):
F (k) = 2F (k−1) < 2rank(j) ≤ 2log n = n.
Also: k < log∗ n, d. h. es gibt maximal log∗ n nichtleere Ranggruppen.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
129
Union-Find
Baumimplementierung
Beachte: 265536 > 10006553 > 1019500 ; Werte n mit log∗ n > 5 kommen in
wirklichen algorithmischen Anwendungen nicht vor.
⇒ Man wird nie mehr als fünf nichtleere Ranggruppen sehen.
(Es gilt aber: limn→∞ log∗ n = ∞.)
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
130
Union-Find
Baumimplementierung
Wir wollen nun die Kosten von m find-Operationen berechnen. Bei find(i)
läuft man einen Weg i = i0 , i1 = p(i0 ), i2 = p(i1 ), . . . , id = p(id−1 )
entlang, von i bis zur Wurzel r (i) = id . – Beispiel: d = 7.
i7
i6
T7
i5
T6
T5
i4
T4
i3
i2
T3
i1
T2
T1
i0
T0
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
131
Union-Find
Baumimplementierung
Wir veranschlagen Kosten d + 1 für diese Operation, also 1 für jeden
Knoten j auf dem Weg.
Die Rechenzeit für alle m finds zusammen ist dann
O(Summe aller Kosten“).
”
Für die Kostenanalyse benutzen wir die sogenannte
Bankkontomethode, in einer anschaulichen Form.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
132
Union-Find
Baumimplementierung
Ein Knoten in Ranggruppe Gk bekommt F (k) e Taschengeld, in dem
Moment, in dem er aufhört, Repräsentant/Baumwurzel zu sein, sein Rang
also endgültig feststeht.
In Gk sind Knoten mit Rängen F (k − 1) + 1, . . . , F (k). Nach Fakt 1 gilt:
|Gk | ≤
n
2F (k−1)+1
+
n
2F (k−1)+2
+ ··· +
n
2F (k)
<
n
2F (k−1)
n
.
=
F (k)
Also bekommen alle Knoten in Gk zusammen höchstens n e, und in allen
höchstens log∗ n Ranggruppen zusammen werden nicht mehr als n log∗ n e
als Taschengeld ausgezahlt.
Dies ist ein entscheidender Punkt in der Analyse: Die Ranggruppen sind
sehr klein, daher kann man sich ein großzügiges Taschengeld leisten.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
133
Union-Find
Baumimplementierung
Weiter legen wir m(2 + log∗ n) e in einer Gemeinschaftskasse“ bereit.
”
Ziel: Wir wollen zeigen, dass das Taschengeld und das Geld in der
Gemeinschaftskasse zusammen ausreichen, um die Kosten aller m
find-Operationen zu bestreiten.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
134
Union-Find
Baumimplementierung
Wir betrachten eine find(i)-Operation. Jeder Knoten j auf dem Weg
verursacht Kosten 1.
Ranggruppe:
i7
_
3
i6
T7
3
i5
T6
T5
2
i4
T4
2
i3
1
i2
T3
0
i1
T2
T1
Es gibt drei Fälle.
FG KTuEA, TU Ilmenau
i0
_
T0
Effiziente Algorithmen – Sommersemester 2012
135
Union-Find
Baumimplementierung
Ranggruppe:
i7
_
GK (i)
GK (i)
3
i6
T7
3
i5
T6
T5
2
i4
T4
2
i3
1
i2
T3
0
i1
T2
T1
i0
_
T0
(i) j ist Wurzel oder ist der vorletzte Knoten auf dem Weg von i nach
r (i).
Die Kosten von j bestreiten wir aus der Gemeinschaftskasse.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
136
Union-Find
Baumimplementierung
Ranggruppe:
i7
_
GK (i)
GK (i)
3
i6
T7
3
i5
T6
T5
2
i4
T4
2
i3
1
i2
T3
0
i1
T2
T1
i0
_
T0
Dieser Fall kostet maximal 2 e für die find(i)-Operation.
Ab hier: j ist nicht Wurzel oder vorletzter Knoten.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
137
Union-Find
Baumimplementierung
Ranggruppe:
i7
_
3
i6
T7
3
i5
T6
T5
GK (ii)
2
i4
T4
2
i3
1
i2
T3
GK (ii)
0
i1
T2
T1
i0
GK (ii)
_
GK (ii)
T0
(ii) rank(j) = 0 oder p(j) ist in einer höheren Ranggruppe als j.
Für die Kosten dieser j bezahlen wir mit (höchstens) log∗ n e aus der
Gemeinschaftskasse.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
138
Union-Find
Baumimplementierung
Ranggruppe:
i7
_
3
i6
T7
3
i5
T6
T5
GK (ii)
2
i4
T4
2
i3
1
i2
T3
GK (ii)
0
i1
T2
T1
i0
GK (ii)
_
GK (ii)
T0
Von diesen j’s kann es höchstens log∗ n viele geben, weil die Ränge entlang
des Weges strikt wachsen (Fakt 1) und es nur log∗ n Ranggruppen gibt.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
139
Union-Find
Baumimplementierung
Ranggruppe:
i7
_
3
i6
T7
TG (iii)
3
i5
T6
T5
2
i4
T4
2
i3
TG (iii)
1
i2
T3
0
i1
T2
T1
i0
_
T0
(iii) p(j) ist in derselben Ranggruppe wie j.
In diesem Fall muss j mit 1 e aus seinem Taschengeld bezahlen.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
140
Union-Find
Baumimplementierung
Nun wird abgerechnet:
(A) Die Gesamtkosten aus Fällen (i) und (ii), summiert über alle m finds,
betragen höchstens m(2 + log∗ n); der Inhalt der Gemeinschaftskasse
reicht.
(B) Wie steht es mit Fall (iii) und dem Taschengeld?
Wir betrachten nun (Trick!) einen festen Knoten j.
Immer wenn j bezahlen muss, nimmt er an einer Pfadverkürzung teil. Sein
neuer Vorgängerknoten p 0 (j) wird seine aktuelle Wurzel (verschieden vom
bisherigen Vorgänger p(j)). Wegen Fakt 1 hat der neue Vorgänger p 0 (j)
einen höheren Rang als der alte.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
141
Union-Find
Baumimplementierung
Sei Gk die Ranggruppe von j. In dieser Ranggruppe gibt es nicht mehr als
F (k) verfügbare Rang-Werte. Daher ändert sich der Vorgänger von j
weniger als F (k)-mal, bevor ein Knoten aus einer höheren Ranggruppe
Vorgänger von j wird (und alle späteren Vorgänger ebenfalls aus höheren
Ranggruppen kommen).
Also tritt Situation (iii) für Knoten j weniger als F (k)-mal ein. Daher
reicht das Taschengeld von Knoten j aus, um für diese Situationen zu
bezahlen.
Die Gesamtkosten aus Fall (iii), summiert über alle j’s, betragen also nicht
mehr als das gesamte Taschengeld, also höchstens n log∗ n.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
142
Union-Find
Baumimplementierung
Satz 4.4.3
In der Implementierung der Union-Find-Struktur mit
wurzelgerichteten Wäldern und Pfadkompression haben
m find-Operationen insgesamt Kosten von maximal
(2 + m + n) log∗ n, benötigen also Rechenzeit
O((m + n) log∗ n).
Jede einzelne union-Operation benötigt Zeit O(1).
Bemerkung:
(a) Da für real vorkommende n die Zahl log∗ n nicht größer als 5 ist, wird
man in der Praxis eine Laufzeit beobachten, die linear in n + m ist.
(b) Implementierungen von Union-Find-Strukturen als wurzelgerichteter
Wald mit Pfadkompression verhalten sich in der Praxis sehr effizient.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
143
Union-Find
Baumimplementierung
Satz 4.4.1 (Vollversion)
(a) Der Algorithmus von Kruskal in der Implementierung mit Union-Find
ist korrekt.
(b) Die Laufzeit des Algorithmus ist O(m log m) + O(m) + O(n log n), also
O(m log n), wenn man die Union-Find-Struktur mit Arrays implementiert.
(c) Die Laufzeit des Algorithmus ist O(m log m) + O(m log∗ n), also
O(m log n), wenn man die Union-Find-Struktur mit einem
wurzelgerichteten Wald mit Pfadkompression implementiert.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
145
Union-Find
Baumimplementierung
Beweis: (a) haben wir schon gesehen.
(b), (c): Der erste Term O(m log m) benennt die Kosten für das Sortieren.
Danach sind 2m find-Operationen und n − 1 union-Operationen
durchzuführen. Die Zeiten hierfür in beiden Implementierungen wurden in
den Sätzen 4.4.2, 4.4.3, 4.4.4 begründet.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
146
Union-Find
Baumimplementierung
Bem.: Wenn die Kanten schon sortiert sind oder billig sortiert werden
können (O(m) Zeit, z. B. mittels Radixsort, siehe AuD-Vorlesung),
und G = (V , E ) sehr wenige Kanten hat, ergeben sich noch günstigere
Laufzeiten:
Union-Find mit Arrays und Listen liefert Laufzeit O(m + n log n): das ist
linear, sobald m = Ω(n log n) ist, also für nicht ganz dünn besetzte Graphen;
Union-Find mit Pfadkompression liefert Laufzeit O(m log∗ n): fast linear.
Beachte aber: Für m = Ω(n log n) bietet der Algorithmus von Jarnı́k/Prim mit
Fibonacci-Heaps lineare Laufzeit ohne Annahmen über die Sortierkosten.
FG KTuEA, TU Ilmenau
Effiziente Algorithmen – Sommersemester 2012
147
Herunterladen