ADS Vorlesung Prof. Dr. W. Conen, FH Gelsenkirchen Minimal spannende Bäume mit dem Boruvka-Algorithmus V1.0c Es fehlt noch der vermutlich älteste Algo für minimale Spannbäume: Laut Tarjan von Boruvka, 1926! Idee wie folgt: In jeder Runde werden Teilbäume (eines MST) mit ihren nächsten Nachbarn verschmolzen (wir nennen die Teilbäume im folgenden Komponenten) Die Kanten, die zu Verschmelzungen führen, werden in den MST aufgenommen Durch die Verschmelzungen werden viele Kanten irrelevant Das sind die Kanten, die Knoten innerhalb einer bereits verbundenen Komponente verbinden (also in der Komponente zu Kreisen führen würden) Zu Beginn steht jeder Knoten für eine Komponente, die nur ihn enthält V1.0c Boruvkas Algorithmus Annahme: wir haben n Knoten, m Kanten, indiziert von [0..n-1], der (ungerichtete) Graph G = (V,E) ist zusammenhängend, Ausgabe: MST Hier der Ablauf des noch folgenden Algorithmus Solange es noch mehr als eine Komponente gibt (Runde/Iteration) Setze für jede Komponente i die kürzeste in dieser Runde bisher gefundene Kante aus i heraus auf null mit einer Länge unendlich Laufe über alle Kanten xy, die noch in E sind (Phase 1) Finde die Repräsentanten der Komponenten, in denen x und y sich befinden, i für x, j für y. Entferne xy aus E, falls i = j, also x und y in einer Komponente sind Sonst prüfe, ob xy eine neue kürzeste Kante aus i bzw. j heraus ist, und, falls ja, dann vermerke dies Laufe über alle Komponenten i (Phase 2) Sei xy die kürzeste Kante, die aus i herausführt mit eine Länge D[i] Wenn x und y nicht mittlerweile in der gleichen Komponente liegen, dann nimm xy in den MST auf und vereine die Komponenten Entferne xy aus E V1.0c Boruvka 3 b 4 d 3 1 a 4 c 3 b 1 4 1 2 e d 3 1 a 4 c 3 b 1 4 1 2 e d 3 1 a 4 2 f 3 2 f 3 2 f 2 c 1 e Der Ausgangsgraph, es werden alle Kanten nach und nach betrachtet. Der „naive“ Algorithmus schreibt keine Reihenfolge vor. 3 Runde 1, Ergebnis Phase 1: Jeder Knoten steht noch für seine eigene Komponente. Die roten Kanten sind die gefundenen minimalen Kanten (die Wahl, z.B. für c ist nicht eindeutig), die aus den Komponenten hinausführen. Runde 1, Ergebnis Phase 2: Nach Betrachtung von b und c wird Kante eb überflüssig. Die grünen Kanten werden in den MST übernommen und aus E entfernt. In E sind noch die schwarzen Kanten. Es gibt jetzt nur noch ZWEI Komponenten. V1.0c Boruvka 3 b 4 d 3 1 a b 1 4 e d 3 a f 2 c 3 2 3 2 2 1 c 1 e f 3 Runde 2, Ergebnis der Phase 1: Jetzt ist das Ergebnis übrigens eindeutig. Denken Sie daran: im Prinzip wird jede schwarze Kante „angepackt“, die Kante ac wird entfernt, weil sie Knoten aus der gleichen Komponente verbindet Runde 2, Ergebnis der Phase 2: In E sind noch die schwarzen Kanten enthalten. Wir haben n-1 Kanten aufgenommen (die grünen), der MST ist komplett, es gibt nur noch eine Komponente. V1.0c Boruvkas Algorithmus: Implementierung Um die zusammenwachsenden Komponenten zu verwalten, verwenden wir UnionFind auf Arraybasis. Die Daten werden in einem Array P mit n Einträgen abgelegt, nummeriert von 0..n-1. Zu einem gegebenen Knoten x findet Find(P,x) den Repräsentanten der Komponente, in der x liegt, ebenso verwenden wir wieder Union und Init. In jeder „Solange“-Runde verwenden wir zwei Arrays, um die Informationen zu den kürzesten Kanten, die aus den Komponenten hinausführen, abzulegen: D[i] gibt die Länge der bisher gefundenen kürzesten Kante min_edge[i] an; zu Beginn jeder Runde werden alle auf ∞ bzw. null initialisiert In der Phase 1 der „Solange“-Runde werden alle Kanten, die noch in E verblieben sind, angeschaut: Falls die Kante innerhalb einer bereits gefundenen Komponente liegt, wird sie aus E entfernt Sonst wird geschaut, ob sie vielleicht kürzer ist, als die bisher gefundenen „kürzesten“ Kanten, die aus den beiden beteiligten Komponenten hinausführen – wenn ja, dann werden die Informationen in D und min_edge upgedated, und zwar für den Knoten, der die jeweilge Komponente repräsentiert, in der der jeweilie Endpunkt der betrachteten Kante liegt (wenn nein, dann geschieht nichts) In der Phase 2 der „Solange“-Runde laufen wir über alle Knoten. Wenn diese Komponenten repräsentieren (also P[i] = i ist), dann nehmen wir die min_edge zu dieser Komponente in den MST auf und vereinen die beiden Komponenten, falls die beiden Knoten, die sie verbindet, nicht bereits (durch andere in dieser Phase aufgenommene Kanten) in einer Komponente liegen. Zuletzt entfernen wir noch die Kante aus E V1.0c Boruvkas Algorithmus Boruvka(G): int P[n], Init(P), MST ← {}, int D[n], Edge min_edge[n]; while Anzahl(P) > 1 do . D[i] ← ∞, min_edge[i] ← null, jeweils für alle i . for each xy ∈ E do . i ← Find(P,x), j ← Find(P,y) . if (i = j) then E ← E - {xy} . else if w(xy) < D[i] then D[i] ← w(xy), min_edge[i] ← xy . if w(xy) < D[j] then D[j] ← w(xy), min_edge[j] ← xy . for each i in [1..n] do . if P[i] = i then xy ← min_edge[i] . if Find(P,x) != Find(P,y) then . MST ← MST ∪ {xy}, Union(P,Find(P,x),Find(P,y)) E ← E – {xy} V1.0c Boruvkas Algorithmus Kosten: Die Union-Find-Operation sind annähernd linear Die Anzahl von Komponenten (also bisher unverbundenen Teilen des MST) verringert sich in jeder Runde mind. um den Faktor 2 Eine nennenswerte Zahl von Kanten wird in jeder Runde entfernt Eine nicht sehr präzise, „konservative“ Abschätzung (ohne Einsparungen durch Kantenentfernungen): O(|E| log |V| log* |E|) = O (m log n log* m) Anzahl der Runden höchsten log |V|, pro Runde höchstens m Find, mit Kosten weniger als m log* m. mit Modifikationen O(m log n) erreichbar, In empirischen Tests (s. Sedgewick) Kruskal für spärlich besetzte Graphen und Prim-Varianten für stärker bis stark gefüllte Graphen unterlegen V1.0c Boruvkas Algorithmus Trotzdem ist der Algorithmus interessant, z.B. für Parallelisierungen – aber auch für Randomisierung („Verzufallisierung“) Dort werden Kanten entfernt, die sicher nicht im MST sein können... Zunächst 3 modifizierte Boruvka-Iterationen: Die Knoten der Teil-MST werden je Teil-MST zu einem einzigen Superknoten verschmolzen die Kanten werden entsprechend umnummeriert, bei parallelen wird nur die kürzeste vermerkt, Schleifen werden entfernt („Kontraktion“) Das kann man übrigens auch in den normalen Boruvka einbauen und damit Aufwand vermeiden (effektivere Reduktion der Kantenzahl) Weitere Kanten werden zufällig gewählt und geprüft, dies geschieht eingebettet in einen bestimmten Ablauf, den wir nicht im Detail anschauen werden, der aber zu zu erwartenden Kosten von O(m+n) führt – also linear! Das heute immer wichtiger werdende Instrument der Randomisierung von Algorithmen kann man leichter an einem anderen Beispiel genauer verstehen: ein randomisierter Algorithmus von Uwe Schöning zur Erfüllbarkeitsprüfung von aussagenlogischen Formeln. Diesen finden sie z.B. in Schönings Buch „Ideen der Informatik“. V1.0c