So. Guten Morgen. Wir sind ja im Bereich der Flussalgorithmen.
Wir haben in der letzten Vorlesung sowohl die theoretischen Grundlagen gesehen und das Wichtigste natürlich,
dass Max Flow, Minkatheorie,
über das wir die Korrektheit der Algorithmen zeigen können,
indem wir einfach wissen,
dass ein Zusammenhang besteht zwischen dem maximalen Fluss in einem Netzwerk und dem minimalen Schnitt.
Das ist das eine jeweils eine Schranke für das andere ist und
dass wenn wir dann in einem Algorithmus sowohl den einen Schnitt finden als auch im Fluss,
dass wir dann wissen, dass der Algorithmus eben das Maximum berechnet hat.
Das war ganz wichtig und dann haben wir uns dieses Konzept von Ford Fulker,
sind wir mal genau angeschaut, was ja daraus basiert,
dass man ein Netzwerk nimmt und darin immer einen Pfad sucht und wenn über diesen Pfad noch eine Restkapazität ist,
dann haben wir diese Restkapazität ausgeschöpft und den Fluss entsprechend erhöht.
Und auf die Art und Weise konnten wir dann zeigen,
dass wir auf alle Fälle einen gültigen maximalen Flussalgorithmus bekommen.
Dann haben wir es mit der Laufzeitabschätzung beschäftigt und das hat
uns zum Edmunds Carpe-Algorithmus geführt
Der genauso vorgeht wie Ford Fullkassen,
nur er weist eben darauf hin,
dass wir auf alle Fälle entlang kürzester Pfade diese Augmentierung vornehmen sollen,
also Erhöhung des Flusses vornehmen sollen.
Das ist wichtig, weil wir nur dann eine gute Laufzeitschranke garantieren können.
Und da sind wir zu einem Algorithmus gekommen mit Laufzeit,
V mal E, Quadrat.
Das ist der Edmonds Carp-Algorithmus für Flüsse. Gut, bis
dahin. Jetzt schauen wir uns einen zweiten Algorithmus an,
den habe ich auch schon eingeführt
Das ist der DNH-Algorithmus,
der quasi parallel zu dem Edmond-Ska-Verfahren eingeführt oder auch publiziert wurde.
Und Berlinschalgorithmus kommt unglücklicherweise im Kormen so nicht vor.
Also insofern müssen sie hier gut zuhören. Sie haben die
Folien, sie finden natürlich Informationen in anderen Büchern dazu.
Aber das können sie halt im Kormen so nicht nachlesen.
Und ich werde ihnen am Ende dieses Kapitels auch noch mal sagen,
warum mir gerade dieser Algorithmus so wichtig ist.
Für die Berechnung von Flüsse. Okay,
was macht Dienic anders
als Edmonds Carb
Im Prinzip ist auch dienisch so ein Argumentierungsverfahren.
Das heißt, wir starten mit einem leeren Fluss.
Hier ist der Pseudo-Code der Grobe für den groben Ablauf.
Alle Kanten kriegen erstmal den Fluss null.
Und dann haben wir auch so ein Argumentierungsschleife hier,
also solange wir einen argumentierenden Fluss finden.
Gehen wir über die Kanten und führen dieses Augumentieren durch.
Das heißt, wir addieren einfach diesen zusätzlichen Fluss.
Das ist hier so ein blockierender Fluss. FW auf den Fluss,
den wir schon haben drauf,
beziehungsweise wenn der Fluss im resitualen Netzwerk in der Gegenrichtung läuft,
dann müssen wir
ihn natürlich
so trahieren
Okay, der Unterschied zwischen Ed Mons Carp und Deenage ist,
dass der Deenage-Algorithmus nicht entlang von einzelnen Faden augmentiert,
sondern quasi tatsächlich einen Fluss schon berechnet, der dann als Ganzes addiert wird.
Und vielleicht erinnern Sie sich ganz am Anfang des Theorieteils,
da haben wir gezeigt, dass wir Flüsse tatsächlich addieren können
und dass das wieder zu einem gültigen Fluss hört.
Und da habe ich mal gesagt, dass wir eigentlich erst mal nur Pfade betrachten,
aber der Beweis, den haben wir trotzdem für Flüsse geführt.
Also dass wir Flüsse zu Flüssen addieren können. Und hier sehen Sie jetzt den Grund,
weil das macht der Deenage-Algorithmus, er berechnet keinen Pfad,
sondern in dem residualen Netzwerk Wiedereinfluss und den können wir als Ganzes
drauf addieren auf den Fluss, den wir schon haben.
Diesen Fluss, der hier berechnet wird,
das ist ein sogenannter Blockierender Fluss und die Idee ist,
dass wir quasi nicht den einen kürzesten Pfad anschauen oder einen der kürzesten Pfade,
sowie das der Edmonds Carp-Algorithmus macht,
sondern wir gucken uns im Netzwerk alle kürzesten Pfade gleichzeitig an.
Und dann versuchen wir, einen Fluss zu konstruieren,
der dieses Netzwerk von kürzesten Pfaden in einem Schritt saturiert,
also in einem Schritt quasi dafür sorgt,
dass diese kürzesten Pfade alle saturiert sind und dann über die kein weiterer Fluss mehr entstehen kann.
So intuitiv, wenn man darüber nachdenkt, ist das,
klingt das nach einer sehr guten Strategie, weil das,
was in dem Edwards Carb-Algorithmus ja irgendwie langsam ist,
ist, dass man jedes Mal wieder neu einsamen Fahrt sucht,
über den argumentiert, da muss man wieder am Fahrt suchen.
Und wenn man jetzt sagt,
man fasst das so ein bisschen zusammen und versucht quasi alle Pfade,
die zumindest die gleiche Länge haben,
mal in einem Schritt zu berechnen,
dann ist da natürlich ein Potenzial,
dass das Verfahren schneller wird und es wird in der Tat ja auch schneller.
Okay, zur Laufzeit des Dienisch-Algorithmus auf dieser groben Ebene können wir schon sehen.
Das ist natürlich alles dominiert wird durch diese Wild-Schleife hier.
Die Frage, die sich hier stellt, ist erstens,
wie viele Augmentierungsschritte benötigen wir? Und die zweite Frage ist,
welche Zeit benötigen wir, um diesen blockierenden Fluss hier tatsächlich zu berechnen?
Und aus den beiden können wir dann die Laufzeit des Verfahrens ableiten.
Das ist hier offensichtlich in OVNE, diese Vorschleife,
die dann einfach den Fluss aktualisiert.
Der erste Teil für die Laufzeit, das haben wir schon gesehen,
das sieht man ja auch direkt am Algorithmus,
dass die Laufzeit für jeden Blockierungsschritt, den wir ausführen,
gerade OVNE ist.
Das ist halt das Innere dieser Schleife, diese innere Schleife hier.
Okay, so, jetzt haben wir zwei Dinge,
die wir uns anschauen müssen für diese Strategie.
Erstmal müssen wir uns anschauen, wie viele Schleifendurchläufe bekommen wir?
Also wie viele Blockierungsschritte, also wie viele blockierende Flüsse kann es hier geben,
bevor wir den maximalen Fluss tatsächlich gefunden haben?
Und das zweite ist, dass wir natürlich noch herausfinden müssen,
wie berechnen wir denn überhaupt diesen blockierenden Fluss?
Und wie lange wird das
dann dauern
Ist das nochmal eine algorithmische Fragestellung, die wir dann lösen müssen?
Okay, bezüglich der Anzahl der Augmentierungsschritte,
das haben wir uns in der letzten Vorlesung auch schon angeschaut.
Bis genau hier sind wir ungefähr gekommen. Da gibt es
das folgende Lämmer, das besagt,
das werden wir ein Flussnetzwerk haben mit Welle und Senke,
dass dann der Denic-Algorithmus maximal Betrag von V minus 1 Blockierungsschritte benötigt.
Das heißt, diese Wildschleife, die wir gesehen haben,
hat Betrag von V viele Durchläufe im ungünstigsten Fall
Wie kann man das sehen, dass das nur so viele sind?
Das Argument geht wieder über Pfadlängen, genau wie bei Admons und Carb.
Allerdings schauen wir uns jetzt nicht hier individuelle Distanzen an voran von einzelnen Knoten,
sondern wir betrachten mal die Pfadlänge zwischen Quelle und Senke.
Es ist jetzt erst so ein bisschen,
man muss das erst im Kopf so ein bisschen auseinanderkriegen.
Es ist jetzt so komisch, wenn man sich vorstellt,
dass diese Fahrtlänge variiert,
weil man hat natürlich dieses Netzwerk im Kopf,
auf dem man den maximalen Fluss berechnet und da sieht das
ja jetzt erst mal so aus,
als wenn die Knoten in dem Grafen natürlich irgendwie
einen Konstantenabstand haben
Das ist natürlich so, aber das resituale Netzwerk verändert sich ja.
Und im Laufe der Zeit haben wir ja den Effekt,
dass immer mehr Kanten eher zur Quelle zeigen als zur Senke,
weil wir sättigen die und im resitualen Netzwerk bleibt dann nur der Fluss in die andere Richtung.
Wieder zurück zur, oder die Kapazität zurück zur Quelle.
Und dadurch verlängern sich natürlich die Pfade Zwischenquelle und Senke im Laufe des Algorithmus.
Ich werde Ihnen gleich auch noch ein Beispiel zeigen,
an dem man das ganz schön sehen kann.
Okay, Wir können uns überlegen, wie lang diese Pfade maximal sein können. Naja,
dieser Pfad ist einfach und der ist beschränkt durch Betrag von V minus 1. Ja,
also ein einfacher Pfad in einem Grafen kann gar nicht länger sein,
weil wir einfach nur jeden weiteren Knoten auf diesem Pfad legen können.
Und dann haben wir so eine Obergrenze für die Pfadlänge zwischen den beiden.
Er kann natürlich, also die Pfadlänge kann natürlich auch unendlich werden. Das ist dann,
wenn wir den maximalen Fluss erreicht haben,
dann gibt es nämlich einen Schnitt und dann gibt es gar
keinen Pfad mehr zwischen S und T. Aber während der Berechnung bis zu dem Punkt,
wo wir den maximalen Fluss erreicht haben, Fahrtlänge immer endlich.
Es muss noch einen Pfad geben, sonst wären wir fertig,
sonst hätten wir den Schnitt gefunden.
Und wenn es einen Pfad gibt, dann ist er eben
beschränkt durch diese Länge hier. Okay, das heißt,
wir wissen, die Fahrtlänge ist maximal so groß.
Und wie hilft uns das jetzt, wenn wir die Laufzeit berechnen?
Das hilft uns, weil wir können nämlich zeigen,
dass die Pfadlänge zwischen Quelle und Senke in jedem Blockierungsschritt streng monoton steigt.
Also das heißt, sie wird also mindestens um eine Einheit,
also um eine Kanten,
es geht ja hier um Kanten und die Anzahl Kanten auf den Pfad,
um mindestens eins diese Entfernung wachsen.
Und dann folgt daraus natürlich sofort das Lämmer,
wenn wir das gezeigt haben hier, denn wir wissen,
die Fahrtlänge ist beschränkt durch Betrag von V minus 1.
Vorher hat die irgendeinen Wert dazwischen und dann wird das wächst das
streng monoton und dann können das natürlich nur Betrag von V minus 1,
viele Blockierungsschritte werden.
Ja, wie kann man jetzt zeigen,
dass diese Distanz tatsächlich streng monoton steigend ist?
Das ist genau diese Argumentation hier,
die wir in der letzten Vorlesung auch schon besprochen haben und
die ist sehr ähnlich zu dem,
was wir
bereits gesehen haben
In dem Beweis von Edmonds und Carb und das Entscheidende ist,
dass wir uns zwischen der,
zwischen der Quelle und der Senke die Pfade anschauen und irgendwo
entlang auf diesem Pfad haben wir wieder eine so ausgewiesene Kante,
die wir jetzt im folgenden betrachten.
Und die Argumentation ist sehr ähnlich zu Edmonds und Carb.
Wir können nämlich zeigen, dass sich diese Entfernung,
jetzt muss ich gucken, dass ich das richtig drum mache,
von U nach V geht diese Kante,
der Knoten von der Quelle vergrößern
Wir wissen, der Fluss ist blockierend, das bedeutet,
wenn wir Pfade kürzester Länge haben, dann werden die auf alle Fälle saturiert.
Das heißt, wenn wir hier einen Pfad haben,
der die gleiche Länge hätte wie in der vorherigen Iteration,
dann müsste der Pfad neu entstanden sein,
weil wenn der schon existiert hätte,
dann hätten wir den auch saturiert,
dann hätten wir irgendwas drüber gepusht.
Das heißt, der Pfad ist erstmal neu zwischen dem Zustand
oder seit der letzten Augmentierung, ist der neu dazu gekommen.
So, das heißt, entlang dieser Pfante entlang dieses Pfades
gibt es eine Kante, die vorher nicht existiert hat.
Und wenn diese Kante neuen zugekommen ist,
dann kann das nur so entstanden sein,
dass wir einen Fluss in der entsprechenden Gegenrichtung hier über die Kante geschoben haben.
Weil dadurch entsteht natürlich eine Restkapazität in der umgekehrten Richtung.
Wir können den Fluss ja wieder zurücknehmen.
Und so entsteht eine Kante neu in dem resitualen Netzwerk.
So, und das Argument war jetzt das folgende. Diese Kante,
die jetzt von V nach Ufer läuft, die war ja offensichtlich
auch auf einem Pfad irgendwie von S nach Tee geführt hat.
Und dieser Pfad war auch ein Pfad kürzester Länge
Weil wir ja immer nur über die kürzesten Pfade argumentieren.
Und wenn der Pfad in dieser Richtung auf dem kürzesten Pfad
war und nachher in der umgekehrten Richtung auf dem kürzesten Pfad.
Und wir wissen bereits, die anderen Pfade sind monoton steigen,
da muss er jetzt auch streng monoton steigend sein.
Das ist nochmal kurz zusammengefasst, was wir uns schon angeschaut haben.
Der Beweis ist sehr analog zu dem Beweis von Edmonds und Carb
. Da haben wir es uns ganz genau angeschaut,
wie die Pfadlängen tatsächlich wachsen während der Algorithmus, diese Augmentierungsschritte durchführt.
Okay, damit wissen wir, wie viele Blockierungsschritte wir benötigen.
Und jetzt stellt sich die große Frage,
wie berechnen wir denn jetzt so einen blockierenden Fluss
Die Idee von Denise ist, dass wir sagen,
okay, wir gucken uns alle kürzesten Pfade gleichzeitig an.
Jetzt brauchen wir erst mal diese Beschreibung aller kürzesten Pfade.
Ja, damit wir auch wissen, über welche kannten wir jetzt Fluss schieben können.
Und dazu hat er ein Level Graf definiert, also eine neue Graf-Variante.
Das ist eigentlich nur ein Teil Graf von dem resitualen Netzwerk
und zwar die Kanten, die auf so einem kürzesten Pfad liegen.
Ich zeige denen das mal an einem Beispiel,
mal angenommen, wir haben diesen einfachen,
wie es einfache Flussnetzwerk hier Wir haben eine Quelle und eine Senke und ein paar Kanten dazwischen.
Und jetzt sehen Sie schon bezüglich der Anzahl der Kanten,
sind S und T, ja, drei Kanten voneinander entfernt.
Also die kürzeste Pfade gibt es eine ganze Reihe,
also hier lang zum Beispiel oder hier lang oder auch hier lang.
Aber die kürzeste Distanz von S und T ist auf alle Fälle drei.
Und was wir jetzt machen ist, wir nehmen einfach,
wir betrachten in diesem Flussnetzwerk einfach nur alle Kanten,
die auf einen dieser kürzesten Pfade liegen.
Und dadurch entsteht so ein Levelgraf, der ist hier dargestellt.
Und jetzt sehen Sie, der Graf verändert sich ein bisschen,
wir haben weniger Kanten und alle Kanten liegen auf einem Pfad
der Länge drei
Okay, wie, wenn man das etwas formaler aufschreibt,
dann ist der Levelgraph dann wie folgt definiert.
Wir haben also ein Flussnetzwerk, wir haben eine Quelle und eine Senke natürlich auch.
Wir haben schon vielleicht einen Fluss und durch den Fluss definieren wir jetzt ein resituales Netzwerk.
Also das sind die Restkapazitäten sozusagen, wie wir noch haben,
so wie wir das in der letzten Vorlesung auch immer schon gemacht haben.
Und aus diesem resitualen Netzwerk nehmen wir jetzt eine Menge von Kanten
heraus und zwar nehmen wir nur die Kanten heraus mit der Eigenschaft,
dass die, die laufen jetzt von V nach W,
dass die Entfernung der von der Quelle zu dem Knoten W,
genau eins länger ist als die Entfernung,
also die minimale Distanz von dem Knoten V zur Quelle.
Also das sind genau die Kanten, die die Dreiecksangleichung scharf erfüllen,
also wo die kürzeste Pfadlänge genau eins länger wird.
Und das sind entsprechend die Kanten auf dem kürzesten Pfaden.
Okay, so, dieser Graf, der hier entsteht,
das ist das Erste, was man mal fällt,
feststellen kann, ist ein Deck.
Ja, der kann keine Zyklen enthalten,
weil diese Gesetzmäßigkeit hier widerspricht ja der Tatsache,
dass es
einen Zyklus gibt
Also wenn die kürzeste Pfadlänge sich immer monoton Nummer eins vergrößert,
dann kann es gar keine Zyklen geben in den Grafen.
Und so sieht der Level Graf dann in unserem Beispiel aus.
Und wahrscheinlich haben sie auch schonfort ein Gefühl dafür,
wie man so ein Level Graf berechnen könnte.
Das Einzige, was man hier machen muss,
ist quasi eine Breitensuche durchführen von unserem Startknoten aus.
Und wenn sie eine Breitensuche machen,
dann haben sie ja quasi zwei Arten von Kanten.
Also sie haben die Kanten,
die in der Breitensuche zu einem
noch nicht explodierten Knoten führen
Und solche Rückhanden, die du schon zu betrachteten Knoten führen und auf diese Art und Weise können sie,
wenn sie entsprechend diese Warumkanten betrachten, können sie die Levelwerte,
die Distanzen setzen und dann können sie direkt bestimmen, welche Kanten dazugehören und welche nicht.
Also in der Breitensuche ist es ganz einfach möglich. So,
wenn wir dann so einen Levelgraf haben,
dann können wir in dem einen Fluss berechnen und eigentlich,
was wir jetzt eigentlich tun, ist,
wir lösen wieder das maximale Flussproblem, aber auf einer etwas einfacheren Eingabe,
nicht mehr auf einen beliebigen Grafen,
sondern auf so einem Level-Grafen hier,
wo die Knoten eigentlich so nach Ebenen sortiert sind und die kannten immer von einer Ebene zur nächsten verlaufen.
Und das ist dann ein blockierender Fluss. Und hier sehen Sie
ein Beispiel für so einen blockierenden Fluss auf dieser Seite.
Sie sehen also hier der üblichen Notation,
das ist die Flussmenge und das ist die Kapazität.
Und da gibt es einige Kanten, die sind eben,
die haben noch eine Restkapazität wie diese hier,
da haben wir dann nur drei von den fünf Einheiten verwendet und andere,
wie zum Beispiel diese Kante hier, die sind dann saturiert.
Da haben wir dann also eine von einer Flusseinheit und
die Kapazität
Ja, und sie wissen natürlich auch schon genau, wie wir sehen,
dass das tatsächlich ein maximaler Fluss in diesem Levelgraf ist.
Sie müssen nämlich nur nach dem Schnitt suchen, der wäre hier.
Also diese Kanten sind alle saturiert und deswegen gibt es keine Möglichkeit mehr.
Das wäre also der Schnitt. Es gibt keine Möglichkeit mehr,
weiteren Fluss von der Quelle S zu Senke-Tee zu transportieren.
In dem Levelgraf, in einem anderen Graf könnte natürlich durchaus
das schon möglich sein. Gut, okay,
jetzt brauchen wir eine Strategie, um genau das zu berechnen.
Die gucken wir uns gleich an. Wir gucken mal,
wie es jetzt weitergeht
Also mal angenommen, wir haben jetzt diesen blockierenden Fluss hier berechnet,
wie auch immer das geht, müssen wir gleich noch schauen.
Dann würden wir als nächstes den Fluss argumentieren, also auf den
bereits vorhandenen Fluss drauf addieren und ein neues resituales Netzwerk konstruieren.
Wo achten Sie hier, war die Entfernung von Quelle zur Senke 3
Einheiten, also 3 Kanten lang. Okay, so,
das wäre jetzt das resituale Netzwerk, was wir bekommen würden,
aber Sie sehen so ein paar Kanten drehen sich um,
das ist also die Kanten,
über die wir Fluss gepumpt haben,
da haben wir jetzt dann Kanten in der entsprechenden Gegenrichtung
Hier können wir jetzt nach weiteren, einer Möglichkeiten zur Argumentierung suchen.
Wir würden jetzt im Deenage-Algorithmus als nächstes wieder ein Levelgraf erzeugen.
Das machen wir wieder mit einer Variante der Breitensuche.
Und das wäre der Levelgraf, der jetzt hier herauskommt.
So sähe der aus. Und da sehen wir jetzt
zwei Dinge. Zum einen sehen wir,
dass die Distanz zwischen Quelle und Senke sich tatsächlich verlängert hat.
Denn jetzt brauchen wir inzwischen ein, zwei, drei,
vier Kanten, um von der Quelle zur Senke zu kommen.
Einfach weil Kanten entfallen, die sind gesättigt, die können wir nicht mehr benutzen.
Ja, dadurch verlängert sich die Distanz zwischen Quelle und Senke
und das wird eben auch streng monoton größer,
so wie wir das eben schon in dem Beweis gesehen haben.
Also hier wächst jetzt die Entfernung von S nach Tee von 3 auf 4.
Was Sie auch sehen an diesem Beispiel, ist,
es gibt so manche Kanten, die bringen uns nicht viel,
also zum Beispiel die hier von hier nach da,
die hilft uns jetzt nicht,
weil die führt uns nicht zu dem Zielknotentee.
Also die hat zwar, diese Kante hat zwar die Eigenschaft,
dass ich entlang dieser Kante die Entfernung genau um eins verlängert,
also gehört schon zum Level Graf dazu, so wie wir den definiert haben.
Aber über diese Bekannte können wir keinen weiteren Fluss pushen.
Das heißt, wir haben jetzt so zwei Szenarien,
wir können hier nach Faden suchen und einige dieser Pfade,
die werden in Tee enden.
Und wenn wir so ein Pfad gefunden haben, dann sagen wir,
okay, da können wir jetzt ein bisschen mehr Fluss drüber schieben.
Und andere Pfade werden nicht in Tee enden, so wie
zum Beispiel dieser hier. Und dann können wir auch einfach
diese Kante dann löschen, wenn wir sowas detektieren.
Und so werden wir das gleich auch machen.
Okay, zunächst schauen wir nochmal, wie es weitergeht.
Also das ist jetzt der neue Level Graf. Wir haben den Fluss,
können wir jetzt hier drauf berechnen, Blockierenden, das wäre der.
Also sehen, wir können hier noch eine Flusseinheit über diesen Pfad
hier pumpen und dadurch werden zwei Kanten sogar gesättigt, diese und diese.
Also damit hätten wir einen blockierenden Fluss in diesem Level Graf berechnet.
Wenn wir den jetzt wieder drauf addieren, kriegen wir dieses resituale Netzwerk hier.
Und wenn wir uns das anschauen, dann sehen wir,
dass wir den maximalen Fluss gefunden haben,
denn es gibt hier den Schnitt,
der läuft hier lang und wir sehen, dass dieser Schnitt gesättigt ist.
Das heißt, im resitualen Netzwerk laufen alle Kanten über
diesen Schnitt nur noch von der Senke zurück zur Quelle
Wir können also über diesen Schnitt keinen weiteren Fluss von der Quelle.
So säengepumpen und sind damit quasi fertig. Okay,
also in diesem einfachen Beispiel haben wir zwei Augmentierungsschritte gebraucht,
um dann den maximalen Fluss zu finden.
Okay, was ist jetzt also noch klären müssen,
ist, wie können wir diesen blockierenden Fluss berechnen?
Und vor allen Dingen, wie lange wird das dauern?
Weil das ja im Prinzip auch wieder eine Art Flussproblem ist,
was wir jetzt erstmal lösen müssen als Teilproblem.
Hier ist der Algorithmus von Dienesch, so als Pseudocode,
Strategie, die sich dahinter verbirgt, ist einfach die,
dass wir in diesem Level-Netzwerk quasi, ähnlich wie bei der Tiefensuche,
einfach alle Kanten angucken und immer versuchen,
möglichst viel Fluss in die nächste Ebene zu schieben.
Also eigentlich wird der Graf vollständig exploriert, dieser Level Graf.
Der Grund, warum das Ganze dann gut funktioniert,
ist, dass der Level Graf nicht groß ist.
Wir können sehen und das sieht man auch schon in dem Beispiel,
dass er natürlich tendenziell weniger Kanten enthält und deswegen effizient betrachtet werden kann.
Und er hat natürlich
keine Zyklen
Okay, so, wie machen wir das?
Wir berechnen also erstmal den Levelgraf mit einer tiefen Suche.
Diesen Algorithmus habe ich Ihnen jetzt hier nicht als Pseudo-Code aufgeschrieben.
Ich denke, das kriegen Sie hin.
Also Tiefensucher und dann die Idee-Werte berechnen und entsprechend gucken,
wo sich die D-Werte um genau eins unterscheiden.
Und das sind dann die Kanten, die dazugehören.
Und jetzt müssen wir den blockierenden Fluss berechnen. Wir initialisieren,
indem wir die Flusswerte für alle Kanten erstmal auf Null setzen.
Ja, und jetzt machen wir was.
Das sieht so ähnlich aus wie wieder der Edmonds Carb-Algorithmus.
Wir suchen nämlich wieder Pfade, allerdings nicht in unserem
ursprünglichen Grafen,
sondern nur in diesem Levelgraf
Und ja, solange wir einen Pfad finden,
ich sage Ihnen gleich was zu den Parametern, nehmen wir diesen Pfad und argumentieren.
Das passiert genau an dieser Stelle. Wir gehen die Kanten
des Pfades durch und erhöhen unseren blockierenden Fluss,
um das Delta, das Delta wird die Flusseinheit sein,
die minimale Kapazität, die wir noch frei haben über diesen Pfad.
Ja, die werden wir jetzt gleich mitberechnen und dann können wir das drauf addieren.
Und wenn wir hier entsprechend die Kapazität ausgeschöpft haben an einer Kante,
also wenn die Restkapazität gleich den blockierenden Fluss ist,
dann löschen wir diese Kante aus dem Netzwerk
Gut. So, auf diese Art und Weise können wir das,
können wir den blockierenden Fluss auch wieder durch Augmentierung über Pfade erzeugen.
Jetzt müssen wir noch schauen,
wie finden wir jetzt diese Pfade in dem Grafen.
Wir müssen uns jetzt keine Sorgen mehr darüber machen,
ob wir wirklich auf dem kürzesten Pfad sind.
Das passiert automatisch, weil im Level Graf gibt es nur noch kürzeste Pfade.
Alle Pfade zwischen Quelle und Senke haben die gleiche Länge.
Dafür sorgt der Graf schon. Was wir also machen ist,
wir laufen einfach los und gehen von Knoten zu Knoten
Und wir wissen, wir nähern uns automatisch der Senke.
So ist der Graf gebaut. Oder der zweite Fall ist,
wir laufen in so eine Sackgasse rein. Also zu einem Knoten,
von dem wir die Senke gar nicht erreichen können.
Und dementsprechend müssen wir einfach nur diese zwei Fälle unterscheiden.
Wir machen das Ganze mit so einer Endrekussion.
Das heißt, wir gucken, die Funktion ist rekusiv.
U ist der Knoten, auf den wir gerade sitzen.
Ja, wir bewegen uns in dem Graben.
Wir sitzen auf einem Knoten. Wir laufen eine Kante weiter
und nähern uns damit der Senke. Wenn wir die Senke
erreicht haben, wenn er so u-gleich Tee ist,
dann setzen wir
den Fluss Delta
Auf unendlich. Also wir sagen, also wenn wir von hier starten,
dann könnten wir unendlich viel Fluss zur Senke liefern,
weil da ist ja gar keine Kante dazwischen.
Und der Pfad, den initialisieren wir mit diesem Endknoten-Tee.
Also das ist die mit der Senke.
Und sagen dann, geben dann True zurück und sagen,
Horweka, wir haben eine Pfad gefunden.
Die Parameter hier sind Call by Reference, das heißt,
die werden im Laufe des Algorithmus halt gesetzt und entsprechend dann verwendet.
Okay, so, wenn wir nicht an der Senke
angekommen sind, dann sind wir an irgendeinem x-beliebigen
anderen Knoten
Und was wir machen ist, wir schauen uns von diesem
Knoten-U aus, alle möglichen, alle möglichen Kanten an.
Ja, wir sondieren hier alle Kanten und rufen einfach rekursiv unsere Feind-Pass-Funktion auf. Ja,
so, und dann gehen wir dann als nächstes zu dem Knoten-V und der Knoten-V,
der wird auf alle Fälle eine Kante näher am Tee dran sein,
also an der Senke, oder er führt in eine Sackgasse,
das kann auch sein.
Ja, das sind die beiden Möglichkeiten. So,
wenn Feind-Path erfolgreich war,
dann kommt jetzt der Algorithmus aus der Rekursion zurück und sagt True,
das heißt, ich habe einen Pfad gefunden,
dann wissen wir, okay, es gibt jetzt einen Pfad,
der führt von V zu unserer Senke.
Ja, und jetzt können wir
die Kante von U nach V natürlich noch davor hängen.
Dann haben wir den Vater geführt von V zur Senke.
Und was die Kapazität angeht, die sie noch haben,
dann ist es entweder die Kapazität für den Rest des Fades,
also was wir schon berechnet haben.
Oder die Restkapazität dieser einen Kante, die wir jetzt an dem Pfad vorne dranhängen.
Dann verlängern wir entsprechend dem Pfad und geben True zurück und sagen,
hey, wir haben Pfad gefunden.
Das heißt, dieser Algorithmus hier, diese Vorschleife,
die wird potenziell gar nicht zu Ende ausgeführt.
Hier ist schon ein Return drin. Das heißt,
sobald wir quasi einen Pfad gefunden haben,
hören wir hier auf und gehen wieder an den Vorgänger
Zurück. So, wenn Find Path nicht erfolgreich war,
dann bedeutet das, dass diese Kante UV,
also wir haben dann in keinem Fall dazu führt,
dass wir den Zielknoten erreichen können.
Und wenn wir den nicht erreichen können,
dann brauchen wir die Kante in Zukunft auch nicht weiter betrachten.
Und in dem Fall, hier würden wir die Kante einfach aus dem Grafen herausnehmen.
Also zusammengefasst, wir starten also hier an dem Knoten S.
Wir haben jetzt in den Levelgrafen,
sieht das in etwa so aus und wir bewegen uns schrittweise
nach unten und irgendwann hier unten ist mal
der Knoten
Und wir laufen in diesem Netzwerk einfach runter. Die Kanten
sind natürlich so orientiert, dass wir nur runterlaufen können.
Und es gibt jetzt zwei mögliche Szenarien. Erstes Szenario,
wir kommen zu einem Knoten, der hat keinen Weg zur Tee.
Die Kante streichen wir, wenn wir so eine Situation haben.
Zweites Szenario. Wir haben einen Pfad gefunden, der uns
zur Tee führt und rekusiv können wir dann wieder aufsteigen.
Bis wir wieder bei S angekommen sind und entsprechend während dieses rekusiven Aufstiegs das Delta berechnen,
also die minimale die minimale Restkapazität,
um die wir dieses Kapazität noch erhöhen können und in der Variablen P können wir dann in einer Art Liste den Fahrt speichern, um hier entsprechend hochzukommen.
Okay, wenn keine Kante uns zum Ziel geführt hat,
naja, dann wissen wir, okay, dieser Knoten,
hier, der liegt irgendwie in der Position,
wo wir gar nicht zu einem Zielknoten kommen können und dann können wir einfach Falls zurückgeben und sagen dann,
also wir haben keinen Pfad gefunden,
der von U noch zur Quelle zurückführt
Ja, das ist die Strategie von dem Algorithmus.
Und da stellt sich jetzt natürlich die Frage, wie lange dauert das?
Es sieht ja erstmal sehr aufwendig aus, weil wir hier
immer in diesem Netzwerk so rauf- und runter laufen, nach neuen Faden suchen.
Und über die dann hier wieder so schrittweise augmentieren.
Um die Laufzeit abzuschätzen, können wir feststellen,
dass wir schrittweise kannten, eigentlich nicht mehr verwenden können.
Also immer dann, wenn wir die Situation haben,
dass wir keinen Pfad gefunden haben, Kante da, die wir löschen können.
Ja, die können wir da nicht weiter betrachten. Wenn wir so einen Pfad gefunden haben,
dann gibt es ja auf diesem Pfad eine der Kanten,
mal angenommen, das wäre jetzt dieser Pfad hier,
der hier lang und hier lang und hier lang so,
das wäre der wäre es gewesen, ja,
dann gibt es ja auch entlang dieses Pfades mindestens eine Kante,
die saturiert wird.
Ja, so, mal angenommen, das wäre diese,
die wird saturiert. Das heißt, die Kante entfällt,
weil wir haben ja keine Restkapazität mehr auf der Kante
und die würden wir löschen.
Die löschen wir hier.
Eine Kante weniger
Streng genommen, kommt natürlich jetzt im resitualen Netzwerk so eine Kante in umgekehrter Richtung dazu.
Also die hier käme jetzt eigentlich da rein,
aber die gehört nicht zum Levelgraf,
weil die führt uns ja zu einem Knoten weiter nach oben
und die liegt nicht auf dem kürzesten Pfad.
Deswegen führen wir keine neuen Knoten ein.
Keine neuen Kanten ein. Okay, gut. So,
also das heißt, jedes Mal, wenn Feindpath erfolgreich war,
können wir eine Kante löschen.
Eine Kante wird gelöscht. Jedes Mal,
wenn Feindpath irgendwie nicht erfolgreich war,
weil wir keinen Pfad gefunden haben,
können wir auch
eine Kante löschen
Ja, das heißt, wir werden immer sukzessive Kanten löschen
und auf die Art und Weise terminiert der Algorithmus natürlich.
Und wir haben auch eine Abschätzung davon, darüber,
wie lange das dauert, weil wir können ja,
wir wissen ja, es gibt ja nur eh viele Kanten.
Und wenn wir die alle gelöscht haben, dann ist offensichtlich
der Algorithmus so weit, dass er terminieren kann.
Also, die Laufzeit steht in diesem Lämmer,
das heißt Calculate Blocking Flow,
berechnet in dem resitualen Netzwerk einen blockierenden Fluss in Worst Case
Laufzeit O von V
Warum kommt der V mal E raus? Also erstmal müssen
wir die Tiefensuche starten, um diesen Levelgrafen zu bestimmen.
Das O von V plus E. Dann müssen wir den blockierenden Fluss initialisieren
, das ist O von E. Wenn wir dann argumentieren,
entlang des Pfades, dann kostet das jeweils O von V,
weil der Pfad maximal V lang ist.
Und bei jeder Augmentierung wird mindestens eine Kante saturiert, hier, die entfällt.
Damit gibt es maximal O von E viele Augmentierungsschritte. So,
das heißt, die Gesamtlaufzeit von Zeile 3 bis 6,
Also darüber hier, ja, ohne das feine Part jetzt erstmal,
ist auf alle Fälle beschränkt durch V mal E.
So, jetzt ist die Frage, wie lange dauert dieses Find Path?
Wir sehen schon, Find Path hat eine Laufzeit,
die ist proportional zur Anzahl der Aufrufe.
Ja, also wir rufen die Funktion auf.
Und in dieser Vorschleife rufen wir die Funktion auch jeweils auf.
Wir müssen also nur gucken, wie häufig wird diese Funktion
Find Path aufgerufen, während der Algorithmus durchlaufen wird.
Wenn wir den Algorithmus V mal aufgerufen haben in so einer Sequenz,
wir rufen ihn auf,
wir rufen ihn wieder auf und wieder auf und wieder auf,
dann wissen wir, dass eines von zwei Szenarien eintreten muss.
Entweder es kommt ein Argumentierungsschritt oder wir können eine Kanze löschen.
Warum? Weil der kürzeste Pfad von S nach Tee ja
kleiner als kleiner Gleichbetrag von V minus 1 ist.
Deswegen wissen wir, dass es nur so viele Schritte dauern kann,
bis wir zu so einem Endzustand kommen.
Dann sind wir bis hier unten gelaufen. Dann haben wir
entweder Tee gefunden oder wir sind in so eine Sackgasse gelaufen.
Und in beiden Fällen können wir eine Kante löschen.
Wenn wir eine Sackgasse gelaufen sind, löschen wir die letzte Kante.
Wenn wir einen Pfad gefunden haben, löschen wir auch eine Kante,
nämlich eine, die wir saturieren.
Das heißt, immer nach Betrag von V vielen Schritten
können wir eine Kante löschen
Ja, und dann ist auch klar, wie lange das dauert,
weil wenn wir immer nach Betrag von VfL aufrufen, eine Kante löschen,
dann können wir nur Betrag von V-Mahlbetrag, von Mal-E viele Feindpass-Aufrufe haben.
Das heißt, die Gesamtlaufzeit von Feindpass ist eben auch V-Mahl-E
und damit ist die Gesamtlaufzeit von Cakeylate, Blocking Flow,
eben auch durch V-Mahl-E beschränkt. Ja,
und daraus resultiert sofort die Laufzeit des Denitsch-Algorithmus,
denn wir wissen schon,
dass wir Betrag von V viele Augmentierungsschritte brauchen und in jedem
der Augmentierungsschritte haben wir erstmal O-F und E-Zeit,
um die Augmentierung durchzuführen in der Wahlschleife und zur Berechnung des Blockierenden Flusses
brauchen wir auch PharmaE. Macht eine Gesamtlaufzeit von V mal V mal E plus E,
also V-Quadrat mal E.
Die Korrektheit folgt direkt aus Max Flow mit Cut.
Das schreibe ich Ihnen jetzt nicht noch mal explizit auf,
aber das folgt daraus. Okay, das ist deutlich besser,
als die Laufzeit, die wir für den Edmonds Carb-Algorithmus gesehen haben.
Das war VE-Quadrat. Und wir wissen ja, je nachdem,
wie hoch die Kantendichte ist, ist auf alle Fälle,
ist die Anzahl der Kanten größer als die Anzahl der Knoten,
sodass wir hier also
einen Gewinn haben
Wenn man sich den Algorithmus auch mal anschaut,
also ich meine, die Betrachtung, die Laufzeitbetrachtung im Okalkül,
ist ja immer so eine Sache, wenn es um Laufzeit geht.
Aber wir sind natürlich auch daran interessiert,
wie schnell ist denn das jetzt wirklich in der Praxis?
Und da spielen eine ganze Menge Konstanten eine Rolle. Deswegen gucke ich natürlich,
wenn ich Algorithmen anschaue, immer nicht nur darauf,
was kommt in deinem O-Kalkül raus, sondern naja,
man kann da mal drauf gucken und sich auch vorstellen,
wie groß werden denn wohl diese Konstanten in so einer Implementierung?
Und wenn man sich diesen Denage-Algorithmus mal anschaut,
dann sieht man, ich brauche keine aufwendigen Datenstrukturen,
keine schwierigen Prioritätswarteschlangen
Eine Breitensuche, die ist relativ einfach vom Aufbau. Und ich habe dieses Verfahren,
das wäre in der Implementierung nicht viel länger als das,
was ihr seht, was hier steht.
Ich brauche ein paar Arrays, ja,
um den blockierenden Fluss zu speichern und könnte das dann umsetzen.
Und auch die Hauptfunktion, die wir gesehen haben,
hier ist auch relativ einfach von der Struktur. Das heißt,
dieser Algorithmus ist nicht nur vom O-Kalkül her effizient,
der hat auch vermutlich sehr niedrige Konstanten und ist in der Praxis sehr effizient.
Das ist einer der Gründe
Warum meiner Meinung nach dieser Algorithmus für Flüsse so wichtig sind,
ist und ich ihn denen auf alle Fälle zeigen wollte.
So, es gibt einen zweiten Grund,
warum dieser Algorithmus so wichtig ist und der hängt mit einem etwas merkwürdigen Netzwerk zusammen,
von dem man jetzt momentan noch nicht so ganz sieht,
was für eine praktische Relevanz das hat.
Es hat nachher eine, die werden wir in ein, zwei Wochen sehen.
Und zwar, wenn es nachher um Matching geht,
also um das Matching-Problem, also Zuordnung von Knoten.
Das Netzwerk, um das es jetzt geht,
ist ein sogenanntes Einheitsnetzwerk und dieses Einheitsnetzwerk ist wie folgt definiert.
Es ist ein Flussnetzwerk,
wie vorher auch
Und es gibt eine Bedingung an den Kapazitäten oder zwei genauer genommen.
Die eine Bedingung ist, alle Kapazitäten sind ganz zahlig.
Und die zweite Bedingung ist, dass wenn wir einen beliebigen Knoten nehmen,
dann ist entweder der Eingangsgrad oder der Ausgangsgrad dieses Knotens eins.
Also alle Knoten außer Quelle und Senker haben die Eigenschaft,
dass genau eine Kante hineingeht oder genau eine Kante herausgeht.
Und dieser eine kannte die rein oder rausgeht,
hat Kapazität eins
Das heißt, ein Einheitsnetzwerk sieht zum Beispiel so aus,
hier in meinem Bild haben sie dünne Kanten und dicke Kanten und sie sollen nur symbolisieren,
die dünne Kante ist eine mit Kapazität 1 und die dicke
ist irgendwie irgendeine beliebige Kapazität größer als eins.
So, und das ist ein Beispiel für so ein Einheitsnetzwerk,
das heißt, sie haben hier Kanten, die,
also sie können jeden beliebigen Knoten betrachten und jeder Knoten hat die Eigenschaft,
es geht genau eine Kante rein oder es geht genau eine Kante raus und diese eine Kante,
die ausgezeichnet ist,
weil sie entweder aus dem Knoten rausgeht oder rein,
die hat Kapazität eins
Und dann können sie so eine Art Graf aufbauen. Der müsste jetzt nicht,
also hier ist es sozusagen auch noch so, das ist ja,
ja, wenn man guckt, auch ein Level-Graf sogar.
So müsste der nicht unbedingt aussehen. Der könnte auch natürlich noch
andere Kanten haben. Sie könnten noch da mehr Kanten reinziehen.
Aber im Großen und Ganzen ist das so die Struktur,
die da rauskommt. Ja, aus diesem Grafen. Okay,
so, wir haben jetzt also irgendwie so ein Spezialgraf.
Der hat irgendwie eine zusätzliche Eigenschaft, die haben wir jetzt gesehen.
Und auf dem wollen wir jetzt einen Fluss berechnen. Ja,
warum dieser Graf, wie gesagt, relevant ist, zeigt,
ich ihn später mal, da werden wir noch drüber stolpern.
Okay, wir benutzen den Denage-Algorithmus, um auf diese Art
von Grafen, den Einheitsnetzwerken Einfluss zu berechnen.
Wir werden also jetzt keinen neuen Algorithmus sehen, sondern einfach nur überlegen,
was hat denn diese Grafstruktur für eine Konsequenz für die Laufzeit des Algorithmus?
Gut, das sagt dieses Theorien. Wie lange braucht
der Denage-Algorithmus auf Einheitsnetzwerke? Und das Theorien sagt,
dass der Algorithmus auf einem Einheitsnetzwerk nach maximal zweimal nach oben aufgerundet,
Wurzel von Betrag von V minus zwei Augmentierungsschritten
Also Wurzel-V, viele Schritte braucht er, Augmentierungsschritte.
Und das ist der erste Teil und darüber können wir dann auch sofort die Gesamtlaufzeit sehen,
die ist nämlich O von ehemal Wurzel-V. Es könnte sein,
das ist das erste Mal in ihrem Leben, ist,
dass sie einen Algorithmus sehen mit einem Wurzelzeichen in der Laufzeit.
Können Sie mal sehen, woher das kommt jetzt? Also jedenfalls,
was wir sehen, ist, also das ist ein sehr effizientes Verfahren,
weil wir wissen, um Fluss zu berechnen,
müssen wir den Grafen ja mindestens mal anschauen.
Das heißt, O von E werden wir auf alle Fälle haben.
Und das wäre eine untere Schranke für die Laufzeit eines solchen Algorithmus.
Und wir haben hier O von ehemal Wurzel V.
Also es ist sehr effektiv und effizient. So,
wie kann man das jetzt sehen und woher kommt eigentlich dieses Wurzel,
dieses Wurzelzeichen? Also Wurzel kommen immer dann rein,
wenn man so ein Verhältnis hat von Breite zu Länge.
Das werden wir hier haben. Oh, das ist nass
. Und wir werden gleich sehen, woher das kommt
Okay, so ich, ich fange mal noch nicht an zu malen.
Also wir haben wieder Quelle, Senke, das ist das Übliche. Ja,
was wir sehen werden ist, dass wir in diesem Netzwerk,
also der Fluss fließt jetzt ja irgendwie hier so in so einem Netzwerk hier so lang,
ja, dass wir in diesem Netzwerk die Länge der Pfade,
die wir haben, ja,
die irgendwie hier lang geht und die Breite,
wie viele Pfade es gibt, zueinander in Beziehung setzen können.
Und darüber bekommen wir diese Laufzeitschranke hier, weil wir zeigen können,
dass die Knotenmenge, die wir haben,
die müssen wir irgendwie verteilen auf diese Pfade.
Ja, und weil wir das tun müssen,
können wir nur wählen zwischen einem ganz langen Pfad.
Dann haben wir aber alle Knoten verbraucht. Oder ganz viele
kurze Pfade. Dann können wir aber ganz schnell die Augmentierungen berechnen.
Und der ungünstigste Fall ist, naja, wenn sie ungefähr Wurzel-V,
viele Pfade haben, die alle so Wurzel-V lang ist.
Das ist einfach nur der ungünstigste Fall, der auftreten ist.
Ja, auf die Art und Weise kommt dieses Wurzelzeichen in
die Laufzeit rein. Okay, erster Teil des Beweises,
wie lange dauert Blocking Flow? Also wie lange dauert es
diesen blockierenden Fluss zu berechnen
Und da können wir einfach sagen, der blockierende Fluss kann
auf Einheitsnetzwerken in O von E-Zeit berechnet werden.
Also wir brauchen pro Blocking-Flow berechnet nur O von E.
Nicht mehr V mal E. Und warum ist das so?
Das ist ein ganz einfaches Argument, was wir sehen können.
Wenn wir mal so einen Pfad angucken, mal angenommen,
das ist ja hier eigentlich ein Level, Graf.
Wir laufen jetzt hier lang und finden jetzt irgendwie einen Pfad hier so lang.
Dann wissen wir, dass entlang dieses Pfades wird jede zweite Kante saturiert.
Weil jede zweite Kante hat ja nur Kapazität 1.
Die Kapazitäten sind alle ganzzahlig. Das heißt,
wenn wir so einen Pfad finden,
dann können wir den Fluss um eins erhöhen
Und alle diese dünnen Kanten werden saturiert. Das heißt,
der komplette Pfad fliegt raus. Eben hatten wir noch die
Situation, nur eine Kante fliegt draus. Nur eine wird
saturiert. Jetzt wissen wir aufgrund der Struktur des Netzwerkes,
dass der komplette Pfad rausfliegt. Denn auch diese dicken Kanten,
hier, die können wir auch rausnehmen,
weil wir haben ja nur eine Möglichkeit, in diesen Knoten hereinzukommen.
Und die ist saturiert, beziehungsweise nur eine Möglichkeit, darum rauszugehen.
Okay, also, jede Kante wird somit im Blocking-Throw-Algorithmus mit
Fine Pass nur einmal betrachtet.
Und dadurch kommen wir zu einer Laufzeit von O von
So, der schwierigere Teil des Beweises zu sehen, wie viele Augmentierungsschritte brauchen wir eigentlich?
Wie häufig müssen wir jetzt diese Wild-Schleife des Dinisch-Algorithmus durchlaufen?
Okay, dazu habe ich Ihnen mal ein Bild aufgemalt.
Also hier betrachten wir mal die einzelnen Augmentierungsschritte auf der X-Achse.
Ja, also jede Markierung hier, jeder Tick,
der steht für einen Augmentierungsschritt. Und hier sehen wir den Fluss,
also dem schon berechneten Flusswert, wenn Sie so wollen.
Und dann wächst das ja so. Also wir wissen,
wir argumentieren ja, wir erhöhen mit jedem Durchlauf dieser Wildschleife
den Fluss und das geht dann so in Stufen wird das dann immer erhöht.
Wir fangen bei null an und dann argumentieren wir das erste Mal,
sind wir hier und dann wieder und dann wieder und dann
gibt es immer kleinere Stufen, vielleicht mal größere Stufen.
Und irgendwann kommen wir hier oben an und das ist der
maximale Fluss, den nennen wir mal F-Stern.
Und hier unten sehen Sie die Augmentierungsschritte. Also jeder Tick
steht für einen Augmentierungsschritt. Und hier sind wir angekommen und
da haben wir den maximalen Fluss tatsächlich erreicht.
Ich habe hier jetzt hier schon zweimal Betrag von V minus zwei hingeschrieben,
aber das wissen wir natürlich zu diesem Zeitpunkt noch nicht.
So, Was wir ja jetzt wissen wollen, ist,
wie viele Schritte brauchen wir hier, um diesen maximalen Fluss zu erreichen?
Den maximalen Fluss kennen wir natürlich eigentlich nicht.
Und das können wir jetzt wie folgt rausfinden. Was wir
nämlich wissen ist, wir gucken uns einfach mal einen x-beliebigen Kartenschritt an.
Wir haben es jetzt kamal gemacht und dann sind wir
irgendwie hier angekommen und haben einen gewissen Fluss erreicht.
Wir sind hier irgendwie angeguckt, wie so ein Fluss haben
wir schon erreicht durch diese K-Augmentierungsschritte.
Und jetzt würden ab da kommt dann der Kaplus
Erste und dann wächst das hier weiter an
Wenn wir jetzt einen Fluss F haben, dann haben wir
ja noch den Rest bis zum maximalen Fluss, F-Ster minus F.
Und wir wissen, dass F-Ster minus F,
das ist ein Fluss im resitualen Netzwerk. Wir könnten den
im nächsten Schritt, wenn wir den richtigen Fluss hätten,
da argumentieren und hätten dann den maximalen Fluss gefunden.
Wir wissen auch, der Fluss ist ganz zahlig,
Ben, wir haben so ein Einheitsnetzwerk und wir argumentieren,
ja, das heißt, da wird niemals irgendwie 0,5 addiert,
sondern das bleibt ganz zahlig.
So, das bedeutet, wir haben jetzt also Pfade und
entlang dieser Pfade können wir den Fluss erhöhen,
aber immer nur um eine Einheit
Also wenn wir einen Pfad finden,
dann können wir über diesen Pfad nur den Fluss um eine Einheit erhöhen,
weil hier sind ja Kanten dazwischen, die alle immer Kapazität eins haben.
Jede Kante kann aber nur auf einem Pfad liegen.
Auch jeder Knoten kann dann somit nur auf einem Pfad liegen,
weil es gibt ja nur eine ausgezeichnete, eintretende oder austretende Kante.
Das heißt, jetzt haben wir so eine Situation,
dass wir sagen, okay, da gibt es jetzt noch einen Fluss,
den wir noch finden müssen. Das ist,
der hat den Wert von Betrag von F-F und wir wissen,
den können wir aufteilen auf Pfade und alle diese Pfade
werden die Kapazität eins haben.
Deswegen muss es auch genau Betrag von F-Stern,
Minusbetrag von F
viele Pfade geben
Weil über jeden dieser Pfade werden wir den Fluss immer nur um eins erhöhen können.
Okay, und wenn wir das gemacht haben,
ist der Pfad auch weg. Dann ist der argumentiert und
die ganzen Kanten hier können nicht mehr entsprechend verwendet werden.
Okay, so, das heißt, aufgrund dieser Einheitsnetzwerkeigenschaft,
wo immer eine kann eine ausgewiesene Kante, diese Kapazität eins hat,
wissen wir, jeder Knoten liegt auf genau maximal Einfahrt.
Der kann auch auf keinem Pfad liegen,
aber er liegt auf höchstens ein Pfad
Wie lang können die jetzt sein? Also wir haben noch eine,
wir wissen, wir müssen Betrag von f-f viele Pfade realisieren.
Also wir haben via S und hier ist T und
ich kann das Bild hier oben nochmal verwenden.
Und wir haben jetzt hier so Pfade, diese Pfade,
die haben alle keine gemeinsamen Knoten.
Ja, weil das geht nicht im Anhaltsnetzwerk.
Und die Frage ist, wie viele Pfade sind das hier?
Das wissen wir, das sind ja Betrag von f-stern-betrag von effende Pfade.
Okay, so viele Pfade brauchen wir und wie viel Knoten
habe wir? Wir haben Betrag von V viele Knoten.
Und die müssen wir jetzt irgendwie auf diese Pfade verteilen
Und die Frage ist jetzt, was passiert? Naja,
also wenn wir jetzt mal überlegen,
dann muss es mindestens einen Pfad geben,
der Betrag von V minus 2 durch, ja,
diese Flussdifferenz länger hat.
Höchstens, ja. Weil, ähm, naja,
wenn sie mal die mittlere Pfadlänge berechnen, dann kämen wir ja sowas daraus.
Sie würden dann alle Knoten aufteilen auf diese ganzen Pfade.
Dann wüssten sie, also im Mittel werden das dann Betrag
von V minus 2, die beiden Knoten zählen nicht.
Durch diesen Fluss, viele Pfade. Und das wäre
der Mittelwert. Und wenn das der Mittelwert ist,
dann muss es ja einen Pfad geben,
der kürzer ist
Das heißt, es gibt mindestens Einfahrt, der höchstens so lang ist.
Sagt uns einfach die Statistiken. Da haben gar nicht mehr Knoten
zur Verfügung, die wir verteilen können auf diese ganzen Pfade.
Okay, jetzt gucken wir uns genau einen speziellen Zeitpunkt an.
Wir gucken uns nämlich den Zeitpunkt an,
nach Wurzel von V minus zwei vielen Augmentierungsschritten.
Also so viele haben wir jetzt gemacht. Das ist der
Zeitpunkt, den wir angucken. Jetzt haben wir genau diesen
Zeitpunkt und fragen uns, was gilt zu diesem Zeitpunkt?
Zu diesem Zeitpunkt wissen wir, Nach Dynac werden die Pfade ja immer länger.
Der Pfad von S nach Tee wird ja immer länger.
Mit jedem Augmentierungsschritt steigt die Pfadlänge von S nach Tee monoton an.
Das bedeutet, wenn wir ka viele Augmentierungsschritte haben,
dann ist der Fluss, denn wir haben ja schon mal mindestens K.
Und die kürzesten Pfade haben dann mindestens eine Länge von Wurzel,
Betrag von Form minus zwei,
das ist die Anzahl der Augmentierungsschritte plus eins,
weil die steigen ja immer monoton,
streng monoton
So, wenn wir jetzt aber wissen,
dass der kürzeste Pfad mindestens so lang ist,
Wurzelfau minus 2 plus 1, ist aber mindestens Einfahrt gibt,
der höchstens Betrag von V minus 2 durch den Restfluss ist,
dann können wir die beiden natürlich in Beziehung setzen.
Und wir wissen, dass Wurzelbetrag von V minus 2 plus 1
kleiner gleich sein muss, dieser Schranke,
also V minus 2, geteilt durch die Flussdifferenz plus 1.
So, und das können sie einfach umformen und dann sehen sie,
dass die Flussdifferenz, die wir hier haben,
Also maximaler Minus, den schon erreichten Fluss.
Kleinergleich, Wurzel V minus 2 ist. Mit jedem weiteren Argumentierungsschritt
werden wir das F um mindestens eine Einheit näher an das F-Stern bekommen.
Wir laufen jetzt hier also weiter hoch. Und dann wissen wir,
wir brauchen maximal Wurzel von V minus 2 weitere Augmentierungsschritte.
Also wir brauchen Wurzel von V minus 2 viele,
um dieses Plateau zu erreichen. Und dann wissen wir,
wir sind nur noch Wurzel von V minus 2 von dem Optimum entfernt.
Und deswegen brauchen wir nur noch weitere Wurzel von V minus 2.
Viele Augmentierungsschritte. Das heißt, insgesamt zweimal Wurzel von V
minus 2 Augmentierungsschritte
Das ist ein ziemlich trickiger Beweis, ehrlich gesagt.
Also es ist ziemlich geschickt gemacht. Und hier sehen Sie
so ein bisschen, ich habe schon versucht,
das extra so zu malen, woher nochmal diese Wurzel kommt.
Also du kannst auch besser vielleicht nochmal so malen,
indem man hier mal so ein Quadrat hinmalt und sagt,
hier ist der Knoten S und hier ist der Knoten Tee.
So, hier liegen alle Pfade und alle Pfade benutzen
jeden Knoten, der hier ist, nur einmal.
Und dann überlegen Sie sich, wenn die Anzahl der Pfade
ganz groß wird, dann haben wir sehr kurze Pfade
Dann haben wir so ein längliches Rechteck. Wenn die Länge der Pfade
groß wird, dann haben wir nur ganz wenige Pfade.
Und die maximale Aufteilung, sie haben ein Trade-off zwischen diesen
beiden Sachen, wenn sie die Laufzeit maximieren wollen,
wenn sie den Worst Case betrachten.
Und der ungünstigste Fall tritt eben auf,
wenn sie Wurzel von V auf beiden Seiten haben,
also bei der Pfadlänge und bei der Anzahl der Knochen.
Ja, okay, gut. Damit haben wir gesehen,
dass wir den Denage-Algorithmus für diese Einheitsnetzwerke nutzen können und
eine sehr gute Laufzeitschranke erreichen können dabei
Wie gesagt, das ist noch momentan etwas anwendungsfairn,
weil wir noch nicht wissen, wofür wir diese Einheitsnetzwerke brauchen,
aber das wird sich in Kürze schon aufklären.
So, bevor wir jetzt das Flusskapitel abschließen,
müssen wir uns noch eine weitere Klasse von Algorithmen anschauen,
denn wir haben noch nicht das Optimum erreicht,
und zwar Edwards Carb war O von V mal E-Quadrat,
ja, und Denic war O von V-Quadrat mal B.
Und die Frage ist natürlich, ob das nicht besser geht.
Warum muss das so lange dauern?
Es hat eine lange Zeit gebraucht in der Algorithmenentwicklung,
bis man ein Verfahren gefunden hat, was besser ist als das.
Und der Grund, warum das so lange gedauert hat,
ist, weil man algorithmisch umdenken musste.
Man musste weg von dieser Idee des Schrittweisen Erhöhen des Flusses
durch so Augmentierungsschritte. Weil das muss man,
davon muss man sich trennen,
um diese Laufzeit hier nochmal zu verwenden
Ja, und jetzt werden wir Push-Relabel-Algorithmen sehen und am Ende kriegen wir
eine Laufzeit raus von O von V hoch 3. Ja,
es ist ein, wenn man mal ganz ehrlich ist,
ein relativ, ja, es ist wieder so ein marginaler Schritt,
wenn wir davon ausgehen, dass wir einen recht dünnen Graf haben,
dann wirkt sich das nicht aus.
Ja, dann ist V ja so groß wie E.
Aber wenn wir einen sehr dichten Graf haben,
ist das natürlich wieder substanziell besser als der DNH-Algorithmus.
Ja. Okay, gut. Wie müssen wir uns noch anschauen?
Und dabei müssen wir, wie gesagt, umdenken, ja.
So, Wovon muss man sich trennen? Also die Art und Weise,
wie wir die Flüsse bisher berechnet haben, ist,
dass wir gesagt haben, wir wissen, welche Kriterien ein Fluss erfüllen muss.
Ja, sie müssen die Kapazität erhalten, Flusserhaltung muss gelten.
Und wir haben immer darauf geachtet, dass das immer gilt,
damit wir das auf keinen Fall verletzen, dass jeder Fluss,
den wir berechnen, immer gültig ist, bezüglich dieser Kriterien.
Und dann haben wir den so schrittweise vergrößert.
So, jetzt machen wir was anderes. Wir werden einfach
diese Regeln verletzen. Wir verletzen die Regeln,
genau genommen werden wir sie ganz bewusst an einer Stelle verletzen
und kriegen Flüsse, die nicht gültig sind.
Die werden was falsch machen und werden von da uns annähern an
einem dann korrekten Fluss. Wir gehen jetzt sozusagen rückwärts.
Also anstatt von einem gültigen Fluss immer was dazu zu nehmen,
solange bis wir den maximalen Fluss erreicht haben,
werden wir jetzt einfach sagen, pfeif auf die Flusserhaltung.
Ja, wir werden jetzt einfach,
wir pushen so viel Fluss da rein, wie es geht.
Dann haben wir auf alle Fälle schon mal was,
was größer ist als der maximale Fluss.
Wir haben natürlich die Flusserhaltung verletzt und werden jetzt so viel wieder
runternehmen vom Fluss, bis wir den Maximalfluss berechnet haben.
Wir machen es also andersrum
Okay, und diesen gedanklichen Schritt, den musste man irgendwann mal
machen in der Geschichte, um quasi zu effizienteren Verfahren zu kommen.
Gut, was machen wir? Wir definieren einen sogenannten Vorfluss.
Das ist also jetzt so ein, so ähnlich wie ein Fluss,
aber er enthält nicht alle Eigenschaften, die Entfluss haben muss.
Und zwar ist so ein Vorfluss so definiert,
dass er die Kapazitätsbeschränkung einhält, also für je den Vorfluss gilt auch,
dass über jede Kante nur so viel gepumpt wird,
wie die Kapazität der Kante hergibt, aber die Flusserhaltung geben wir auf,
aber nur in eine Richtung, was wir sagen,
ist, dass der Vorfluss eine einseitige Flusserhaltung haben muss.
Das heißt, der Fluss, der in den Knoten hineinfließt,
muss größer gleich den Fluss sein, der aus dem Knoten herausfließt.
Gut, und die Art und Weise,
wie wir uns das vorstellen können, ist,
wir haben also diverse Kanten, die führen in einen Knoten herein.
Und wir haben wieder Kanten, die führen aus einem Knoten heraus.
Und bisher war es so, dass wir gesagt haben,
alles, was reingeht, geht raus.
Und wenn sie sich das so als so ein Netzwerk von Röhren vorstellen,
würden sie sagen, ja,
das muss ja so sein, das ist alles dicht,
hier geht das rein,
da geht es halt wieder raus
Jetzt ist es halt anders, dieser Knoten hier ist nicht mehr dicht.
Der hat halt irgendwie so einen Überlauf und da,
das können Sie sich vorstellen wie so ein Dings und dann wie so eine Tonne.
Und wenn da einfach zu viel reinläuft, dann fließt es
eben darüber und fließt sonst wohin. Das heißt,
wir verzichten darauf, dass unsere Knoten dicht sind, da kann,
die können halt überlaufen, dann fließt es irgendwie ab.
Aber was nicht sein kann, ist,
dass hier weniger ankommt, als das, was rausgeht.
Das ist nicht erlaubt. So,
wenn wir so eine Situation haben,
dann reden wir von einem Vorfluss
Okay, die Algorithmen werden jetzt einen Trick verwenden,
um gezielt dafür zu sorgen, dass der Fluss sich langsam,
was heißt langsam, also in Schritten, von der Quelle zur Senke bewegt.
Das machen wir durch eine Höhenfunktion. Das heißt, unsere Knoten hier,
die kriegen jetzt eine Höhe, die liegen jetzt sozusagen,
die können weiter oben hängen oder weiter unten hängen.
Und diese Höhe benutzen wir auch recht anschaulich, um sicherzustellen,
dass der Fluss in die richtige Richtung fließt.
Wenn wir also jetzt hier wieder so ein Gefäß haben,
Können wir Fluss in diese Richtung pumpen?
Ja, aber nur, wenn das Höhengefälle zwischen diesen beiden Knoten stimmt.
Also wir werden nur erlauben, dass der Fluss quasi so runterfließt.
Ja, also so wie man sich das bildlich vorstellen kann,
wenn man sich überlegt, das wäre jetzt so ein Netzwerk mit Wassern und Eimern.
Okay, dafür brauchen wir eine Höhenfunktion,
die hat die folgenden Eigenschaften, also wir haben ein Flussnetzwerk,
wir haben eine Quelle und eine Senke,
wir haben so einen Vorfluss F und jetzt muss diese Höhenfunktion folgende Eigenschaften haben.
Erstmal ist es eine Funktion auf den natürlichen Zahlen,
ja, also inklusive Also null. Und dann natürliche Zahlen.
Die Eigenschaften ist die Quelle, muss die Höhe V haben.
Also die Quelle ist auf, hat diesen fest vorgegebenen Höhenwert.
Den dürfen wir auch nicht ändern im Laufe des Algorithmus.
Und die Senke hier, die muss die Höhe null haben.
Und alle anderen Knoten, weiß das nicht, festgelegt,
die können irgendwo da liegen. Aber wenn wir eine Kante haben im resitualen Netzwerk,
also wir haben ja schon so einen Fluss,
wenn wir jetzt eine Kante haben im resitualen Netzwerk,
dann muss gelten das Haar von U,
kleiner gleich H von V plus 1 ist.
Also wenn wir eine Kante haben, die läuft von U nach V.
Das ist unser, unser Topf U, das ist der Topf V. Dann wissen wir,
Ja, na, wenn die eine Höhe haben,
das ist Haar von U, dann wissen wir,
das Haar von U kleiner gleich Haar von V plus 1 ist.
Das ist Haar von V, H von V plus 1 ist das.
Das heißt, wenn wir hier eine Restkapazität haben,
die entlang dieser Kante, dann darf das Höhenverhältnis zwischen den beiden nicht stärker sein,
nicht größer sein als eins.
Und wenn die Eigenschaften gelten, dann sagen wir,
diese Funktion H ist eine gültige Höhenfunktion.
Wenn wir diese Argumentation hier umdrehen, dann wissen wir,
dass wenn zwei Knoten eine Höhendifferenz haben von größer als eins,
also wenn wir oben noch so ein Knoten hätten, ja.
Dann kam eine solche Kante im resitualen Netzwerk nicht existieren.
Ja, die gibt es nicht. Im resitualen Netzwerk sind
alle Kanten so, dass die Höhendifferenz maximal eins ist.
Nicht nur die Höhendifferenz, die Höhendifferenz war falsch,
dass das Gefälle maximal eins ist.
In andere Richtung kann das natürlich sein. Also es könnte
durchaus sein, dass es diese Kante hier hochgibt.
Das kann sein, das ist okay.
Aber das Gefälle ist beschränkt auf maximal eins.
So, das ist die Höhenfunktion. So,
jetzt habe ich noch ein schönes Bild hier gemalt
Um da zu erklären, wie das Ganze funktioniert. Also wir haben hier die Höhenfunktion,
hier sind irgendwie die Knoten, sind nicht groß sortiert.
Also es gibt hier nichts auf der X-Achse. Hier sehen wir
die Höhe. Wir wissen, zwei Höhenwerte sind vorgegeben.
Die Quelle ist auf Höhe V, Anzahl der Knoten.
Und die Senke hier unten, die ist auf Höhe null.
Und hierzwischen gibt es jetzt also die Kanten,
diese roten Karten sind Kanten des Resitualen Netzwerks.
Die Knoten haben irgendwelche Höhenwerte, die liegen jetzt hier alle irgendwie so. Ja,
wir wissen aber, die eine wichtige,
das ist eine wichtige Kriterium ist, Dass diese Kanten,
die wir sehen, im residualen Netzwerk haben, Maximalgefälle 1,
die können steiler nach oben gehen, das ist gar kein Problem,
aber das Gefälle ist beschränkt durch eins.
Und unsere Knoten hier, die sind nicht dicht,
das heißt, die haben einen Überschuss haben,
also da geht mehr Fluss rein, als rausgeht und der fließt irgendwie ab.
Okay, so, wie gehen wir jetzt vor?
Was ist unsere Strategie mit diesem Konzept? Erstens,
erstens der Vorfluss, den haben wir schon gesehen.
Ein Knoten kann überlaufen, hat aber mindestens so viel Eintretenden wie austretenden Fluss.
Das erste, also wir fließt irgendwie was rein,
aber es kann mehr reinfließen,
aber es kann nicht weniger reinfließen
als rausfließt
Zweites, wir haben eine Höhe und wir werden sehen,
die Höhen sind beschränkt,
alles liegt irgendwie zwischen null und zwei V,
warum das so ist, werden wir noch sehen.
Quelle liegt immer auf V, Senkel liegt immer auf null. Drittens,
die Höhe definiert eine Art Gefälle auf den Kanten,
das resitualen Netzwerks und die Höhenfunktionen stellt sicher,
dass immer diese Kanten ein maximales Gefälle von eins haben.
Sie können quasi hochgehen, aber wenn sie runterlaufen,
dann ist das maximale Gefälle eins
Wir jetzt vor, wir werden jetzt unseren Fluss pushen,
da kommt auch der Name. Und zwar werden wir jetzt
keine Pfade mehr suchen. Das ist der große Unterschied.
Wir werden jetzt nicht mehr versuchen, einen Pfad zu finden,
sondern wir verschieben einfach Fluss von einem Knoten zum anderen.
Und der Grund, warum wir das machen können,
ist, naja, wir haben ja hier dann Überschuss,
wenn wir hier was rüberschieben,
dann fließt halt nicht mehr der Knoten-U über,
sondern dann fließt als nächstes eben der Knoten-V über.
Ja, das ist ja kein Problem. Dürfen wir ja
beim Vorfluss machen. Und in diesem Augenblick haben wir uns davon getrennt,
dass wir jedes Mal irgendwelche Pfade suchen müssen über dieses ganze Netzwerk.
Wir können einfach eine Kante lokal betrachten und sagen,
wir verschieben Fluss von U nach V.
Dadurch, dass wir erlauben, dass einfach zu viel Fluss
an dem Knoten ankommen darf
Okay, wenn wir jetzt hier so einen Überschuss haben und
wir pumpen dann den Fluss da so runter,
dann kommen wir irgendwie zu so einer Situation mal,
dass alle Kanten vielleicht gesättigt sind, ja.
Das heißt, wir haben gar keine Möglichkeit mehr,
hier Fluss wegzupumpen von dem Knoten.
Der hat aber trotzdem noch einen Überschuss. Das macht aber nichts,
weil wenn der keine austretenden Kanten mehr hat im residualen Netzwerk,
dann können wir den einfach in der Höhenfunktion ein bisschen höher schieben.
Also so der Knotenfow hier zum Beispiel, der hat ja,
der hat zwar noch Kanten, die hier rausgehen,
aber die haben ja eine Steigung,
die gehen hoch,
die haben kein Gefälle
Also wenn diese Kante nicht da ist von UNAV,
dann könnten wir den KnotenV auch anheben.
Und in der Höhenfunktion ein bisschen höher anordnen. Und dann
hätten wir vielleicht wieder eine Kante mit Gefälle. Und dadurch,
dass die Quelle hier oben ist und die Senke hier unten,
werden wir sozusagen schrittweise diese Knoten so ein bisschen hochziehen und dadurch quasi indirekt erreichen,
dass der Fluss immer von der Quelle irgendwie runterfließt,
dann zu der Senke.
Also wenn die Kante nicht mehr da wäre, könnten wir dann,
und die wäre gesättigt, könnten wir halt den Knoten hochschieben.
Und wir könnten den so viel hochschieben, bis wir die
nächste Kante haben, die wieder Gefälle hat von eins.
Weiter hoch geht nicht, weil dann hätte die Kante ja
Gefälle von zwei
Okay, gut. So, und wie fangen wir an?
So fangen wir an. Wir sagen einfach, wir wissen schon,
aha, die Quelle, die liegt auf Höhe V,
die Senke hier unten liegt auf Höhe null.
Wir legen mal alle anderen Knoten auch auf Höhe null,
um so eine gültige Anfangskonfiguration zu finden.
Jetzt haben wir nur ein Problem. Es gibt ja Kanten,
die gehen von der Quelle hier raus zu irgendwelchen Knoten in dem Netzwerk.
Und die würden dann ja von V auf den Level null gehen.
Das wäre ja blöd, weil das dürfte ja nicht sein,
weil in der Höhenfunktion hätten wir maximal ein maximal erlaubtes Gefälle
von eins und die würden ja jetzt sagen,
Gefälle von V, die würden hier runterlaufen.
Das können wir aber ganz leicht reparieren,
indem wir sagen, wir sättigen einfach diese Kanten.
Also alles, alle kannten die von der Quelle zu den
Knoten gehen, die werden wir voll auslasten. Ja,
also Wasserrand voll auf, die Kante läuft quasi voll,
die ganze Kapazität wird ausgelastet von der Kante. Das dürfen wir,
weil wir müssen ja nur einen Vorfluss definieren und da unten an dem Knoten hier,
an dem Arm, da kommt jetzt unheimlich viel an,
das macht aber nichts, der hat einfach Überschuss, ja, einen Überschussfluss.
Der fließt also jetzt quasi hier einfach raus, ja.
Dadurch, dass wir alle Kanten sättigen,
die von der Quelle zu irgendeinem anderen Knoten gehen, ist sichergestellt,
dass wir keine Kanten haben, die die Höhenfunktion verletzen.
Die liegen alle, entweder zeigen die hier hoch zu der Quelle und alle Anaknoten
liegen eh auf Level 0 und die können kein falsches Gefälle haben.
Das ist quasi unsere Anfangssituation, mit der starten wir,
unsere Berechnung. Jetzt geht's los und wir verändern entweder die
Höhe oder wir verschieben Fluss von einem Knoten zum nächsten.
Die Operation, mit der wir die Höhe verändern,
heißt Relabel, diese Relabel-Operation, hebt einen Knoten an,
die wird er nie absenken,
die hebt ihn immer an
Ja, und zwar hebt die so einen Knoten so lange an, bis wir eine Kante haben mit Gefälle
1. Immer wenn wir das gemacht haben und haben den Knoten so hochgehoben,
dass wir eine Kante mit Gefälle 1 haben,
dann können wir als nächstes eine Push-Operation machen und mit der
Push-Operation Fluss von diesem Knoten wegtransportieren zu dem anderen,
der dann in dem Gefälle 1 unterhalb dem Knoten liegt.
Okay, das heißt, wir haben zwei Operationen,
wir haben eine Push-Operation,
um über eine Kante mit Gefälle 1 Fluss zu transportieren und
wir haben eine Relabel-Operation,
um die Höhe eines Knotens anzupassen
So, und das habe ich eben schon gesagt,
dadurch, dass wir diese Höhenwerte von Quelle und Senke vorgeben,
erzeugen wir quasi automatisch jetzt eine Situation,
dass der Fluss zur Senke fließt.
Wenn sie sich jetzt die Situation vorstellen, jetzt könnten sie
diesen Knoten V anheben, ja, um eine Einheit.
Und der Fluss kann in dem Augenblick aber nicht zurück zur Quelle,
weil wir dürfen ja nur Fluss über Kanten transportieren,
die ein Gefälle haben von eins, ja.
Der kann also nicht zurück zur Quelle und deswegen kann er
sich nur hier weiter nach unten bewegen und er fließt automatisch
irgendwie in Richtung Senke
Das Ganze geht so lange weiter, bis alle Kanten zur Senke irgendwie gesättigt sind,
ja, wenn wir da alles hingepumpt haben.
Irgendwann werden dann diese Knoten hier über die Position von S steigen,
dann gehen die hier weiter nach oben und dann gibt es plötzlich wieder eine Möglichkeit zur Quelle zurückzufließen.
Und das ist sozusagen der letzte Überschuss, der da noch übrig ist,
der wird dann in der nächsten Phase wieder zurück zu S fließen.
Aber zu dem Zeitpunkt gibt es schon keine Möglichkeiten,
mehr zur Tee zu kommen, sodass wir also schon sichergestellt haben,
als erstes Mal maximieren wir den Fluss,
wir pumpen alles runter zur Tee, was geht,
dann haben wir noch Überschuss und dadurch,
dass wir die Knoten hier sukzessive über das Niveau von dem Startknoten anheben von der Quelle,
fließt der dann zurück zur Quelle und wir haben dann den Fluss korrekt berechnet.
Ein kompliziertes Schema, aber ich finde,
wenn man sich das ungefähr so vorstellt mit diesen Bedeutung der Höhenwerte,
dann ist es, glaube ich, auch nachvollziehbar,
was man hier einfach probiert.
Das könnte man sich sogar mechanisch vorstellen, wie das Ganze gehen könnte.
Okay, so, jetzt haben wir also zwei Operationen,
die müssen wir uns genau angucken.
Die Operationen selber sind relativ einfach. Wir sind hier,
kommt jetzt die erste, die Push-Operation und die Relable-Operation.
Wir müssen nur gucken, wie wir die entsprechend korrekt anwenden
Die Push-Operation hat jetzt die Aufgabe Fluss von einem Knoten-U zu einem Nachbarknoten-V zu transportieren.
Und das Wichtige an der Push-Operation ist nicht die Operation selber,
sondern wann wir sie anwenden dürfen und dass wir das auch einhalten.
Die Push-Operation darf angewendet werden, wenn der Knoten einen Überschuss besitzt,
also der muss, der muss sozusagen mehr Fluss bekommen, wie er hat.
Sonst hätten wir ja das Problem, dass wir potenziell nachher
den Vorfluss verletzen, weil mehr Fluss rausfließt, als reinkommt.
Also der muss einen Überschuss haben. Dann muss der Knoten-U eine Kante besitzen mit Restkapazität,
also es muss im resitualen Netzwerk eine Kante von U nach V geben,
ja, mit Kapazität größer Null, ja,
noch sagen, die Kante existiert im resitualen Netzwerk.
Klar, sonst können wir ja die nichts von U nach V was pumpen.
Und diese Kante muss das Gefälle von Eins haben.
Das heißt, H von U, muss H von V
plus Eins sein. Nur wenn die drei Kriterien gelten,
dann dürfen wir Fluss von U nach V transportieren.
Wie viel Fluss pumpen wir? Naja, wir wissen,
wir müssen die Vorflusseigenschaft erhalten. Das heißt,
wir können maximal so viel Fluss von U nach V transportieren,
bis die Kante ausgelastet ist, bis sie saturiert ist.
Also will keinen weiteren Fluss über die Kante schieben können,
dass sie eine Möglichkeit oder wir haben die Situation,
dass der Knoten U keinen Überschuss mehr hat,
dass wir den ganzen Überschuss abgebaut haben,
könnte natürlich auch sein.
Und dann können wir auch keinen weiteren Fluss darüber pumpen
Das sind die beiden Möglichkeiten, um das, wie das begrenzt wird. So,
die Push-Operation sieht dann einfach so aus, hier nur als Kommentar,
angewendet werden darf sie, wenn diese Kriterien gelten,
der Überschuss muss größer sein als null,
die Restkapazität über die Kante muss größer sein als null und die Höhenfunktion muss so sein,
dass HU gleich Hafen V plus 1 ist.
Was machen wir? Wir pushen einen Fluss über die Kante und zwar vom Wert her,
das Minimum des Überschusses an der Kante, an dem Knoten-U und der Restkapazität her.
Okay, Aber wir bestimmen einfach das Minimum, wie viel das ist.
Das bezeichnen wir ja als DF. Wenn die Kante,
jetzt müssen wir den Fluss aktualisieren, das kennen wir schon,
wenn die Kante UV in dem ursprünglichen Grafen enthalten ist,
dann erhöhen wir den Fluss über die Kante UV, um diesen D-Wert.
Anderenfalls ist das wieder eine Kante,
die im residualen Netzwerk drin ist, im Originalgraf,
in der umgekehrten Richtung, dann müssen wir den Fluss reduzieren,
dass so dieses IFLs-Konstrukt haben wir jetzt schon ganz häufig gesehen.
Und jetzt müssen wir nur noch dafür sorgen,
dass der Überschuss korrigiert wird und zwar der Überschuss an dem Knoten,
uh, der verringert sich um D und der Überschuss an dem Knoten V,
der erhöht sich um D.
Ja, wir haben also eine gewisse Menge Fluss,
einfach nur über eine Kante von U nach V verschoben
Ja, das ist die Push-Operate, ja. Okay.
Für so eine Push-Operation haben wir jetzt einfach zwei Fälle.
Wir wissen nicht genau, woher kommt die Flussmenge?
Entweder der Überschuss wird abgebaut oder die Kapazität hier wird ausgeschöpft.
Und die beiden Push-Operationen, die müssen wir trennen,
weil die algorithmisch, haben diese beiden Situationen ganz unterschiedliche Konsequenzen.
Und das wird für die Laufzeitbetrachtung wichtig sein.
Deswegen definieren wir einen sättigenden und einen nicht sättigenden Push.
So ein Push, der ist sättigend, wenn die Kante,
über die wir pushen, nachher keine Restkapazität mehr haben.
Also wenn wir da alles drüber gepumpt haben, haben,
bis die Kante ausgelastet ist.
Sollen sie es denn nicht sättigen? Nicht sättigend bedeutet,
dass wir den Überschuss abgebaut haben und wir hatten nicht genug
Überschuss an diesem Knoten, um die Kante zu saturieren.
Dann ist er nicht sättigend. Also das sättigend bezieht sich immer
auf die Kantenkapazität. Also nicht sättigender Push entfernt den Überschuss,
weil wir nehmen ja das Minimum von den beiden und wenn der nicht sättigend war,
dann wissen wir, dass der Knoten nachher keinen Überschuss hat.
Ist ja auch was wert. Haben wir uns im Fluss ein bisschen genährt,
weil Überschuss wollen wir eigentlich abbauen im Laufe der Zeit.
Okay, das ist das erste Lämmer hier,
also ein nicht sättigender Push von ONAV entfernt den Überschuss in
Und nach der Push-Operation gilt dann eben Evan-U gleich 0.
Der erzeugt natürlich einen Überschuss bei V, notwendigerweise, erzeugt er den.
Die zweite Operation, die wir brauchen, das ist die,
die die Höhenwerte anpasst, das ist die Relabel-Operation.
Auch die, also die vergrößert jetzt den Höhenwert,
auch die hat ganz feste Regeln, wann wir sie anwenden dürfen.
Und die Regeln sind das Entscheidende. Die Regeln,
wann wir sie anwenden dürfen, ist erstens,
der Knoten-U muss Überschuss haben, also Evan-U muss größer sein als null,
sonst dürfen wir die nicht anwenden.
Und die zweite Regel ist, HV&U ist kleiner,
gleich HV&V,
für alle kannten im resitualen Netzwerk
Das heißt, es gibt keine Kanten mit Gefälle.
Nur dann dürfen wir die Relabel-Funktion benutzen.
Ja? Was macht die Relabel-Funktion? Ja, die erhöht einfach
die Höhe von dem Knoten U. Und zwar einfach so,
dass danach im Resitual-Netzwerk eine Kante mindestens eine Kante mit Gefälle 1 existiert.
Ja, wir schieben den Knoten so weit nach oben,
bis die erste Kante entsteht mit Gefälle 1.
Und das können wir ganz leicht berechnen. Da gehen wir
einfach über alle Kanten im resitualen Netzwerk und setzen das suchende minimale Höhe.
Das können wir um eins erhöhen. Und das ist unsere
neue Höhe für den Knoten
Also für diese Relabel-Operation müssen wir einmal über die Advertienzliste gehen, um das zu bestimmen. Okay,
so, jetzt müssen wir erst mal schauen,
ob unsere Funktion überhaupt wohldefiniert ist,
ob wir die so überhaupt ausführen dürfen,
ob das alles korrekt ist und das müssen wir bei der Relabel-Operation uns anschauen,
weil da muss ja dieses Minimum definiert sein,
sonst wüssten wir ja nicht, was wir tun sollen an dieser Stelle.
Das heißt, diese Menge hier, die darf nicht
leer sein. Also wenn dieser Knoten-U keine Kante hat,
die aus den Knoten heraus läuft, mit einer Resis-Restkapazität,
dann könnten wir das Minimum nicht bestimmen.
Das wäre blöd. Dann könnten wir die Relaable-Funktion so nicht implementieren.
Das ist das Erste, was wir sehen müssen.
Das ist aber ganz leicht zu sehen.
Also diese Rail-Alable-Funktion ist wohl definiert. Das heißt,
wir können sie immer ausführen, wenn diese Bedingungen gelten.
Denn der Knoten hat ja einen Überschuss. Sonst dürfen wir
sie ja nicht ausführen. Der hat einen Überschuss. Und
wenn er einen Überschuss hat, dann fließt ja was in
den Knoten rein, sonst könnte er keinen Überschuss haben.
Und wenn was reinfließt, dann gibt es natürlich im
resitualen Netzwerk eine Kante in umgekehrter Richtung mit Restkapazität,
weil sie den Fluss ja wieder zurücknehmen können.
Das heißt, die gibt es immer. Das heißt,
wenn der Überschuss größer ist als Null, dann gibt es Kanten,
die Uhr verlassen. Und wenn es Kanten gibt,
die Uhr verlassen, dann ist diese Menge hier nicht leer.
Und wenn die nicht leer ist, können wir das Minimum berechnen.
So, damit ist die Funktion so, dass wir sie
immer ausführen können. Wenn die Bedingungen gelten. Gut,
Initialisierung habe ich eigentlich an dem Bild schon gezeigt,
wie starten wir, in der Fluss wird am Anfang initialisiert,
wenn es eine Kante ist, die die Quelle verlässt,
dann ist der Fluss gleich der Kapazität.
Also alle kannten die aus der Quelle rausgehen, werden voll ausgelastet.
In umgekehrter Richtung, da bin ich mir jetzt gerade Ähm...
Das muss ich nochmal nachgucken, ob das wirklich stimmt.
Das könnte noch so ein Überbleibsel sein von der Anti-Parallelität.
Das muss ich nochmal nachgucken, ob das tatsächlich da noch so drinsteht.
Auf alle Fälle, alle anderen Kanten kriegen den Fluss null.
Die Höhenfunktion, die Höhenfunktion hat für den Knoten S einfach Betrag von V.
Und für alle anderen Knoten, inklusive der Senke, den Wert null.
Das ist die Staatssituation, die wir haben.
Das kommt wahrscheinlich raus
Ja, genau. Der negative Flüsse haben wir eigentlich nicht.
Und das ist noch ein Überbleibsel. Ich habe es ja
am Anfang schon mal erzählt,
dass in dem Kormen diese ganze Beschreibung sich geändert hat,
in der alten Version des Kormen gab es Anti-Parallelität von Flüssen.
Da hat man das so modelliert, dass jeder Fluss,
der in die eine Richtung geht,
war ein negativer Fluss zusätzlich in der anderen Richtung auf der Gegenkante.
Und dann kommt man aber ehrlicherweise irgendwann doch ganz schön durcheinander.
Das hat man dann auch gemerkt. Deswegen sind diese anti-Parallelen
Kanten verschwunden. Das war die eine Konsequenz. Und dieses Kriterium,
dieser Anti-Parallelität auch auf den Flüssen ist dann auch verschwunden.
Das ist wahrscheinlich, muss ich das noch von der Folie löschen.
Das gucke ich nochmal nach. Okay, so. Ist das
jetzt so in Ordnung? Ja, der ist kapazitätsbeschränkt,
der Fluss. Das wissen wir. Und ist einseitig Fluss
erhalten. Es fließt überall mehr rein, als rausfließt.
Es ist also ein gültiger Vorfluss. Was gilt für die
Höhenfunktion? Wir wissen, einen Höhenunterschied gibt es nur,
wenn der eine Knoten die Quelle ist. Und dann wissen wir aber,
dass diese Kanten von S nach V alle gesättigt sind.
Und nach der Initialisierung. Und deswegen ist im resitualen Netzwerk
die Kante von S nach V ja nicht enthalten,
sondern nur in umgekehrter Richtung.
Und deswegen ist hanegültige Höhenfunktion. Außer einem Fluss und entsteht
natürlich jetzt auch der Überschuss an den Knoten.
Den können wir auch ganz leicht bestimmen. Wir wissen,
dass der Überschuss hier an den Knoten gerade die Kapazität ist,
wenn das ein Zielknoten ist von einer Kante, die von der Quelle kommt.
Also wenn wir jetzt eine Quelle haben von S zu einem Knoten-U,
dann hätten wir gerade die Kapazität dieser Kante als Überschuss an
dem Knoten-U und sonst haben wir noch gar keinen Überschuss.
Der Rest ist null. Für die Quelle Irgendwo muss dieser Überschuss
ja herkommen. Deswegen werden wir uns einfach für die Quelle mal merken und sagen,
ja, die hat jetzt einen negativen Überschuss,
weil da fließt ja jetzt alles raus.
Ja, und dann messen wir quasi, wie viel daraus fließt.
Und das ist unser negativer Überschuss. Es ist einfach minus
die Summe aller Knoten, die wir hier haben und da die entsprechende Kapazität.
Es ist nur eine Definitionsfrage hier. Okay. So,
jetzt können wir angeben, wie wir diese Initialisierung als Pseudocode formulieren.
Das ist ganz einfach. Wir müssen also nur diese Zustände herstellen. Am Anfang Höhenfunktionen und Überschrift Schluss wird,
mit null initialisiert, dann gehen wir durch alle Kanten durch,
initialisieren den Fluss, entlang dieser Kante mit null,
dann setzen wir den Höhenwert der Quelle auf die Anzahl der Knoten in dem Netzwerk,
dann gehen wir die Adjezenzliste der Quelle durch und setzen den Fluss,
entlang dieser Kante auf CSU, auf die Kapazität.
Sie sehen hier aus dem Pseudo-Code,
habe ich es rausgenommen, da fehlt sogar die Zeile.
Die Zeile 10 fehlt, das war die mit der Gegenrichtung.
Also dann erhöhen wir den Überschluss an dem Knoten U
und wir senken quasi den Überschuss an der Quelle,
um den entsprechenden Und dann haben wir die,
dann haben wir quasi den Grundzustand, die Initialisierung erreicht.
Ja? Okay, wie läuft jetzt der Algorithmus ab?
Das ist der Algorithmus. Das ist unser Flussalgorithmus hier.
Der ist noch sehr grob beschrieben, aber er sagt schon genau,
das Vorgehen, wir initialisieren unseren Vorfluss,
so wie ich das hier beschrieben habe.
Dann haben wir wieder die große Wildschleife, so ähnlich wie auch bei den anderen Flussalgorithmen.
Und die sind jetzt bezüglich der Bedingungen nur anders aus.
Wir gucken uns die Push- und Relabel-Operationen an und wir sagen einfach,
solange es noch eine Operation gibt, die wir ausführen können,
solange es irgendeine Operation gibt, auf irgendeinen der Knoten,
die wir ausführen dürfen, wo die Bedingungen erfüllt sind.
Nehmen wir die und führen sie aus. Also wir suchen
irgendeine Push- oder Relabel-Operation auf irgendeinen Knoten und machen das.
Und das machen wir so lange, bis wir keine Operation mehr ausführen können.
Und die Behauptung ist, wenn wir das getan haben,
irgendwann, also die erste Behauptung ist,
irgendwann können wir eine der Operationen nicht mehr ausführen oder können wir beide Operationen auf allen Knoten nicht mehr ausführen.
Das heißt, der Algorithmus terminiert. Und die zweite Behauptung
ist, wenn das eintritt, dann haben wir
den Maximaleinfluss berechnet
Ja, es gibt noch eine Menge zu tun für die Push-Relabel-Algorithmen.
Wir müssen nämlich erst mal zeigen, dass dieses generelle Schema überhaupt funktioniert.
Dass das überhaupt dazu führt, dass wir einen Maximaleinfluss kriegen.
Das ist das Erste, was wir uns angucken müssen.
Und dann müssen wir anschauen, wie lange wird denn das alles dauern.
Auch das kann natürlich im Zweifelsfall sehr, sehr viele Wild-Schleifen-Durchläufe bedingen,
wenn wir hier einfach diese Relabel- und Push-Operation nacheinander suchen.
Und wir müssen natürlich überlegen,
wie finden wir denn überhaupt diese Operation,
die wir ausführen dürfen
Okay, also das ist unsere Beweisstrategie, die wir haben werden.
Also wie wir zeigen werden, dass das tatsächlich funktioniert. Das Erste,
was wir machen werden, ist, wir werden sagen,
wenn der Algorithmus so läuft, wie wir ihn dir vorgestellt haben,
dann wird der Fluss, den wir haben,
zu jeder Zeit immer ein gültiger Vorfluss sein und die Höhenfunktion wird immer eine gültige Höhenfunktion sein.
Also diese beiden Eigenschaften werden wir niemals verletzen. Das Zweite,
was wir dann zeigen, ist, wenn er terminiert,
wenn der Algorithmus tatsächlich aufhört,
dann haben wir auch einen maximalen Fluss berechnet.
Und das Dritte, was wir dann zeigen werden,
ist,
dass er tatsächlich irgendwann anhält
Und wenn wir die drei Sachen gezeigt haben,
dann wissen wir, wer funktioniert der Algorithmus.
Und dann können wir uns an die Details ranwagen und eine gute Strategie ausarbeiten,
wie wir quasi jetzt dann diese Push-and-Relabel-Operation finden.
Und natürlich ist es hier auch noch, dieser Algorithmus hat ja eine Menge Freiheitsgrade.
Also es gibt ja wahrscheinlich viele Push-and-Relabel-Operationen, die wir ausführen können.
Also da gibt es viele Knoten, bei denen wir
was machen können. Wir können auch vielleicht eher auf Gurten-U
einen Push machen oder, also aufgrund von Uhen,
Push oder ein Relabel oder auf einer Kante UV und einen Push.
Also da gibt es viele Optionen. Und sowas ist eigentlich
immer sehr, schön, weil hier haben wir Optimierungspotenzial.
Wir werden sehen, der Algorithmus wird korrekt sein,
egal in welcher Reihenfolge wir pushen und relabel ausführen.
Aber die Reihenfolge, in der wir das machen,
hat potenziell Einfluss auf die Laufzeit und wir können noch weiter optimieren an der Stelle.
Okay, gut. So,
bevor wir jetzt noch groß in den Beweis einsteigen,
ich habe ja sowieso ein paar Mal schon ein paar Minuten überzogen,
würde ich sagen, hier ist ein guter Punkt,
um mal eine Pause zu machen.
Wir können jetzt schon mal alles sacken lassen und dann werden wir
uns nächsten Montag an diesen Beweis hier ranwagen und schrittweise zeigen,
dass diese Push-Relabel-Algorithmen auch tatsächlich das tun,
was wir uns davon erhoffen.
Gut,
das war es dann für heute