Generischer MST-Algorithmus T T0 T 00 V1 T 000 V2 Wald F EADS ľErnst W. Mayr 3.2 Generischer minimaler Spannbaum-Algorithmus 445/598 3.3 Kruskal’s Algorithmus algorithm Kruskal (G, w) := sortiere die Kanten nach aufsteigendem Gewicht in eine Liste L initialisiere Wald F = {Ti , i = 1, . . . , n}, mit Ti = {vi } MSB:=∅ for i := 1 to length(L) do {v, w} := Li x := Baum ∈ F , der v enthält; co x :=Find(v) oc y := Baum ∈ F , der w enthält; co y :=Find(w) oc if x 6= y then MSB:=MSB ∪ {{v, w}} Union(x, y) co gewichtete Vereinigung oc fi od EADS ľErnst W. Mayr 3.3 Kruskal’s Algorithmus 446/598 Korrektheit: Falls die Gewichte eindeutig sind (w(·) injektiv), folgt die Korrektheit direkt mit Hilfe der “blauen“ und “roten“ Regel. Ansonsten Induktion über die Anzahl |V | der Knoten: √ Ind. Anfang: |V | klein: Sei r ∈ R, Er := {e ∈ E; w(e) < r}. Es genügt zu zeigen: Sei T1 , . . . , Tk ein minimaler Spannwald für Gr := {V, Er } (d.h., wir betrachten nur Kanten mit Gewicht < r). Sei weiter T ein MSB von G, dann gilt die Hilfsbehauptung: Die Knotenmenge eines jeden Ti induziert in T einen zusammenhängenden Teilbaum, dessen Kanten alle Gewicht < r haben. EADS ľErnst W. Mayr 3.3 Kruskal’s Algorithmus 447/598 Beweis der Hilfsbehauptung: Sei Ti =: (Vi , Ei ). Wir müssen zeigen, dass Vi in T einen zusammenhängenden Teilbaum induziert. Seien u, v ∈ Vi zwei Knoten, die in Ti durch eine Kante e verbunden sind. Falls der Pfad in T zwischen u und v auch Knoten 6∈ Vi enthält (also der von Vi induzierte Teilgraph von T nicht zusammenhängend ist), dann enthält der in T durch Hinzufügen der Kante e entstehende Fundamentalkreis notwendigerweise auch Kanten aus E \ Er und ist damit gemäß der “roten“ Regel nicht minimal! Da Ti zusammenhängend ist, folgt damit, dass je zwei Knoten aus Vi in T immer durch einen Pfad verbunden sind, der nur Kanten aus Er enthält. EADS ľErnst W. Mayr 3.3 Kruskal’s Algorithmus 448/598 Zeitkomplexität: (mit n = |V |, m = |E|) Sortieren O(m) Find-Operationen n − 1 Unions m log m = O(m log n) O(m) O(n log n) Satz 103 Kruskal’s MSB-Algorithmus hat die Zeitkomplexität O((m + n) log n). Beweis: s.o. EADS ľErnst W. Mayr 3.3 Kruskal’s Algorithmus 449/598 Beispiel 104 (Kruskals Algorithmus) 8 7 9 2 4 11 7 6 4 10 8 1 ⇓ 8 4 8 EADS ľErnst W. Mayr 14 2 4 11 7 7 2 6 1 1 2 minimaler (nicht eindeutiger) Spannbaum 7 6 9 8 5 14 4 10 3 2 3.3 Kruskal’s Algorithmus 450/598 3.4 Prim’s Algorithmus algorithm PRIM-MSB (G, w) := initialisiere Priority Queue PQ mit Knotenmenge V und Schlüssel +∞, ∀v ∈ V wähle Knoten r als Wurzel (beliebig) Schlüssel k[r] := 0 Vorgänger[r] := nil while P Q 6= ∅ do u := ExtractMin(P Q) for alle Knoten v, die in G zu u benachbart sind do if v ∈ P Q and w({u, v}) < k[v] then Vorgänger[v] := u k[v] := w({u, v}) fi od od EADS ľErnst W. Mayr 3.4 Prim’s Algorithmus 451/598 Beispiel 105 (Prim’s Algorithmus) +∞ v7 4 2 +∞ v0 0 11 7 8 +∞ 7 v6 8 v1 +∞ 1 EADS ľErnst W. Mayr v8 6 v2 +∞ +∞ v5 9 14 4 2 10 v3 +∞ Ausgangszustand: +∞ alle Schlüssel = +∞ v4 aktueller Knoten u: Startknoten: r (= v0 ) 452/598 Beispiel 105 (Prim’s Algorithmus) +∞ v7 4 2 +∞ v0 0 +∞ 7 v6 8 11 v8 6 7 8 v1 +∞ 1 4 8 v7 4 11 v2 +∞ 2 +∞ 7 v6 6 7 8 v1 8 EADS ľErnst W. Mayr v8 1 v2 +∞ 9 14 4 2 +∞ v0 +∞ v5 4 2 10 v3 +∞ +∞ v5 9 Ausgangszustand: +∞ alle Schlüssel = +∞ v4 aktueller Knoten u: Startknoten: r (= v0 ) suche u := FindMin(P Q) und entferne u aus P Q +∞ setze Schlüssel der Nach14 v4 barn in P Q mit w({u, v}) < Schlüssel[v]: 10 (v1 = 8, v7 = 4) v3 +∞ 452/598 Beispiel 105 (Prim’s Algorithmus) 4 +∞ 7 v6 8 v7 4 2 +∞ v0 11 v8 6 7 8 v1 v2 +∞ 2 8 7 v6 1 8 8 v7 1 2 +∞ v0 4 11 7 8 6 v1 8 EADS ľErnst W. Mayr v8 1 v2 +∞ 4 2 +∞ v5 suche u := FindMin(P Q) und entferne u aus P Q +∞ setze Schlüssel der Nach14 v4 barn in P Q mit w({u, v}) < Schlüssel[v]: 10 (v1 = 8, v7 = 4) v3 +∞ +∞ v5 suche u := FindMin(P Q) 9 und entferne u aus P Q +∞ setze Schlüssel der Nach14 v4 barn in P Q mit w({u, v}) < Schlüssel[v]: 10 (v6 = 8) v3 +∞ 9 452/598 Beispiel 105 (Prim’s Algorithmus) 8 8 v7 1 2 +∞ v0 7 v6 11 v8 8 v1 v2 +∞ 1 8 4 6 7 2 2 1 v0 7 v7 v6 2 2 11 6 7 8 v1 8 EADS ľErnst W. Mayr v8 1 v2 +∞ 4 2 +∞ v5 suche u := FindMin(P Q) und entferne u aus P Q +∞ setze Schlüssel der Nach14 v4 barn in P Q mit w({u, v}) < Schlüssel[v]: 10 (v6 = 8) v3 +∞ 7 v5 suche u := FindMin(P Q) 9 und entferne u aus P Q +∞ setze Schlüssel der Nach14 v4 barn in P Q mit w({u, v}) < Schlüssel[v]: 10 (v3 = 4, v5 = 7, v8 = 2) v3 4 9 452/598 Beispiel 105 (Prim’s Algorithmus) 2 1 v0 7 v7 v6 v5 2 2 11 v8 8 v1 v2 +∞ 1 8 4 6 7 2 2 v7 1 v0 7 v6 3 v8 11 8 v1 7 EADS ľErnst W. Mayr v5 v2 1 6 2 suche u := FindMin(P Q) und entferne u aus P Q +∞ setze Schlüssel der Nach14 v4 barn in P Q mit w({u, v}) < Schlüssel[v]: 10 (v3 = 4, v5 = 7, v8 = 2) v3 9 4 7 4 6 7 7 suche u := FindMin(P Q) und entferne u aus P Q +∞ setze Schlüssel der Nach14 v4 barn in P Q mit w({u, v}) < Schlüssel[v]: 10 (v1 = 7, v2 = 6) v3 9 4 452/598 Beispiel 105 (Prim’s Algorithmus) 2 v7 1 v0 7 v6 3 v8 11 8 v1 v2 1 7 v5 4 6 7 2 6 2 v7 1 v0 7 v6 3 7 EADS ľErnst W. Mayr 9 4 6 v2 1 4 7 14 v1 7 suche u := FindMin(P Q) und entferne u aus P Q +∞ setze Schlüssel der Nach14 v4 barn in P Q mit w({u, v}) < Schlüssel[v]: 10 (v1 = 7, v2 = 6) v3 9 v5 v8 11 8 7 2 2 10 v3 10 v4 suche u := FindMin(P Q) und entferne u aus P Q setze Schlüssel der Nachbarn in P Q mit w({u, v}) < Schlüssel[v]: (v2 = 2, v4 = 10) 452/598 Beispiel 105 (Prim’s Algorithmus) 2 v7 1 v0 7 v6 3 v5 v8 11 8 14 v1 2 2 2 v7 1 7 v6 3 8 v1 1 EADS ľErnst W. Mayr 4 10 v3 v2 1 9 14 6 7 10 v4 7 v5 v8 11 suche u := FindMin(P Q) und entferne u aus P Q setze Schlüssel der Nachbarn in P Q mit w({u, v}) < Schlüssel[v]: (v1 = 1) 10 v3 v2 1 7 10 v4 suche u := FindMin(P Q) und entferne u aus P Q setze Schlüssel der Nachbarn in P Q mit w({u, v}) < Schlüssel[v]: (v2 = 2, v4 = 10) 9 4 6 7 v0 7 5 452/598 Beispiel 105 (Prim’s Algorithmus) 2 v7 1 v0 7 v6 3 8 14 v1 EADS ľErnst W. Mayr 4 10 v3 v2 6 9 14 6 v1 7 v5 v8 7 8 7 v6 3 11 10 v4 5 2 v7 suche u := FindMin(P Q) und entferne u aus P Q setze Schlüssel der Nachbarn in P Q mit w({u, v}) < Schlüssel[v]: solche Nachbarn existieren nicht 10 v3 v2 1 1 10 v4 suche u := FindMin(P Q) und entferne u aus P Q setze Schlüssel der Nachbarn in P Q mit w({u, v}) < Schlüssel[v]: (v1 = 1) 9 4 6 7 v0 v5 v8 11 1 7 5 452/598 Beispiel 105 (Prim’s Algorithmus) 2 v7 1 v0 7 3 v1 v7 8 5 7 v6 3 v5 v8 v1 EADS ľErnst W. Mayr 4 10 v3 v2 6 9 14 6 7 9 v4 suche u := FindMin(P Q) und entferne u aus P Q setze Schlüssel der Nachbarn in P Q mit w({u, v}) < Schlüssel[v]: (v4 = 9) 10 v3 v2 11 10 v4 suche u := FindMin(P Q) und entferne u aus P Q setze Schlüssel der Nachbarn in P Q mit w({u, v}) < Schlüssel[v]: solche Nachbarn existieren nicht 9 14 6 2 v0 v5 4 6 7 1 7 v8 11 8 v6 5 452/598 Beispiel 105 (Prim’s Algorithmus) 2 v7 1 v0 7 3 10 v3 v2 6 2 v0 8 v6 v5 v8 v1 EADS ľErnst W. Mayr 14 v4 10 v3 v2 6 8 4 6 7 suche u := FindMin(P Q) und entferne u aus P Q setze Schlüssel der Nachbarn in P Q mit w({u, v}) < Schlüssel[v]: (v4 = 9) 5 7 3 11 9 v4 14 v1 v7 9 4 6 7 1 v5 v8 11 8 v6 Endzustand: suche u := FindMin(P Q) und entferne u aus P Q, damit ist P Q leer und der Algorithmus beendet 5 3.4 Prim’s Algorithmus 452/598 Korrektheit: ist klar. Zeitkomplexität: n ExtractMin O(m) sonstige Operationen inclusive DecreaseKey Implementierung der Priority Queue mittels Fibonacci-Heaps: Initialisierung ExtractMins DecreaseKeys Sonstiger Overhead EADS ľErnst W. Mayr O(n) O(n log n) (≤ n Stück) O(m) (≤ m Stück) O(m) 3.4 Prim’s Algorithmus 453/598 Satz 106 Sei G = (V, E) ein ungerichteter Graph (zusammenhängend, einfach) mit Kantengewichten w. Prim’s Algorithmus berechnet, wenn mit Fibonacci-Heaps implementiert, einen minimalen Spannbaum von (G, w) in Zeit O(m + n log n) (wobei n = |V |, m = |E|). Dies ist für m = Ω(n log n) asymptotisch optimal. Beweis: s.o. EADS ľErnst W. Mayr 3.4 Prim’s Algorithmus 454/598 3.5 Prim’s Algorithmus, zweite Variante Die Idee der folgenden Variante von Prim’s Algorithmus ist: Lasse die Priority Queues nicht zu groß werden. Seien dazu G = (V, E), |V | = n, |E| = m, w Gewichtsfunktion, und k ein Parameter, dessen Wert wir erst später festlegen werden. Der Algorithmus arbeitet nun in Phasen wie folgt: 1 Initialisiere eine Schlange von Bäumen, jeder Baum anfangs ein (Super-) Knoten. Zu jedem Baum initialisiere eine Priority Queue (Fibonacci-Heap) mit den Nachbarn der Knoten im Baum, die selbst nicht im Baum sind, als Elementen und jeweils dem Gewicht einer leichtesten Kante zu einem Knoten im Baum als Schlüssel. 2 Markiere jeden Baum in der Schlange mit der Nummer der laufenden Phase. EADS ľErnst W. Mayr 3.5 Prim’s Algorithmus, zweite Variante 455/598 3 Bestimme k für die Phase 4 while vorderster Baum in der Schlange hat laufende Phasennummer do lasse ihn wachsen, solange seine Priority Queue höchstens k Elemente enthält (und noch etwas zu tun ist) if Priority Queue zu groß (mehr als k Elemente) then füge Baum mit inkrementierter Phasennummer am Ende der Schlange ein fi od 5 Falls Phasensprung“: Schrumpfe alle Bäume zu Superknoten, ” reduziere Kantenmenge (d.h., behalte zwischen zwei Knoten jeweils nur die leichteste Kante) 6 Beginne nächste Phase mit Schritt 1.! EADS ľErnst W. Mayr 3.5 Prim’s Algorithmus, zweite Variante 456/598 wenn groß genug hinten anstellen .. Endemarkierung EADS ľErnst W. Mayr Endemarkierung falls Phasensprung schrumpfen und nächste Phase 3.5 Prim’s Algorithmus, zweite Variante 457/598 Analyse des Zeitbedarfs: Sei t die Anzahl der Bäume in der Schlange zu Beginn einer Phase. Betrachte die Schlange von Bäumen: v T1 T2 ··· Ti ··· Tt Abgesehen von den Operationen, die bei der Vereinigung von Superknoten anfallen, beträgt der Zeitaufwand pro Phase O(m). EADS ľErnst W. Mayr 3.5 Prim’s Algorithmus, zweite Variante 458/598 Vereinigung zweier Superknoten, z.B. T1 und T2 : Für jeden (Super-)Knoten v in T2 ’s Priority Queue: √ 1 v ∈ T : (nichts zu tun) 1 2 v in Priority Queue von T1 : DecreaseKey. Hilfsdatenstruktur: für alle Knoten in den Priority Queues ein Zeiger auf den Superknoten, in dessen Priority Queue der Knoten letztmals am Anfang der Schlange stand. 3 sonst: Einfügen EADS ľErnst W. Mayr 3.5 Prim’s Algorithmus, zweite Variante 459/598 Betrachte Knoten mit Halbkanten“: jede Halbkante kommt nur ” 1x in allen Bäumen der Queue vor. Mit m Kanten ergeben sich 2m Halbkanten. v e m Kanten EADS ľErnst W. Mayr v → e e und 2m Halbkanten v = b e Kante aus zwei Halbkanten 3.5 Prim’s Algorithmus, zweite Variante 460/598 Zeitaufwand pro Phase (mit Bildung der Superknoten zu Beginn): Initialisierung: O(m) ExtractMin: < t Operationen sonstige Priority Queue-Operationen, Overhead: Zeit O(m) Da die Priority Queues höchstens k Elemente enthalten, wenn darauf eine teure“ Priority Queue-Operation durchgeführt wird, ” sind die Kosten pro Phase O(t log k + m) . 2m Setze k = 2 t (damit t log k = 2m). Damit sind die Kosten pro Phase O(m). EADS ľErnst W. Mayr 3.5 Prim’s Algorithmus, zweite Variante 461/598 Wieviele Phasen führt der Algorithmus aus? t ist die Anzahl der Superknoten am Anfang einer Phase, t0 sei diese Zahl zu Beginn der nächsten Phase. Sei a die durchschnittliche Anzahl ursprünglicher Knoten in jeder der t Priority Queues zu Anfang der Phase, a0 entsprechend zu Beginn der nächsten Phase. Wir haben: 1 a= 2 t0 ≤ 2m t 2m k EADS ľErnst W. Mayr (mit Ausnahme ev. der letzten Phase) 3.5 Prim’s Algorithmus, zweite Variante 462/598 Also: a0 = 2m 2m ≥ k = 2 t = 2a 0 t Für die erste Phase ist a = 2m n , für jede Phase ist a ≤ n − 1. Also ist die Anzahl der Phasen 2m (i) ≤ 1 + min i; log2 (n − 1) ≤ . n n o (i) Setzen wir β(m, n) := min i; log2 n ≤ m n , dann gilt n β(m, n) ≤ log n für n ≤ m ≤ . 2 ∗ EADS ľErnst W. Mayr 3.5 Prim’s Algorithmus, zweite Variante 463/598 Satz 107 Für gewichtete, zusammenhängende (ungerichtete) Graphen mit n Knoten, m Kanten kann ein minimaler Spannbaum in Zeit O(min{m · β(m, n), m + n log n}) bestimmt werden. EADS ľErnst W. Mayr 3.5 Prim’s Algorithmus, zweite Variante 464/598