Grundlagen der Informatik 2 – Algorithmik Vorlesung 1 und 5

Werbung
Grundlagen der Informatik 2 – Algorithmik
Vorlesung 1 und 5
Prof. Dr. Wolfram Conen
[email protected]
Raum: P -1.08
Version 0.9β , 1. Mai 2005
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Inhalte
• Dijkstra: Korrektheit, Datenstrukturen zur effizienten Implementierung (Priority
Queues, Exkurs: Heap Sort, Radix Sort, Bucketsort); Minimal spannende Bäume
(Union-Find)
• Greedy-Algorithmen, Huffman-Codierung, Hashing
• Algorithmen für uninformierte und informierte Suche nach Problemlösungen
• NP-Vollständigkeit: TSP und andere schwere Probleme; Approximation
• Strings und Suffix-Trees
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 2
Was wir nicht mehr tun können . . .
• (Effiziente) randomisierte Algorithmen zur (exakten oder approximativen) Problemlösung
• Andere Modelle der Berechenbarkeit (Termersetzungssysteme, DNA-Computer
etc.)
• Logik und formal nicht-erkennbare Wahrheiten (Gödel)
• Moderne Logik und das Semantic Web (Description Logic, Temporal and Action
Logics)
• Weitere Grundlegende Algorithmen und Methoden der KI, z.B. zum maschinellen
Lernen, zur symbolischen Wissensverarbeitung, zum Data Mining, zur Sprachverarbeitung etc.
• . . . und viel, viel mehr, das auf den Inhalten unserer GIN1a/1b/2-Veranstaltungen
aufbaut.
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 3
Ziele aus der Modulbeschreibung . . .
• Erkennen der grundlegenden Bedeutung von mathematischen/theoretischen Instrumenten für das Finden und die Analyse von Problemlösungen.
• Beherrschen der grundlegenden Begrifflichkeiten, der wichtigsten Resultate und
der wesentlichen Beweisverfahren.
• Überblicksartige Kenntnisse der grundlegenden theoretischen Resultate und Methoden, die wichtigen Einfluss auf weitere Felder der Informatik haben (z.B. auf
Algorithmik, Sprachen, künstliche Intelligenz).
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 4
Literatur...
Kaufen Sie erstmal nichts! Ich nenne Literatur bei den einzelnen Inhalten!
Für die Jäger und Sammler unter ihnen (zum Nachschlagen für den Schrank) hier
das meist gelobte Buch zu Algorithmen und Datenstrukturen: Cormen, Leierson,
Rivest, Stein Introduction to Algorithms, MIT Press, 2001 (2. Auflage, 1202 Seiten,
ca. 60 Euro)
Für den Blick über den Tellerrand dieser Vorlesung:
• Schöning: Ideen der Informatik, Oldenbourg, 2002
• Gritzmann, Brandenberg: Das Geheimnis des kürzesten Weges, Springer, 2.
Aufl., 2003
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 5
Unterlagen...
Für Skriptversionen, Übungsblätter, aktuelle Nachrichten usw. guckst du wie gehabt
hier:
http://www.informatik.fh-gelsenkirchen.de/conen
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 6
Algorithmik: Dijkstra
Dijkstra revisited (1)
Input: Gewichteter Digraph G = (V, E) mit nicht-negativen Gewichten
Output: Kürzeste s, v -Wege und deren Längen Distanz(v ) für alle v ∈ V
BEGIN S ← {s}, Distanz(s) ← 0
/* Bogenlänge(s, v) = ∞, wenn es keinen Bogen von s nach v gibt */
(1) FOR ALL v ∈ V /{s} DO Distanz(v ) ← Bogenlänge(s, v ); Vorgänger(v ) ← s; END FOR
WHILE S 6= V DO
/* ⇐= Hier auf alle Knoten erweitert! */
(2) finde v ∗ ∈ V \S mit Distanz(v ∗ ) = min{Distanz(v ): v ∈ V \S}; (3) S ← S ∪ {v ∗ }
(4) FOR ALL v ∈ V \S DO
IF Distanz(v ∗ )+Bogenlänge(v ∗ , v ) < Distanz(v ) THEN
Distanz(v ) ← Distanz(v ∗ )+Bogenlänge(v ∗ , v ); Vorgänger(v ) ← v ∗
Satz 1. D IJKSTRAS Algorithmus arbeitet korrekt.
Beweis: Wir zeigen, dass die folgenden Zusicherungen vor jeder Ausführung von (2) und am Ende gelten:
(i) Für alle Knoten v ∈ S und alle Knoten w ∈ V \S gilt Distanz(v ) ≤ Distanz(w).
(ii) Für alle Knoten v ∈ S gilt: Distanz(v ) ist die Länge eines kürzesten s-v -Weges in G. Falls Distanz(v ) < ∞, dann
gibt es einen s-v -Weges der Länge Distanz(v ), dessen letzter Bogen (Vorgänger(v ),v ) ist (außer für v = s) und
dessen Knoten alle in S liegen.
(iii) Für alle Knoten w ∈ V \S ist Distanz(w) die Länge eines kürzesten s-w-Weges im Untergraph W mit den Knoten
VW = S ∪ {w}. Falls Distanz(v ) < ∞, dann Vorgänger(w) ∈ S und Distanz(w) = Distanz(Vorgänger(w) +
Bogenlänge(Vorgänger(w,v )).
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 7
Algorithmik: Dijkstra
Dijkstra revisited (2)
Input: Gewichteter Digraph G = (V, E) mit nicht-negativen Gewichten
Output: Kürzeste s, v -Wege und deren Längen Distanz(v ) für alle v ∈ V
BEGIN S ← {s}, Distanz(s) ← 0
/* Bogenlänge(s, v) = ∞, wenn es keinen Bogen von s nach v gibt */
(1) FOR ALL v ∈ V /{s} DO Distanz(v ) ← Bogenlänge(s, v ); Vorgänger(v ) ← s; END FOR
WHILE S 6= V DO
/* ⇐= Hier auf alle Knoten erweitert! */
(2) finde v ∗ ∈ V \S mit Distanz(v ∗ ) = min{Distanz(v ): v ∈ V \S}; (3) S ← S ∪ {v ∗ }
(4) FOR ALL v ∈ V \S DO
IF Distanz(v ∗ )+Bogenlänge(v ∗ , v ) < Distanz(v ) THEN
Distanz(v ) ← Distanz(v ∗ )+Bogenlänge(v ∗ , v ); Vorgänger(v ) ← v ∗
Beweis (Forts.): (Per Induktion, hier abgekürzt) Klarerweise gelten alle Bedingungen nach der Initialisierung (Induktionsanfang). Wir müssen also noch beweisen,
dass (2),(3) und (4) die Gültigkeit der Bedingungen nicht verletzen.
Anmerkung: Der Algorithmus terminiert natürlich: in jeder Runde wird ein Knoten aus V \S zu S hinzugefügt. Deshalb ist klar, dass die Gültigkeit von (ii) nach
Abschluß des Algorithmus garantiert, dass alle kürzesten Wege gefunden wurden
(denn das genau sicher (ii) zu jedem Zeitpunkt für alle Knoten aus S zu – und in S
sind am Ende eben alle Knoten enthalten!)
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 8
Algorithmik: Dijkstra
Dijkstra revisited (3)
Input: Gewichteter Digraph G = (V, E) mit nicht-negativen Gewichten
Output: Kürzeste s, v -Wege und deren Längen Distanz(v ) für alle v ∈ V
BEGIN S ← {s}, Distanz(s) ← 0
/* Bogenlänge(s, v) = ∞, wenn es keinen Bogen von s nach v gibt */
(1) FOR ALL v ∈ V /{s} DO Distanz(v ) ← Bogenlänge(s, v ); Vorgänger(v ) ← s; END FOR
WHILE S 6= V DO
/* ⇐= Hier auf alle Knoten erweitert! */
(2) finde v ∗ ∈ V \S mit Distanz(v ∗ ) = min{Distanz(v ): v ∈ V \S}; (3) S ← S ∪ {v ∗ }
(4) FOR ALL v ∈ V \S DO
IF Distanz(v ∗ )+Bogenlänge(v ∗ , v ) < Distanz(v ) THEN
Distanz(v ) ← Distanz(v ∗ )+Bogenlänge(v ∗ , v ); Vorgänger(v ) ← v ∗
(Wir nehmen nun an, dass die Bedingungen vor der Ausführung von (2) gegolten
haben und zeigen, dass sie dann auch nach (2),(3) und (4) gelten!)
Sei nun v ∗der Knoten, der im Schritt (2) ausgewählt wird (das gilt für den ganzen
Rest des Beweises!)
Zu (i): Für jedes v ∈ S und jedes w ∈ V \S gilt Distanz(v ) ≤ Distanz(v ∗) ≤
Distanz(w) wg. (i) und der Art der Auswahl in (2), also gilt (i) nach (3) und (4) weiterhin.
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 9
Algorithmik: Dijkstra
Dijkstra revisited (4)
Input: Gewichteter Digraph G = (V, E) mit nicht-negativen Gewichten
Output: Kürzeste s, v -Wege und deren Längen Distanz(v ) für alle v ∈ V
BEGIN S ← {s}, Distanz(s) ← 0
/* Bogenlänge(s, v) = ∞, wenn es keinen Bogen von s nach v gibt */
(1) FOR ALL v ∈ V /{s} DO Distanz(v ) ← Bogenlänge(s, v ); Vorgänger(v ) ← s; END FOR
WHILE S 6= V DO
/* ⇐= Hier auf alle Knoten erweitert! */
(2) finde v ∗ ∈ V \S mit Distanz(v ∗ ) = min{Distanz(v ): v ∈ V \S}; (3) S ← S ∪ {v ∗ }
(4) FOR ALL v ∈ V \S DO
IF Distanz(v ∗ )+Bogenlänge(v ∗ , v ) < Distanz(v ) THEN
Distanz(v ) ← Distanz(v ∗ )+Bogenlänge(v ∗ , v ); Vorgänger(v ) ← v ∗
Hält (ii) nach (3) (und damit auch nach (4), denn dort wird nichts mehr an S verändert)? Um das zu prüfen, reicht
es wg. der Gültigkeit von (iii) vor (3) zu zeigen, dass kein s − v ∗ -Pfad, der einen beliebigen Knoten w aus V \S enthält,
kürzer sein kann, als Distanz(v ∗ ). Nehmen wir also an, dass ein solcher Pfad P mit w doch existieren würde und sei w
der erste Knoten ausserhalb von S , auf den wir bei der Reise von s nach v ∗ treffen.
Weil (iii) vor (3) galt, ist Distanz(w) ≤ Pfadkosten(P[s,w] ).1 Weil die Bogengewichte nicht-negativ sind, gilt
Pfadkosten(P[s,w] ) ≤ Pfadkosten(P ) < Distanz(v ∗ ). Das impliziert aber Distanz(w) < Distanz(v ∗ ), im Widerspruch
zur Wahl von v ∗ in (2) (hier wird versteckt nochmal (iii) verwendet: die Distanz von v ∗ war vor (2) minimal unter allen
Knoten aus V \S ).
1P
[s,w] ist der Teil des Weges P , der von s nach w führt, s. auch Übungen zu GIN1b (eindeutig, weil P ein Weg ist).
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 10
Algorithmik: Dijkstra
Dijkstra revisited (5)
Input: Gewichteter Digraph G = (V, E) mit nicht-negativen Gewichten
Output: Kürzeste s, v -Wege und deren Längen Distanz(v ) für alle v ∈ V
BEGIN S ← {s}, Distanz(s) ← 0
/* Bogenlänge(s, v) = ∞, wenn es keinen Bogen von s nach v gibt */
(1) FOR ALL v ∈ V /{s} DO Distanz(v ) ← Bogenlänge(s, v ); Vorgänger(v ) ← s; END FOR
WHILE S 6= V DO
/* ⇐= Hier auf alle Knoten erweitert! */
(2) finde v ∗ ∈ V \S mit Distanz(v ∗ ) = min{Distanz(v ): v ∈ V \S}; (3) S ← S ∪ {v ∗ }
(4) FOR ALL v ∈ V \S DO
IF Distanz(v ∗ )+Bogenlänge(v ∗ , v ) < Distanz(v ) THEN
Distanz(v ) ← Distanz(v ∗ )+Bogenlänge(v ∗ , v ); Vorgänger(v ) ← v ∗
Noch zu zeigen ist, dass (iii) nach (2),(3) und (4) gilt: Falls für ein w aus V \S in (4) der Vorgänger auf v ∗ und
die Distanz auf Distanz(v ∗ ) + Bogenlänge(v ∗ ,w) gesetzt wird (also ein Update erfolgt), dann existiert ein s-w-Weg im
Teilgraph H mit den Knoten VH = S ∪ {w} der Länge Distanz(v ∗ ) + Bogenlänge(v ∗ ,w) mit dem letzten Bogen (v ∗ , w)
(achten sie wieder darauf, dass (iii) für v ∗ galt). Gilt nun nach dem Update (iii) weiter für alle w aus V \S ?
Nehmen Sie an, dass es nach (3) und (4) ein w und einen s-w-Weg P im Teilgraph H mit VH = S ∪ {w} gibt, der
kürzer ist, als Distanz(w) (also (iii) verletzt). P muß nun v ∗ enthalten (nur dieser Knoten wurde zu S hinzugefügt), sonst
wäre (iii) bereits vor (3) verletzt gewesen (achten sie darauf, dass Distanz(w) niemals ansteigt)!
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 11
Algorithmik: Dijkstra
Dijkstra revisited (6)
Input: Gewichteter Digraph G = (V, E) mit nicht-negativen Gewichten
Output: Kürzeste s, v -Wege und deren Längen Distanz(v ) für alle v ∈ V
BEGIN S ← {s}, Distanz(s) ← 0
/* Bogenlänge(s, v) = ∞, wenn es keinen Bogen von s nach v gibt */
(1) FOR ALL v ∈ V /{s} DO Distanz(v ) ← Bogenlänge(s, v ); Vorgänger(v ) ← s; END FOR
WHILE S 6= V DO
/* ⇐= Hier auf alle Knoten erweitert! */
(2) finde v ∗ ∈ V \S mit Distanz(v ∗ ) = min{Distanz(v ): v ∈ V \S}; (3) S ← S ∪ {v ∗ }
(4) FOR ALL v ∈ V \S DO
IF Distanz(v ∗ )+Bogenlänge(v ∗ , v ) < Distanz(v ) THEN
Distanz(v ) ← Distanz(v ∗ )+Bogenlänge(v ∗ , v ); Vorgänger(v ) ← v ∗
Sei nun v der Vorgänger von w in P . Da v ∈ S ist, wissen wir wg. (i), dass Distanz(v ) ≤ Distanz(v ∗ ) (v ∗ wurde zuletzt
ausgewählt, war also vor (2) in V \S und da galt ja (i) auch schon mit v ∈ V !) und dass Distanz(w) ≤ Distanz(v ) +
Bogenlänge(v, w) (wäre es anders hätte (4) bereits in früheren Runden zu einem Update führen müssen!). Wir schließen:
Distanz(w) ≤ Distanz(v ) + Bogenlänge(v, w) ≤ Distanz(v ∗ ) + Bogenlänge(v, w) ≤ Pfadkosten(P).
Die letzte Ungleichung gilt, weil wg. (ii) Distanz(v ∗ ) die Länge einen kürzesten s-v ∗ -Weges in S ist und weil P einen sv ∗ -Weg und den Bogen (v, w) enthält. Aber natürlich steht Distanz(w) ≤ Pfadkosten(P ) im Widerspruch zur Annahme,
dass P kürzer ist, als Distanz(w) – also kann es einen solchen Weg P nicht geben (also gilt (iii) nach (3) und (4)!). q.e.d.
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 12
Algorithmik: Heap und Co. (für Dijkstra)
Bucket/Radix Sort, PQueues, Heaps
[s. PPT-Präsentationen bzw. die zugehörigen PDF-Files]
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 13
Algorithmik: K RUSKAL
Spannende Bäume, Teil 2
Zur Erinnnerung: Sei G = (V, E) ein ungerichteter Graph mit Kantenbewertung w.
• G heißt zusammenhängend (oder verbunden), wenn jeder Knoten von jedem
anderen Knoten erreichbar ist (bei gerichteten Graphen hieße das dann stark
zusammenhängend).
• Ein Graph ohne Kreise (oder Zyklen) heißt kreisfrei (oder azyklisch).
• Ein zusammenhängender Graph ohne Zyklen heißt auch freier Baum (frei, weil
es keine ausgezeichnete Wurzel gibt).
Anmerkung: Wenn der Graph nicht zusammenhängend, aber kreisfrei war, dann
kann man ihn auch als Wald von freien Bäumen ansehen.
Übrigens: Eine Komponente ist ein maximal zusammenhängender Teilgraph von G,
d.h. es gibt keine Kante in G, die einen Knoten, der nicht im Teilgraph ist, mit diesem
Teilgraph verbindet. Mit anderen Worten: andere Knoten, als die die im Teilgraph
enthalten sind, sind von Knoten dieses Teilgraphs aus nicht erreichbar.
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 14
Algorithmik: K RUSKAL
Die einzelnen Komponenten eines Graphs induzieren eine Zerlegung der Knotenmenge (s. GIN1b-Folien). Wenn der Graph zusammenhängend ist, dann besteht er
natürlich nur aus einer Komponente.
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 15
Algorithmik: K RUSKAL
Spannende Bäume, Teil 2
Freie Bäume haben z.B. die folgenden interessanten Eigenschaften:
(i) Ein freier Baum mit n ≥ 1 hat genau n − 1 Kanten.
(ii) Wenn man einem freien Baum eine beliebige Kante hinzufügt, entsteht ein Zyklus
Machen sie sich insbesondere die letzte Eigenschaft klar! Abstrakt ist es klar:
• zwischen jedem Paar von Knoten, z.B. x und y , aus V gibt es bereits einen Weg
(der Baum ist ja ein zusammenhängender Graph).
• Wenn sie jetzt einen Kante einfügen, die x und y miteinander verbindet (unter
der Annahme, dass es zwischen diesen beiden Knoten keine direkte Verbindung
gab, ein solches Paar gibt es in einem Baum immer, wenn n > 2 ist), dann gibt
es einen weiteren Weg von x nach y , also haben sie einen Kreis!
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 16
Algorithmik: K RUSKAL
Spannende Bäume, Teil 2
Weiteres zur Erinnerung:
• Ein Spannbaum zu einem zusammenhängenden Graphen G = (V, E) mit
Kantenbewertungen bzw. Gewichten w ist ein freier Baum, der alle Knoten aus
V enthält und dessen Kanten eine Teilmenge von E bilden.
• Das Gewicht eines Spannbaum ist die Summe der Gewichte seiner Kanten.
• Spannbäume mit einem Gewicht, das im Vergleich zu allen Spannbäumen von G
minimal ist (d.h. es gibt keinen Spannbaum zu G mit kleinerem Gewicht) heißen
minimale Spannbäume (natürlich kann es mehrere Spannbäume mit dieser Eigenschaft geben).
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 17
Algorithmik: K RUSKAL
Spannende Bäume, Teil 2
Wir kennen bereits den Algorithmus von P RIM, der einen minimalen Spannbaum,
ausgehend von einem ausgezeichneten Knoten s, findet.
• Der Ablauf entspricht dem D IJKSTRA-Algorithmus
• Als Distanzen werden aber die Gewichte von einzelnen Kanten mitgeführt, und
nicht die Gewichte von Wegen!
• Diese Gewichte für einen Knoten v aus V S sind die Gewichte der jeweils besten
Kante, die aus S heraus direkt zum Knoten v führt
[Beispiel in ihrem Mitschrieb]
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 18
Algorithmik: K RUSKAL
Spannende Bäume, Teil 2
Der P RIM-Algorithmus konstruiert also nach und nach einen minimalen Spannbaum,
der in jedem Schritt um eine Kante erweitert wird.
Es geht aber auch anders . . .
Definition 2. [Cut] Ein Cut eines Graphen ist eine Zerlegung (alternativ: Partition)
der Knoten (zum Zerlegungsbegriff s. Übungsaufgabe 13 zu GIN1b) in zwei Mengen. Eine kreuzende Kante ist eine Kante, die einen Knoten der einen Menge mit
einem Knoten der anderen Menge verbindet.
Satz 3. [Cut Eigenschaft] Sei für G = (V, E) die Menge Z = {U, W } ein Cut
der Knotenmenge V . Sei uw eine kreuzende Kante in G mit minimalen Kosten unter
allen kreuzenden Kanten, also aus der Menge {u0w0|u0 ∈ U, w0 ∈ W }. Dann gibt
es mindestens einen minimalen Spannbaum zu G, der uw enthält (i) und jeder
minimal spannende Baum enthält eine minimale kreuzende Kante. (ii)
[Beweise s. Übung, dieser Beweis (ebenso wie der nächste) findet sich z.B. im Sedgewick, Part 5]
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 19
Algorithmik: K RUSKAL
Spannende Bäume, Teil 2
Satz 4. [Cycle Eigenschaft] Gegeben sei ein Graph G und ein Graph G0 der aus
G durch Hinzufügen einer Kante e entsteht. Fügt man e zu einem minimalen spannenden Baum für G hinzu und löscht eine maximale Kante auf dem resultierenden
Kreis, dann erhält man einen minimalen spannenden Baum für G0.
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 20
Algorithmik: K RUSKAL
Spannende Bäume, Teil 2
Dieser Satz liefert die Grundlage für den Algorithmus von K RUSKAL, der für G =
(V, E) (kleine) minimale Spannbäume nach und nach zusammenfügt, und zwar
wie folgt:
• Zu Beginn sei T ein Graph, der genau die Knoten V enthält, aber keine Kanten.
• Die Kanten aus E werden nach Gewicht sortiert.
• In jeder Runde eine Kante mit minimalem Gewicht ausgewählt und aus der Menge der noch nicht betrachteten Kanten entfernt.
• Wenn die Kante zwei bisher getrennte Komponenten miteinander verbindet,
dann wird sie in den Graphen eingefügt (und die Komponenten werden dadurch
verschmolzen).
• Ansonsten wird die Kante ignoriert (sie würde zu einem Kreis führen! Warum?)
• Fertig ist man, wenn T nur noch aus einer einzigen Komponente besteht.
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 21
Algorithmik: K RUSKAL
Kruskal Implementierung
Nach und nach die Minima aus einer Menge zu entnehmen können wir schon! (PriorityQueue)
Aber wie können wir geschickt feststellen, ob eine ausgewählte Kante zu einem
Kreis führt oder zwei bisher getrennte Komponenten verbindet?
• Jede ungerichtete Kante kann man als zwei gerichtete Bögen darstellen.
• Die Bögen sind nichts anderes als geordnete Paare.
• Wir können diese geordneten Paare als Teil einer Verbunden-mit-Relation betrachten.
• Wir nehmen zudem an, dass jeder Knoten mit sich selbst verbunden ist.
• ... und ergänzen die Relation um Paare, die aus der Transitivität der Relation
entstehen.
Wir betrachten die Kanten ja nach und nach. Zu jedem Betrachtungszeitpunkt bestimmt die Kantenmenge eine Relation, die die Knoten V in Äquivalenzklassen
zerlegt.
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 22
Algorithmik: K RUSKAL
Spannende Bäume, Teil 2
• Jede Prüfung, ob eine Kante uv zu einem Kreis führen würde, können wir nun
in die Frage übersetzen, ob u und v in der gleichen Äquivalenzklasse sind (also
schon verbunden sind).
• Wenn das nicht der Fall ist, dann fügen wir die Kante uv hinzu. Für unsere
“Verbunden-mit”-Relation bedeutet dies, dass wir (u, v) und (v, u) hinzufügen –
und alle Paare, die aus der Transitivität der Relation folgen.
• Das brauchen wir aber gar nicht wirklich zu tun, denn u ist in einer Äquivalenzklasse U und v in einer Äquivalenzklasse V mit U 6= V . Durch Schaffen einer
Verbindung zwischen U und V folgt mit der Transitivität, dass sich einfach eine
neue Äquivalenzklasse U ∪ V bildet, die u und v enthält.
• Dies entspricht genau dem Zusammenfügen zweier Komponten in T !
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 23
Algorithmik: K RUSKAL
Spannende Bäume, Teil 2
Wir können das Problem also auch mit Hilfe von Äquivalenzklassen betrachten:
• Test, ob uv zu einem Kreis führt: sind u und v in der gleichen Äquivalenzklasse?
• Verschmelzen von Komponenten: Vereinigung von Äquivalenzklassen
Das kann man als Operationen auf Zerlegungen beschreiben (zur Erinnerung: eine
Zerlegung P einer Menge M besteht aus Mengen, die überschneidungsfrei und
nichtleer sind und vereinigt M ergeben)
• z = find(P ,k): Liefert das Element der Zerlegung P , in dem sich k befindet
• P = union(P ,z1,z2): Vereinigt z1 und z2 aus P und liefert das “neue” P zurück
Beachten Sie, dass P ein Mengensystem ist, Elemente aus P also Mengen sind.
Die folgende Implementierung verwendet allerdings Elemente dieser Mengen als
Repräsentanten für die Mengen (so, wie jedes Element einer Äquivalenzklasse als
Repräsentant der ganzen Klasse gewählt werden kann).
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 24
Algorithmik: K RUSKAL
Union-Find
Init(P)
For i ← 0 to n do P[i] ← i
Union(P,i,j)
Random z in [0,1]
if z = 0 then P[i] ← j else P[j ] ← i
Find(P,i)
if i =P[i] then return i
else j ← Find(P,P[i])
P[i] ← j
return j
[s. auch die Informationen zu TARJAN]
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 25
Algorithmik: K RUSKAL
Eine Verbesserung
• Die obige Implementierung verwendet bereits die sogenannte Pfadkompression
(s. Mitschrieb)2
• Sie wählt allerdings zufällig aus, ob zwei Komponenten i und j durch j oder
durch i repräsentiert werden
• Diese Wahl kann man auch bewußt treffen:
– In einem weiteren Array wird gespeichert, wieviele Elemente sich hinter einem
repräsentierenden Element verbergen
– Dann wird die kleinere Menge zur größeren hinzugefügt (d.h. jedes Element
zeigt dann auf den Repräsentanten der größeren Menge)
– Natürlich muß man dann auch noch die Information zur Elementzahl updaten
– Noch simpler (aber fast genau so gut und leichter zu analysieren) ist ein Ranking: für jeden Knoten gibt der Rank ein obere Grenze für die Höhe des Knotens an
2 die Elemente einer Äquivalenzklasse zeigen also direkt auf den Repräsentanten der Klasse (sonst könnte aus dem
nach und nach erfolgenden Verschmelzen von Klassen eine “tiefe” Baumstruktur folgen, die es teurer macht, die Frage
nach dem Repräsentanten der Klasse, in der ein gegebenes Element ist, zu beantworten, s. Übung.)
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 26
Algorithmik: K RUSKAL
Init(P)
For i ← 0 to n do P[i] ← i; rank[x] ← 0
Union(P,i,j)
if rank[i] < rank[j ] then P[i] ← j else
P[j ] ← i
if rank[i] = rank[j ] then rank[i] ← rank[i] + 1.
Find-Worst-Case: O(log n), Amortisationsanalyse für eine beliebige Folge von
O(n) Union und Find Operationen führt zu O(n log∗ n) (das ist praktisch linear
– s. Mitschrieb, Details s. z.B. Cormen et. al)
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 27
Algorithmik: K RUSKAL
Spannende Bäume, Teil 2
Algorithmus K RUSKAL:
Input: Zusammenhängender, ungerichteter Graph G = (V, E)
mit Gewichten w und n Knoten
Output: Minimaler Spannbaum T zu G
Init(P );
Sei T ← (V, ∅); Füge alle Kanten aus E in die PQueue Q ein;
kantenanzahl ← 0;
while kantenanzahl < n − 1 do
vw ← deleteMin(Q);
a ← find(P ,v ); b ← find(P ,w);
if a 6= b then
insert(T ,vw);
P ← union(P ,a,b);
kantenanzahl ← kantenanzahl+1;
end if
end while
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 28
Algorithmik: K RUSKAL
Spannende Bäume, Teil 2
Analyse des K RUSKAL-Algorithmus (n = Knotenanzahl, m = Kantenanzahl):
• Initialisierung von P und T :
O(n)
• Initialisierung von Q:
O(m log m) (geht natürlich auch in O(m))
• Schleife:
– maximal m deleteMin
O(m log m)
– maximal m find und maximal n union
Je nach Implementierung O(n log n + m) oder
O(m log n)
Da wir angenommen haben, dass G zusammenhängend ist, gilt m ≥ n − 1, insgesamt folgt also ein Aufwand von O(m log m).
c 2005, Dr. W. Conen — Nutzung nur an der FH Gelsenkirchen
Version 0.9β , 1. Mai 2005, Seite 29
Herunterladen