3 Persistente dynamische Mengen

Werbung
1 Rot-Schwarz-Bäume
Rot-Schwarz-Bäume stellen eine Implementierung von binären Suchbäumen dar, mit
einer Worst-Case-Laufzeit von O(log n). Jeder Knoten hat eine Farbe (rot oder
schwarz).
Ein Rot-Schwarz-Baum ist balanciert, da durch die Farbregeln sichergestellt wird,
das kein Pfad mehr als doppelt so lang ist, wie ein anderer.
1.1 Eigenschaften von Rot-Schwarz-Bäumen
-
jeder Knoten ist rot oder schwarz
die Wurzel des Baumes ist schwarz
jedes Blatt (NIL) ist schwarz
die Kinder von roten Knoten sind schwarz
alle Blätter haben die selbe Schwarztiefe (der Knoten an dem man sich
befindet zäht nicht mit)
Jeder neu eingefügte Knoten ist erstmal rot, danach wird durch Rotationen die
Rot-Schwarz-Eigenschaften wieder hersgestellt, falls diese beim Einfügen des
neuen Knotens verletzt wurden.
Der Sentinel-Knoten ist immer schwarz.
1.2 Rot-Schwarz-Bäume <-> (2,4)-Bäume
a) RS-Baum -> (2,4)-Baum
jeder rote Knoten wird in seinen Vaterknoten gemischt
b) (2,4)-Baum -> RS-Baum
siehe Skript
1.3 Rotation
Die Rotation dient zur Wiederherstellung der Rot-Schwarz-Eigenschaften des
Baumes, sie werden ggf. nach Insert- oder Delete-Anweisungen notwendig.
Rotationen sind lokale Operationen, sie verändern die SuchbaumEigenschaften nicht.
1.4 RB-Insert-Fixup(T,x)
-
neuen Knoten einfügen und rot färben
welche rs-Eigenschaften wird verletzt; beim Einfügen von Knoten können
nur Bedingung 2 oder 4 verletzt werden.
Bedingung 2 ist verletzt, wenn der eingefügte Knoten die Wurzel ist
Bedingung 4 ist verletzt, wenn der Vater des eingefügten Knotens rot ist
2 Treaps
Problem: einzufügende Elemente treffen gut sortiert nacheinander ein, dadurch
degeneriert der Baum.
Ein Treap ist ein binärer Suchbaum mit einer modifizierten Knotenordnung
Jeder Treap-Knoten wird gekennzeichtnet durch einen Schlüssel und durch einen
Wert Priorität, der für jeden Knoten unabhängig zufällig gewählt wird.
Die Treap-Knoten sind so geordnet, das Ihre Schlüssel der binären
Sucheingenschaft genügen und ihre Prioritäten der Min-Heap-Eigenschaft:
- ob Knoten rechtes oder linkes Kind ist bestimmt der Schlüssel
- nach unterhin stegende Prioritäten
Rotiert wird bei Verletztung der Min-Heap-Eigenschaft
3 Persistente dynamische Mengen
Ziel ist es, frühere Versionen von Mengen zu erhalten (Persistent). Eine Möglichkeit
wäre, vor jeder verändernden Operation die Menge zu kopieren. Dies benötigt
allerdings viel Zeit und Speicherplatz.
Eine schnellere Möglichkeit ist, nur einen Teil der Menge zu kopieren und dann die
neuen Elemente einzufügen. Ein teil der Knoten stammen dann noch aus dem
original Baum.
Probleme:
Angenommen, die Knoten der Menge speichern nur die Felder key, left, right; somit
keine Referenz auf den Vater.
1.
Man muss alle Knoten identifizieren, welche bei einer verändernden
Operation betroffen sind.
2.
3.
Man muss eine Methode schreiben, welche aus dem Baum T einen neuen
Baum T’ generiert, in welchen der neue Schlüssel k eingefügt wird.
Welche Zeit- und Speicherkomplexität hat diese Methode?
4 Erweitern von Datenstrukturen
Um Standard-Datenstrukturen an reale Probleme anzupassen, müssen diese oft
angepasst werden. Meistens reicht es, Operationen hinzuzufügen.
4.1 Dynamische Ordnungsstatistik
Zwei häufige benötigte Informationen über Elemente dynamischer Mengen
sind:
1)
den Rang eines Elementes in einer total geordneten Menge zu
bestimmen
2)
ein Element mit einem bestimmten Rang zu finden
Es gibt verschiedene Lösungsnöglichkeiten, welche sich besonders in der
Laufzeit unterscheiden.
Die einfache Brute-Force-Methode löst anfragen in O(n2)-Laufzeit. Ein
schnellerer Algorithmus löst das Problem nicht-deterministisch
(zufallsgesteuert) mit einem Decrease & Conquer-Verfahren in O(n) Zeit.
Durch die Verwenung geeigneter Datenstrukturen, wie z.B. Rot-SchwarzBäume, kann das i-te Element in O(log n)-Zeit gefunden werden.
Die Abbildung zeigt eine Datenstruktur, die schnelle ordnungsstatistische
Operationen unterstützt. Das Feld „Größe“ enthält die Knotenanzahl des
jeweiligen Teilbaum.
Verwendet man einen Rot-Schwarz-Baul als Struktur, werden folgende
Informationen in den Knoten gespeichert:
-
key[x]
color[x]
p[x]
left[x]
right[x], zusätzlich noch
size[x], enthält die Anzahl innerer Knoten (inkl.x)
Die Größe des Sentinel-Knoten (NIL) definieren wir mit 0, somit ergibt sich für
die Berechnung der Größe eines Knotens folgende Formel:
size[x] = size [left[x]] + size[right[x]] + 1
4.1.1 Suchen eines Elementes im Baum
Nun entwickelt man einen Algorithmus, welcher das I-kleinste Element der
Menge sucht ( OS-Select(x,i) ).
In Zeile 1 wird der Rang von x berechnet und dann in Zeile 2 mit dem
gesuchten Element verglichen. Stimmen beide Wert überein, haben wir das ikleinste Element schon gefunden.
Ist i kleiner als der Rang von x, wird im linken Teilbeum weiter gesucht.
Ist i größer als r, wird im rechten Teilbaum gesucht. Hierbei ist x das i-rkleinste Element im rechten Teilbaum.
Ein Beispiel gibt es im Skript, Folie K2.4.1 Seite 5)
4.1.2 Rang eines Elementes Bestimmen
Der folgende Algorithmus dient zum bestimmes des Ranges eines Elementes
(lineare Position bei In-Order-Traversierung).
Rang x = Anzahl der Knoten die bei In-Order-Traversierung vor dem Knoten
liegen + 1.
Der Algortithmus arbeitet sich vom Knoten x zur Wurzel hoch. Da jede
Iteration der while-Schleife O(1) Zeit benötigt, ist die Laufzeit des Algortithmus
proportional zur Baumhöhe, als O(log n).
Neuberechnung der Baumgröße
Damit die beiden oben genannten Algorithmen eine gute Performance haben
ist es wichtig, die Baumgröße bei Änderungen effizient zu aktualisieren.
Die Insert-Operation bei Rot-Schwarz-Büumen besteht aus zwei Phasen:
- Suchen der Einfügestelle und Einfügen des Knotens
- Den Suchpfad zurückverfolgen und die Rot-Schwarz-Eigenschaften ggf.
wieder herstellen.
Um die größe der Teilbäume zu aktualisieren, wird beim Einfügen auf dem
Suchpfad der Wert size[x] bei jedem Knoten inkrementiert.
Bei den Rotationen in der zweiten Phase können höchstens zwei size-Werte
inkorrekt werden. Diese werden bei der Rotation aktuakisiert.
Da beim Einfügen in Rot-Schwarz-Bäumen höchstens zwei Rotationen nötig
sind und das Ändern der size-Felder in O(1) statt findet, benötigt das Einfügen
weiterhin nur O(log n)-Zeit.
Das Löschen findet dazu analog auch in O(log n)-Zeit statt.
4.1.3 Erweitern von Datenstrukuren
1. Auswählen einer geeigneten Datenstruktur
2. Entscheiden, welche zusätzlichen Informationen in die BasisDatenstruktur aufgenommen werden müssen.
3. Verifizieren, das die zusätzlichen Informationen von den bestehenden
Basisoperationen korrekt und effizient verwaltet werden.
4. Entwickeln neuer Operationen.
4.2 Intervall-Bäume
Besonders geeingnet für die Darstellung von Zeitperioden
Geschlossene Intervalle enthalten ihre Grenzen.
Intervall-Trichometrie, d.h. es gelten folgende Eigenschaften:
- i und i’ überlappen sich
- i liegt links von i’
- i liegt rechts von i’
Drei Operationen auf Intervallbäumen:
- Intervall-Insert(T,x)
- Intervall-Delete(T,x)
- Intervall-Search(T,x)
4.2.1 Struktur und Knoteninformationen
Damit die Operationen auf dem Intervall-Baum logarithmische Laufzeit haben,
werden dem Rot-Schwarz-Baum folgende zusätzliche Informationen
hinzugefügt:
- Intervall int[x]
- Anfangspunkt left[x]
- Endpunkt right[x]
- Maximaler Endpunkt max[x]
Als Schlüssel des Knoten x wählen wir den kleinsten Wert des Intervalls (lox
endpoint). Somit liefert eine In-Order-Traversierung des Baumes die Intervalle
in aufsteigender Sortierung.
Der max[x]-Wert eines Knotens speichert den maximalen Endpunkt (right[x])
von dem Teilbaum unter x.
4.2.2 Intervall-Search
Einzige neue Methode ist Intervall-Search(T,i), welche einen Knoten im Baum
findet, dessen Intervall sich mit dem Intervall i überschneidet.
Die Suche nach dem Intervall beginnt bei der Baumwurzel und steigt dann den
Baum hinab. Die Suche endet, wenn kein überlappendes Intervall gefunden
wurde, beim Sentinel-Knoten. Da auch hier jede Iteration der while-Schleife
O(1)-Zeit benötigt, läuft Intervall-Search im Worst-Case mit O(log n) ab.
Die Suche beginnt bei der Wurzel.
Ist der low[x]-Wert kleiner als der max[x]-Wert des linken Knotens, wird die
Suche im linken Teilbaum fortgesetzt. Andernfalls im rechten Teilbaum.
Überschneiden sich nun die beiden Intervalle, wird der Knoten
zurückgegeben, ansonsten wird die Suche nach selbem Schema fortgesetzt.
Grundidee von Intervall-Search
Die Grundidee liegt darin, das es bei der Suche immer nur eine „sichere“
Richtung gibt, in der die Suche fortgesetzt wird.
5 Dynamische Programmierung
Die Grund idee der dynamischen Programmierung besteht darin, ein Problem durch
Teilprobleme unter Benutzung kleiner Integer-Indizes-Mengen zu charakterisieren.
Das Zeil dieser Art der Problemcharakterisierung ist es, eine optimale
Subproblemlösung durch die Kombination der (möglicherweise sich überlappenden)
Lösungen kleinerer Probleme zu definieren.
Im Gegensatz zu D&C-Algorithmen sind bei der dynamischen Programmierung die
Subprobleme voneinander abhängig, d.h. Subprobleme teilen sich gemeinsam
Subsubprobleme.
5.1 Vier Schritte eines DP-Algorithmus
-
Charakterisiere die Struktur einer optimalen Lösung
Definiere rekursiv den Wert einer optimalen Lösung
Berechne bottom-up den Wert einer optimalen Lösung
Konstruiere eine optimale Lösung aus den berechneten Informationen
Die Schritte 1-3 sind die Basis für eine DP-Lösung eines Problems. Schritt 4
entfällt, wenn nur der Wert einer optimalen Lösung gsucht wird. Für Schritt 4
müssen manchmal zusätzlice Informationen während der Ausführung von
Schritt 3 bereitgestellt werden, um die Konstruktion einer optimalen Lösung zu
erleichtern.
//TODO
5.2 Elemente der dynamischen Programmierung
Für die dynamische Programmierung sind zwei Schlüsseleigenschaften von
bedeutung:
-
optimale Substruktur
sich überlappende Subprobleme
5.2.1 Optimale Substruktur
Ein Problem besitzt dann eine optimale Substruktur, wenn eine optimale
Lösung des Originalproblems die optimalen Lösungen von Subproblemen
enthält.
D.h. man trifft eine Auswahl, welche das Originalproblem teilt. Diese Auswahl
führt dann zu einem oder mehreren zu lösenden Subproblemen. In diesem
Schritt wird aber noch nichts darüber gesagt, wie die Auswahl getroffen wird.
Es wird lediglich angenommen, das diese Auswahl zu einer optimalen Lösung
führt.
Danach wird geprüft, welche Subprobleme aus der Auswahl folgen und wie
der resultierende Subproblem-Raum am besten beschrieben wird.
Nun muss man zeigen, das die in der optimalen Lösung benutzten
Subproblemlösungen selbst optimal sind (Verwendung der „cut-and-paste“ –
Methode)
„Cut-and-Paste“ – Methode
In dieser Technik nimmt man an, das jede Subproblemlösung nicht optimal ist und leitet aus
dieser Annahme einen Widerspruch her.
Durch das Ausschneiden nicht-optimaler Subproblemlösungen („cutting out“) und das Einsetzen
einer optimalen Lösung („pasting in“) zeigt man, dass man zu einer besseren Lösung des
Originalproblems kommt, was aber dann zum Widerspruch zu der Annahme einer optimalen
Lösung führt.
Die optimale Substruktur kann im Problemraum auf zwei Arten variieren:
1. Wie viele Subprobleme werden in der optimalen Lösung des
Originalporblems genutzt?
2. Wie viele Auswahlmöglichkeiten gibt es bei der Entscheidung,
welche Subprobleme für die optimale Lösung genutzt werden?
Beispiele:
Beim Fertigungsstrassen-Scheduling wird für die optimale Lösung nur ein
Subproblem (schnellster Weg zur vorherigen Station) benutzt, aber es
müssen zwei Auswahlmöglichkeiten (vorherige Station auf Band A oder
B) betrachtet werden.
Bei der Matrixketten-Multiplikation werden zwei Subprobleme (Problem wird
in zwei Teile aufgeteilt) betrachtet und i-j Auswahlen (es gibt i-j
Splittpunkte k).
5.2.2 Laufzeit von DP-Algorithmen
Die Laufzeit von DP-Algorithmen hängt vom Produkt zweier Faktoren ab:
-
der Gesamtzahl der Subprobleme und
der Anzahl der Auswahlmöglichkeiten, die bei jedem Subproblem zu
betrachten sind.
Beim Scheduling-Beispiel gibt es O(n) Subprobleme und für jedes Subproblem
zwei Auswahlmöglichkeiten, also O(n).
Bei der Matrix-Multiplikation gibt es O(n2) Subprobleme und für jedes
Subproblem n-1 Auswahlmöglichkeiten, also O(n3).
5.2.3 DP-Programmierung vs. Greedy-Algortithmen
Greedy-Algorithmen haben viele Ähnlichkeiten mit DP-Algorithmen. Beide
Vorgehensweisen lösen ihr Problem über optimale Substrukturen.
Bei Greedy-Algorithmen wird die optimale Substruktur allerdings in einer
„top-down“ – Art benutzt. Statt zuerst optimale Lösungen für Subprobleme zu
finden und dann eine Auswahl zu treffen, treffen Greedy-Algorithmen zuerst
eine Auswahl (nach dem Greedy-Kriterium).
Es wird diejenige Auswahl getroffen, die momentan am günstigsten erscheint.
Mit der getroffenen Auswahl wird dann ein resultierendes Subproblem gelösst.
DP-Algorithmen dagegen nutzen einen „bottom-up“ – Ansatz. Zuerst
werden die optimalen Lösungen der Subprobleme gesucht. Nachdem diese
gelöst sind, wird die optimale Lösung des Originalproblems konstruiert.
5.2.4 Überlappende Subprobleme
Die zweite wichtige Eigenschaft, die ein Optimierungsproblem haben muss,
um für eine Lösung durch dynamische Programmierung in Frage zu kommen,
ist die, dass der Raum der möglichen Subprobleme „klein“ sein soll.
Ein rekursiver Algorithmus löst für das Problem die Subprobleme immer
wieder von Neuem, statt neue Subprobleme zu generieren. Überlappen sich
nun zwei Subprobleme, ist es sinnvoll, DP zu verwenden.
Dagegen generier ein D & C – Algorithmus in jedem Rekursionsschritt völlig
neue Subprobleme.
Ein DP-Algorithmus nutzt sich überlappende Subprobleme aus, um jedes
Subproblem genau einmal zu lösen und die Lösung in einer Tabelle zu
speichern, auf die in konstanter Zeitz zugegriffen werden kann, wenn ein Wert
benötigt wird.
Vergleicht man den rekursiven „top-down“ – Algorithmis mit dem „bottom-up“ – DPAlgoritmus, stellt man fest, das letzterer effizienter ist.
Dies liegt daran, da er die Vorteile von sich überlappenden Subproblemen ausnutzt,
indem er sich die Lösungen von Subproblemen in einer Tabelle merkt.
Der rekursive Algorithmus hingegen löst jedes Subproblem mehrmals und zwar jedes
Mal, wenn es im Rekursionsbaum vorkommt.
Faustregel:
Immer dann, wenn ein Rekursionsbaum für die natürliche rekursive Lösung eines
Problems dieselben Subprobleme mehrmals enthält, sollte man prüfen, ob eine
Lösung durch dynamische Programmierung in Frage kommt.
5.2.5 Rekonstruktion einer optimalen Lösung
Aus praktischen Gründen ist es sinnvoll, auch die Entscheidungen, die bei der
Lösung der Subprobleme gemacht werden, in einer Tabelle zu speichern (vgl.
die l-Tabelle beim Scheduling).
Somit müssen die Auswahl-Informationen nicht rekonstruiert werden, sondern
stehen in O(1) zur Verfügung.
Eine weitere Idee der dynamischen Programmierung ist es, einen Algorithmus
mit einem Gedächtnis zu verstehen.
Somit erhölt man die Effizienz des DP-Ansatzes bei gleichzeitiger Anwendung
der „top-down“ – Strategie.
Beim Matrix-Problem füllt man einfach die Tabelle bei Beginn mit einen
bestimmten Wert, der angibt das das Ergebnis noch nicht berechtet wurde.
Wird ein Subproblem nun das erste Mal gelöst, wird es in der Tabelle
gespeichert und steht ab jetzt für weitere Berechnungen zu Verfügung.
5.3 Schlussfolgerungen
Wenn alle Subprobleme mindestens einmal gelöst werden müssen, dann ist
ein „botom-up“ – DP-Algorithmus um einen konmstanten Faktor besser als ein
„Gedächtnis“-Algorithmus, weil es keinen Rekursions-Overhead und weniger
Verwaltungs-Overhead für die Tabelle gibt.
Wenn allerdings einige Subprobleme im Subproblemraum überhaupt nicht
gelöst werden müssen, dann hat die „Gedächtnis“-Methode Vorteile, weil nur
diejenigen Subprobleme zu lösen sind, die auch definitiv gebraucht werden.
6 Greedy – Algorithmen
Die Idee der Greedy-Algorithmen besteht darin, aus einer Menge von optimalen
Teillösungen eine optimale Lösung für das Originalproblem zu konstruieren. Um zu
einer optimalen Lösung zu gelangen, wird eine Folge von Teillösungen ermittelt, die
aus lokaler Sicht optimal sind.
Der Greedy-Ansatz muss nicht notwendigerweise zu einer optimalen Lösung führen.
Es gibt Probleme, deren Lösung nicht optimal sein muss. Begnügt man sich mit einer
suboptimalen Lösung, so kann diese mit einem oft sehr schnellen Greedy-Verfahren
erreicht werden.
6.1 Dynamische Programmierung vs. Greedy-Algorithmus
Bei der dynamischen Programmierung werden für eine optimale Lösung
mehrere Subprobleme betrachtet.
Beim Greedy-Verfahren wird hingegen nur ein Subproblem betrachtet; man
nennt das ausgewählte Subproblem auch Greedy-Auswahl.
Nach der Greedy-Auswahl bleibt nur ein eiziges nicht-leeres Subproblem
übrig.
6.1.1 Greedy-Lösung
In der Greedy-Lösung wird nur noch ein Subproblem in der optimalen Lösung
betrachtet. Das anbdere ist garantiert leer, das es schon gelöst wurde und
somit der optimalen Gesamtlösung hinzugefügt würde.
Somit kann jedes Subproblem mit einer „top-down“-Strategie gelöst werden.
Ein Greedy-Algorithmus trifft eine Entscheidung nach einer heuristischen
Strategie. Es wird diejenige Auswahl getroffen, die im Moment am besten
erscheint. Diese Strategie liefert aber nicht immer die optimale Lösung.
Bei der Suche nach einer geeigneten Strategie sollte man auf zwei
Schlüsseleigenschaften besonders achten:
- die Greedy-Auswahl
- die optimale Substruktur
Der Bottom-Up-Ansatz bei der dynamischen Programmierung verlangt,
das eine optimale Lösung des Original-Problems erst dann konstruiert werden
kann, wenn die optimalen Lösungen der Subprobleme bekannt sind.
Durch den Top-Down-Ansatz bei Greedy-Algorithmen kann ein Objekt zur
optimalen Lösung des Original-Problems hinzugefügt werden schon bevor die
optimalen Lösungen der Subprobleme bekannt sind.
Die Voraussetzung für eine Greedy-Auswahl schafft in vielen Fällen eine Ordnung
der Objekte.
7 Flüsse in Netzwerken
7.1 Der Fluss im Netzwerk
Ein Fluss in einem Netzwerk hat die Kapazitätsfunktion c, eine Quelle q und
eine Senke s.
Eigenschaften:
-
Kapazitätsbeschränkung
Erhaltung des Flusses, d.h. die Kapazitäten die in einen Knoten rein
fliessen müssen ihn auch wieder verlassen.
7.1.1 Wert eines Flusses
Der Wert eines Flusses ist definiert als die Summe der Flusswerte aller die
Quelle q verlassenden Pfeile
7.1.2 Maximaler Fluss
Ein maximaler Fluss in einem Graphen G ist ein Fluss f mit maximalem Wert
|f| unter allen Flüssen in G.
7.2 Trennender Schnitt
???
7.2.1 Kapazität des Schnittes
Die Kapazität des Schnittes ist die Summer der Pfeile, die von Q nach S
führen.
7.2.2 Minimaler Schnitt
Ein Schnitt mit kleinster Kapazität unter allen möglichen Schnitten heißt
minimaler Schnitt.
7.2.3 Fluss über einen Schnitt
Die Summe aller Pfeile die von Q nach S führen abzüglich der Summe aller
Pfeile, die von S nach Q führen.
7.3 Der Satz von Ford und Fulkerson
Der Fluss in einem Netzwerk kann niemals größer sein, als der Fluss über
einen beliebigen Schnitt im Netzwerk.
Daraus folgt, das der minimale Schnitt gleichzeitig auch der maximale Fluss in
einem Netzwerk ist.
Min Cut = Max Flow
7.3.1 Zunehmender Weg
Ein Weg von q nach s, auf dem ohne Rücksicht auf die Pfeilrichtungen (ein
ungerichteter Weg) der Fluss erhöht werden kann, heißt zunehmender Weg.
Also alle Pfeile, auf denen der Fluss erhöht (f(e) < c(e)) bzw.
verringert (f(e) > 0) werden kann.
7.3.2 Restgraph
Der Restgraph enthält alle zunehmende Wege eines Graphen.
Somit beschreibt der Restgraph alle Möglichkeiten der Flussvergrößerung:
-
er enthält einen Pfeil e, wenn der rest(e) > 0 ist
er enthält den e entgegen gerichteten Pfeil, wenn f(e) > 0.
Der Restgraph enthält damit die Informationen, um wieviel der Fluss zwischen
Knoten x und y noch erhöht und/oder gesenkt werden kann.
WICHTIG:
Man kann nun zeigen, das ein Fluss f genau dann maximal ist, wenn es für f
keinen zunehmenden Weg gibt, und dass genau dann der Wert des Flusses f
der Kapazität des Minimalen Schnittes entspricht.
7.3.3 Laufzeit von Ford und Fulkerson
Die Laufzeit des Algorithmus hängt von den Knoten, Kanten und der
Kapazität ab.
Dadurch zeigt sich, das der Algorithmus sehr ineffizient arbeitet, weil er
möglicherweise zunehmenden Wegen folgt, die nur eine geringe
Flusserhöhung zulassen.
Verbesserungsidee:
Bei der Flussvergrößerung wählt man nicht beliebige zunehmende Wege,
sonder immer den kürzesten zunehmenden Weg.
Somit wird nach höchstens |E| Schleifendurchläufen die Anzahl der Pfeile um
min. 1 erhöht.
Die maximale Länge des kürzesten Weges ist |V| - 1 (sonst gäbe es Zykel).
7.3.4 Blockierender Fluss
Ein Fluss, der auf einem zunehmendem Weg nicht mehr zu vergrößern ist,
heißt blockierender Fluss.
Man betrachtet somit nicht mehr einen beliebigen Weg im Restgraph zur
Flusserhöhung, sondern immer nur den/die kürzesten Wege.
Diese werde im Niveagraphen dargestellt.
7.4 Niveagraph (Edmonds-Karp)
Der Niveagraph ist ein Teilgraph des Restgraphen und enthält nur solche
Pfeile, die von q aus erreichbar sind und auf dem kürzesten Weg liegen.
Der Niveagraph enthält jeden kürzesten vergrößernden Weg, aber nicht
unbedingt jeden vergrößernden Weg.
Die Wege lassen sich mit der Breitensuche in O(|E|) Zeit konstruieren.
Laufzeit:
Gegeben sei ein Netzwerk mit n Knoten und m Kanten. Der Edmonds-Karp
Algorithmus berechnet den maximalen Fluss in O( n * m 2) Zeit.
Wie findet man am schnellsten einen blockierenden Fluss??
Man wählt einen Weg von q nach s und erhöht den Fluss so, das eine Kante
auf dem Weg gesättigt ist. Dann entfernt man alle gesättigten Pfeile. Man
wiederholt dies solange, bis s nicht mehr von q erreichbar ist.
Das finden eines Weges von q nach s ist eine Tiefensuche. Der gefundene
Weg ist höchstens |V| - 1 Kanten lang. Die Flusserhöhung ist nun die kleinste
Restkapazität der Pfeile auf dem Weg.
Alle Restkapazitäten der Pfeile auf dem Weg werden angepasst, die gesättigte
Kante wird entfernt.
Da bei jeder Flussvergrößerung mindestens eine Kante entfernt wird, entsteht
nach höchstens |E| Flussvergrößerungen ein blockierender Fluss.
Herunterladen