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