die Korrektheit dieses Algorithmus basiert quasi zentral auf einem speziellen Lämmer.
Davon haben wir den ersten Teil des Beweises in der letzten Vorlesung auch schon gesehen. Und dieses Lämmer besagt,
wenn C- und C-2 starke Zusammenhangskomponenten unseres Grafen sind,
also zwei unterschiedliche starke Zusammenhangskomponenten, dann gilt für alle Kanten,
UV, die in der Komponente C beginnen und in der Komponente C-Strich enden,
dass die Finishing-Zeit dieser Komponente C, also aller Knoten,
die da drin sind, das Maximum davon,
größer ist als die Finishing-Zeit von C-Strich.
Das heißt, die Kanten verlaufen immer nur von Komponenten mit
größer Finishing-Zeit, zu Komponenten mit kleinerer Finishing-Zeit. Okay,
um das zu zeigen, überlegen wir uns wieder anhand von einer Platzierung,
von so ein paar Knoten und Kanten, welche Szenarien auftreten können.
Wir machen zwei eine Fallunterscheidung. Der erste Fall ist mal,
dass wir die Komponente C als erstes entdecken.
Das Szenario sehen Sie auf der rechten Seite der Folie.
Das heißt, wir haben ja also die Komponente C,
da sind viele Knoten drin Und von irgendwo kommen wir jetzt
in den Graphen und sehen hier den Knoten
X als ersten Knoten der Komponente C. Und irgendwann später werden
wir dann die Komponente C-Strich entdecken. Gut,
jetzt haben wir den ersten Knoten X hier gesehen und wir wissen dann,
dass die Discovery-Zeit der ganzen Komponente, die entspricht der Discovery-Zeit von diesem Knoten,
von dem ersten, den wir hier sehen.
Und das nächste, was wir jetzt wissen, ist,
dass wenn wir hier an dem Knoten X angekommen sind,
das ist ja der erste der Komponente,
da sind die anderen offensichtlich alle noch weiß.
Die anderen Knoten, die müssen alle weiß sein,
weil das ist ja der erste,
er werden wir mit einem anderen Knoten
Angefangen. So, wenn die alle noch weiß sind,
dann wissen wir, dass diese ganzen Knoten der Komponente,
die sind alle Nachfahren in unserem DFS-Baum, ja,
weil das ist das Theorien der weißen Pfade,
wenn ein weißer Pfad existiert,
dann ist der Knoten noch ein Nachwahre von X.
Und wir haben zusätzlich auch die Kante, die geht von U
nach V und V ist jetzt in der anderen starken Zusammenhangskomponente.
Und hier haben wir auch, weil die Komponente ja
noch gar nicht entdeckt wurde. Wir haben ja gesagt,
die Discovery-Zeit der Komponente ist die Striche ist größer.
Die sind also auch alle weiß und dementsprechend sind auch die Knoten V und W Alle,
die hier unten in der Komponente C-Strich sind,
auch nachfahren von unserem Knoten X.
So, wir haben also hier zwei Komponenten,
die im Baum der Tiefensuche quasi hier einen großen Teilbaum beschreiben werden.
Und nach dem Theorien der weißen Pfade sind die heute alle Nachfolger von X. Und wir wissen auch schon,
mit dem Klammerungstheorie,
dass wir natürlich diesen Knoten X erst abschließen werden und die Finishing-Zeit erst wieder abspeichern werden,
wenn alle anderen Knoten bearbeitet wurden,
insbesondere eben alle Knoten der Komponente C-Strich
Und deswegen ist die Finishing-Zeit von dieser Komponente auf alle Fälle
kleiner als die Finishing-Zeit, von der ursprünglichen Komponente C.
Und das wollten wir zeigen. Das ist der einfachere Fall
. Wir wissen allerdings nicht, dass wir wirklich zuerst die
Komponente C entdecken. Es könnte ja durchaus auch sein,
dass wir, wir wissen nicht genau,
in welcher Reihenfolge wir die Knoten besuchen,
dass wir zuerst die Komponente C-Strich besuchen.
Es ist ja nicht vorgegeben, in welcher Reihenfolge wir das tun.
Und das ist der zweite Fall,
der ein bisschen komplizierter ist
Und jetzt gehen wir mal davon aus,
dass die Discovery-Zeit von C größer ist als die von C-Strich.
Also wir besuchen RSC-Strich und werden dann später C sehen.
Und das Szenario ist hier rechts wieder aufgemalt.
Wir kommen irgendwo in diesen Teilgraf hinein,
indem wir als erstes den Knoten Y sehen und der Knoten
Y ist der erste Knoten der Komponente C-Strich.
Okay, so, was passiert jetzt? Die Discovery-Zeit von
der Komponente hier wird auf D von Y gesetzt.
Das ist in diesem Teil. Und es ist wieder so,
alle Knoten dieser Komponente sind zu dem Zeitpunkt weiß,
es gibt weiße Pfade von Y zu diesen Knoten hier drin,
insbesondere auch zu W-Strich.
Und die sind alle nachfahren. Das muss so sein.
Und die Finishing-Zeit von dieser Komponente werden wir setzen,
sobald wir irgendwas Y wieder zurückkehren. So,
wenn wir jetzt mal die Komponente C betrachten,
wir wissen, es gibt so eine Kante,
die führt uns von einem Knoten U in der Komponente C zu einem boten V in der Komponente C-Strich.
Dann wissen wir, wie sind auch alle weiß.
Die müssen ja alle weis sein, weil wir gesagt haben,
die Discovery-Zeit, die ist ja größer.
Dann haben wir also, diese Komponente haben wir
noch gar nicht betrachtet
Das heißt, für alle Knoten weh aus der anderen Komponente
C hier oben gilt, die sind alle weiß.
Wir wissen auch, dass der Graf, der entsteht,
indem wir die starken Zusammenmlungskomponenten als Knoten betrachten.
Das ist der G-S-C-C, dass der ein Dack ist.
Der hat also keine Zyklen, kann er ja nicht,
wenn der Zyklen enthalten würde,
würden ja mehrere Komponenten wieder zu einer Komponente zusammenwachsen.
Der hat also keine Zyklen. Und wir wissen, es gibt ja
diese Kante von UNAVs. Das heißt, es gibt eine Kante,
die führt uns von der Komponente C zur Komponente C-Strich.
Also kann es keine Kante geben von C-Strich,
weil dann wäre das ja eine starke Zusammenhangskomponente.
Das kann nicht gehen. Wolkig gibt es keinen einzigen,
kein einziges Knotenpaar, R&S,
wobei er in den C-Strich liegt und S wäre dann in C,
sodass hier eine Kante hochgeht.
Es gibt also keinen Pfad, der uns von C-Strich nach
C führt. Ja, und dann ist auch klar,
dass wir diese Knoten hier oben alle gar nicht besuchen können,
wenn wir von Y aus starten.
Es gibt keinen Pfad, der uns in diese Komponente hineinführt.
Wenn wir dann also die Finishing-Zeit festlegen von Y,
wenn wir hier diesen Knoten endgültig abgearbeitet haben,
sind die Knoten immer noch alle weiß
Weil es gibt ja keinen Pfad und der uns hier hochgeführt hätte. Okay,
und demnach ist die Finishing-Zeit der Komponente C,
die ist ja größer als die Discovery-Zeit der Komponente C und die Discovery-Zeit der Komponente C
ist größer als die Finishing-Zeit von dem Quoten Y,
weil die sind ja alle noch weiß,
wenn wir Y abschließen und dementsprechend wissen wir,
dass die Finishing-Zeit von C auch größer sein muss,
als die Finishing-Zeit von C-Strich.
Also nochmal ein bisschen bildlicher gesprochen, wenn wir tatsächlich mit C anfangen,
dann wissen wir, dass wir erst C-Strich vollständig bearbeiten,
bevor wir wieder zurückkehren zu C und damit ist die Finishing-Zeit sie größer als sie von C-Strich.
Und im zweiten Fall, wenn wir erst mit C-Strich anfangen,
dann wissen wir, es gibt keinen Pfad,
der uns noch zäh führen kann.
Und deswegen ist die Finishing-Zeit von C-Strich kleiner als die von C,
weil diese Komponente werden wir erst zu einem späteren Zeitpunkt vollständig bearbeiten.
Okay, so, jetzt drehen wir alle Kanten um in dem Graphen,
das ist der zweite Schritt des Algorithmus.
Und dann ist relativ schnell klar,
dass dieser Algorithmus tatsächlich die Komponenten mit jedem DFS Visit-Aufruf
in der DFS-Hauptroutine aufzählt
Das besagt dieses Theorien, das ist die Korrektheit des SCC-Algorithmus.
Wir haben also einen gerichteten Graphen G und dann bestimmt
der Algorithmus Strongly Connected Components, den wir gesehen haben,
die starken Zusammenhangskomponenten von G. So, wie beweisen wir das?
Die Idee des Korrektheitsbeweises ist,
dass wir eine vollständige Induktion machen über die Schleife der Zeile 8 bis 11,
das müssen wir gerade gucken,
das ist hier also über,
wir haben quasi eine Schleifeninnenvariante für diese Vorschleife
hier in Zeile
Die besagt, dass wir hier genau das Richtige tun,
nämlich, dass mit jedem DFS Visit-Aufruf genau eine starke Zusammenhangskomponente markiert wird.
Das werden wir im Folgenden beweisen. Alles andere ist dann
eigentlich klar. Wie machen wir das? Ja,
wie bei Schleifen in Varianten üblich, können wir die Korrektheit
quasi durch eine Induktion zeigen. Wir zeigen quasi,
dass in jeden Durchlauf der Algorithmus eine starke Zusammenhangskomponente ausgibt.
Und dass daraus resultiert, dass auch im nächsten Durchlauf wieder
genau eine Komponente ausgegeben wird
Okay, so, die Situation ist dann ungefähr so,
wie hier bildlich dargestellt. Also dieses K hier, das sind die,
das sind quasi diese Durchläufe, die wir haben. Also immer dann,
wenn DFS Visit in der Hauptroutine gestartet wird,
wird ein Baum erzeugt, ein DFS-Baum,
B von K. Ja,
und zu irgendeinem speziellen Zeitpunkt sind wir jetzt dabei und rufen zum Karten mal diese DFS Visit-Routine aus der Hauptroutine auf.
Zu dem Zeitpunkt ist die Situation wie folgt. Also wir
haben den dieses DFS Visit schon k-minus eins Mal aufgerufen und
wir gehen davon aus, dass der Algorithmus korrekt war.
Das ist ein Induktionsbeweis. Und in all diesen Aufrufen haben wir
jeweils eine DFS-Baum erhalten und der entspricht von bezüglich der Knotenmenge genau einer starken Zusammenhangskomponente.
Das sind die hier, das ist der Baum,
k-minus eins und der Baum kam minus zwei und kam minus
drei und so weiter bis bis eins.
Das sind also immer starke Zusammenhangskomponenten und was noch wichtig ist,
alle diese Knoten sind ja vollständig abgearbeitet,
folglich sind die alle schwarz
Die haben alle schwarze Farbe, weil mit denen sind wir fertig.
Wenn wir bei einem DFS in der Hauptroutine die DFS Visitroftine aufrufen,
dann gibt es zu dem Zeitpunkt,
oder wir können auch sagen, zwei Zeilen später,
wenn wir in diese Routine erstmals eingetreten sind,
genau einen grauen Knoten.
Das ist der Knoten, der erste Knoten,
den wir besuchen von dem neuen Baum, der ist dann grau.
Die anderen Knoten hier oben, die sind alle schwarz,
die haben wir schon abgearbeitet und die Knoten hier unten,
die wir noch nicht gesehen haben, die sind alle weiß.
Es gibt nur, wie gesagt,
diesen einen grauen Knoten und jetzt fangen wir an,
diesen Baum zu explorieren mit der DFS Visitroutine
Gut, das ist das Szenario, in dem wir uns befinden.
So sieht also die Färbung all unserer Knoten aus. Und jetzt müssen wir zwei Dinge zeigen. Das erste,
was wir zeigen müssen, ist, dass dieser DFS-Bahn,
der mit der Wurzel-U jetzt durchlaufen wird, auf alle,
der muss quasi eine Obermenge sein,
der einer starken Zusammenhangskomponente C. Also wir sagen mal,
der ist auf alle Fälle größer als die starke Zusammenhangskomponente C. Und im zweiten Schritt werden wir dann zeigen,
dass dieser Baum sogar eine Teilmenge ist und wenn er eine Obermenge und eine Teilmenge ist,
dann haben wir gesehen,
dass die beiden identisch sind
Ja, okay. Also, der erste Teil ist ganz einfach.
Also, wieso ist die starke Zusammenhangskomponente eine Teilmenge dieses Baumes?
Ja, wir wissen, dass zu diesem Zeitpunkt,
den wir betrachten, wo wir diesen Knoten grau gefärbt haben,
sind ja alle Knoten hier weiß.
Und wir haben das Theorien der weißen Pfade. Wir wissen,
es gibt Pfade von U zu all diesen Knoten.
Warum gibt es die alle? Naja, ist klar,
ist ja eine starke Zusammenhangskomponente.
Sprechend gibt es natürlich auch Pfade von U
zu allen Knoten
Hier der Komponente. Und weil die alle weiß sind,
haben wir das Theorien der weißen Pfade.
Und das sagt uns dann, dass alle diese Knoten hier unten,
die werden alle Nachfolger der Wurzel hier.
Und dann sind die entsprechend alle in diesem Baumb von K. Also das heißt,
die Komponente ist auf alle Fälle eine Teilmenge von diesem Baum,
den wir explorieren.
Ich glaube, das ist auch vollkommen logisch, dass das
so sein muss. Die schwierige Frage ist natürlich,
wie können wir denn verhindern, dass dieser Baum nicht größer wird?
Dass er nicht auch Knoten betrachtet,
die hier eben nicht mehr Teil der starken Zusammenhangskomponente sind
Das ist der zweite Teil des Beweises. Jetzt müssen wir zeigen,
dass der Baum tatsächlich auch eine Teilmenge der Komponente ist.
Und dafür habe ich Ihnen mal das aufgemalt, wie das aussehen könnte.
Hier oben sind wieder die anderen Bäume, die wir schon vollständig abgearbeitet haben.
Hier ist wieder unsere Komponente, die wir gerade betrachten.
Und U ist dieser eine Knoten,
der schon grau ist, also die Wurzel dieses Baumes.
Und dann haben wir eine ganze Reihe von Knoten,
die gehören alle zu der starken Zusammenhangskomponente.
Und jetzt könnte es natürlich eine Kante geben,
die von V zu einem Knoten W läuft,
die hier so runter zeigt.
Und W ist aber in einer anderen starken Zusammenhangskomponente
Und dann gibt es halt keine Kante, die irgendwie wieder hochführt.
Deswegen sind die nicht in einer starken Zusammenhangskomponente,
aber so eine Kante von V nach W, könnte ja durchaus existieren.
So, wir nehmen jetzt an, dass es so
eine Kante gibt und so ein Knotenweh mit dieser Eigenschaft.
Und wir werden zeigen, dass das ein Widerspruch ist.
Dass es tatsächlich nicht sein kann,
dass wir so eine Kante von V nach W haben.
Gut, wie machen wir das? Also unser Knotenweh ist
also in dem Baum enthalten. Es gibt also so eine Kante,
die führt uns von V nach W. W ist in einer
anderen starken Zusammenhangskomponente C-Strich.
Und die ist ungleich der Komponente
Und was wir auch machen ist,
wir nehmen für W den Knotensen, der am dichtesten dran liegt,
an unserer Wurzel, der diese Eigenschaft hat.
Das vereinfacht einfach den Beweis. Wir wissen, der Knoten gehört ja
zur Komponente C. Und wir schauen uns einfach die Stelle an,
wo wir die Komponente C das erste Mal verlassen.
Und das wäre diese Kante von V nach W. Können auch sagen,
wie ist so gewählt, dass die Pfadlänge von U nach W minimal ist.
Also die Anzahl der Kanten auf dem Weg. Gut,
was gilt zu? Was geht jetzt? Also wir wissen,
wir wissen, dass für die Bäume eins bis kminus eins,
das sind die, die wir alle bearbeitet haben, die hier oben.
Da sind alle Knoten schwarz. Die sind alle komplett abgearbeitet.
Dieser Knoten will hier, der muss aber weiß sein,
weil der ist ja ein Nachfolger von U und dann mit dem Theorien der weißen Pfade,
wissen wir, der Knoten muss zu dem Zeitpunkt weis sein,
sonst wäre er kein Nachfolger.
Das heißt, dieser Knoten hier, dieser Knoten weh,
der kann nicht zu einer anderen Komponente gehören,
die wir früher entdeckt haben.
Der muss zu einer Komponente gehören, die wir später entdecken.
Gut, also das heißt C-Strich, die Komponente,
zu denen der Knoten weh gehört,
ist ungleich einem BI mit einem I von 1 bis Camino
seins.
Also dazu gehört das nicht
So, W ist Nachfahre von U, da W in dem Baum ist.
Das heißt, es existiert ein weißer Pfad von U nach W.
So, VW ist jetzt also die letzte Kante dieses Fades.
So haben wir die ausgewählt. Und wir wissen,
aufgrund der Reihenfolge, wie wir die Knoten prozessieren,
dass die Finishing-Zeit der Komponente C kleiner ist als die Finishing-Zeit der Komponente C-Strich.
Wir haben die Kanten, wir haben die erst der Größe nachsortiert,
aber jetzt haben wir alle Kanten umgedreht.
Da ändert sich auch das Größenverhältnis
Okay, so, wir betrachten mal die Wurzel der Komponente C-Strich,
das sei mal der Knoten S, keine Ahnung,
irgendeiner dieser, hier, das wäre jetzt eine Wurzel,
dieser Komponente, das ist der Knoten,
der nachher von dieser Komponente die kleinste oder die größte Finishing-Zeit hat.
Dann wissen wir, dass die Finishing-Zeit von dem Knoten U,
die ist kleiner gleich der Finishing-Zeit der ganzen Komponente und die
ist aber kleiner als der Finishing-Zeit von C-Strich und das
ist die Finishing-Zeit irgendwie eines Startknotens Ess hier.
Weil dieser Knoten hier noch nicht entdeckt wurde,
W war ja jetzt der erste Knoten,
den wir gesehen haben auf diesem Pfad
Ja, gilt, dass der Knoten weiß ist.
Wenn der Knoten weiß ist und wenn die Finishing-Zeit größer ist,
dann wissen wir, dass wir diesen Knoten zuerst gewählt hätten.
Den hätten wir zuerst betrachtet. Und eben nicht uh,
weil wir die ja in einer geordneten Reihenfolge machen. Das heißt,
zum Zeitpunkt X, wenn wir also entscheiden,
welchen Knoten wir als nächstes betrachten,
wird der Knoten S gewählt und nicht U und das ist eben ein Widerspruch zu der Situation,
das O hier betrachtet wird und damit kann der Knotenweg gar nicht existieren.
Ja, das ist der formale Beweis, dass der Algorithmus korrekt ist und wie gesagt,
meistens ist es einfacher, sich das bildlich vorzustellen.
Der Trick ist einfach, die Komponenten Intendenten selber bilden ein Deck,
haben keine Zyklen. Und wenn wir nur dafür sorgen,
dass wir in der tiefen Suche die Komponenten von hinten nach vorne,
also von der Senke zur Quelle erkennen, dann wissen wir,
dass immer dann, wenn wir eine Kante verlassen,
wenn wir eine Komponente verlassen, wir auf eine andere Komponente treffen,
die wir vorher schon bearbeitet haben.
Das ist der ganze Trick. Ja, hier nochmal unser Beispiel.
Jetzt vollständig. Hier oben sind die Discovery- und Finishing-Zeiten.
In dem nächsten Schritt drehen wir alle Kanten um.
Das ist der transponierte Graphen,
also die Kante von C nach D,
die läuft jetzt von D nach
Und so weiter. Und jetzt bearbeiten wir die Knoten mit fallenden Finishing-Zeiten.
Wir sehen, hier haben wir die Finishing-Zeit 16,
das ist die Größe, mit dem Knoten B würden wir es beginnen.
Und jetzt sieht man nochmal sehr schön, dass alle kannten,
die in die zu B gehörende Komponente hineinlaufen oder sie verlassen,
in die Komponente hineinlaufen.
Es gibt also keine Kante, die diese Komponente entsprechend verlässt.
Okay, so, und dann bearbeiten wir die Komponente.
Wir haben hier Pfade zwischen allen Knoten, das wissen wir,
weil es ist ja eine starke Zusammenhangskomponente.
Wir werden dann also den B, die Knoten A,
den Knoten E sehen. Die werden alle bearbeitet und dann
sind wir wieder in der Hauptroutine und haben die erste starke Zusammenhangskomponente entdeckt.
Der Knoten mit der zweitgrößten Finishing-Zeit ist dann der Knoten C,
der hat hier eine Zehn. Und jetzt sehen wir,
bei C gibt es bei Cs-Komponente, gibt es durchaus beide Fälle.
Es gibt durchaus Kanten, die in die Komponente C hinein zeigen.
Es gibt auch Kanten, die aus der Komponente hier hinaus zeigen.
Aber die, die hinaus zeigen, haben die Eigenschaft,
dass sie in Komponenten zeigen, die wir schon bearbeitet haben,
nämlich in die Komponente eins hier.
Das ist diese Kante
Okay, das heißt, wir bearbeiten die Komponente,
dann sehen wir C, dann sehen wir D,
haben die beiden hier markiert.
Der Algorithmus der DFS Visit-Algorithmus terminierte an dieser Stelle wieder.
Wir sind wieder in der Hauptroutine. Die Nächstgröße Finishing-Zeit hat der Knoten G,
hier mit der 7. Wir sitzen also auf dem.
Auch hier ist es wieder so, wir haben Kanten,
die die Komponente verlassen. Zum Beispiel die und die.
Wir haben auch Bekannten, die in die Komponente hineinzeigen,
zum Beispiel diese. Wenn wir Kanten haben,
die die Komponente verlassen, zeigen die immer in Komponenten hinein,
die wir vollständig bearbeitet haben.
Dementsprechend sind das hier
alles Kreuzkanten
Und wir würden also G und F erkennen und als eine Komponente 3
und dann würden wir hier die letzte Komponente erkennen und hätten dann diesen Komponenten Grafen hier.
Ja, jetzt sind die Kanten wieder umgedreht. Also hier waren sie ja transformiert,
die Kanten in dem BLC beziehe sich wieder auf den ursprünglichen Grafen und wir haben also dann die Komponente ABE,
die Komponente CD, die Komponente FG und die Komponente H. Hier sieht man auch nochmal sehr schön,
dass es ein Deck ist und wir sehen auch,
dass die Komponentennummern eine tokologische Sortierung sind.
Ja, also das ist die Komponente 1, das ist eine Quelle,
Komponente 2 hat nur eintretende Kanten von Komponente 1 und Austretende 3 und 4,
Komponente 3 hier,
hat Eintretende von 1 und 2 und eine Austretende nach 4 und 4,
hat nur eintretende Kanten, und zwar von Komponente 2 und 3. Ja,
das ist ein im Prinzip sehr eleganter Algorithmus,
um starken Zusammenhang zu berechnen.
Und jetzt können wir uns überlegen, okay,
das hat schon Linearzeit, was will man mehr?
Wir könnten überlegen, gibt es da überhaupt irgendetwas dran zu verbessern?
Und da kann man zwei Dinge ausmachen,
die je nach Anwendungskontext entweder problematisch sind oder nicht problematisch.
Die Nachteile des Algorithmus sind Zum einen,
das ist mehr so was Kosmetisches, dass diese DFS-Algorithmus zweimal ausgeführt wird,
das könnte sich in den Konstanten des Algorithmus natürlich auswirken,
müssen das alles doppelt machen, diese ganzen, die Tiefensuche.
Der schwerwiegende Nachteil des Algorithmus ist, dass der Eingabegraf in
der Regel statisch ist und die Kanteninvertierung häufig nicht durchgeführt werden kann,
weil sie die Datenstruktur, auf der sie arbeiten, nicht manipulieren können.
Der Graf ist einmal so gegeben und sie können dann nicht einfach sagen,
jetzt den kopiere ich mir den mal reale Kanten um.
Das geht bei kleinen Grafen natürlich immer,
das ist auch kein Argument mit bezüglich der asymptotischen Laufzeit,
weil einen Grafen zu kopieren, dauert ja auch nur lineare Zeit,
aber es ist ein praktisches Argument.
Es ist vielleicht auch ein Speicherplatzargument, dass sie den Grafen nicht mal eben kopieren können.
Und ich habe ihn in der letzten Vorlesung schon Beispiele gezeigt. Gerade Beispiele,
die Grafanalysen bezüglich Internetdaten beinhalten haben, so ein Problem,
wo sie einfach zum Beispiel links im Internet als Kanten in einem Graf beziehen,
bezeichnen,
dann können sie nicht ohne weiteres die Kanten in Bettierung machen
Deswegen gibt es einen anderen Algorithmus und streng genommen ist er
sogar älter als den, den wir gerade gesehen haben,
den wir gerade gesehen haben, der ist nur leichter zu verstehen.
Und der Algorithmus ist von Robert Tajan 72 entwickelt und er war der Erste,
der damals gezeigt hat, dass man den starken Zusammenhang überhaupt in Linearzeit berechnen konnte.
Das war zu dem Zeitpunkt unbekannt. Es kannte keiner einen Algorithmus,
um das zu tun. Also insbesondere der Algorithmus, den ich Ihnen gerade gezeigt habe, war zu der Zeit nicht bekannt. So,
Das hat der Robert Tajan sich überlegt,
also er hat sich gesagt, naja, okay,
was zeichnet diese starken Zusammenhangskomponenten aus und die Knoten darauf,
naja, die Knoten darauf liegen irgendwie auf einem Zyklus, ja,
weil ich von zwei Knoten, UNV die Eigenschaft habe,
es gibt ein Pfad von UNAV und von V nach U,
also liegen die irgendwie auf einem Zyklus,
der muss nicht unbedingt einfach sein,
der kann vielleicht auch Knoten mehrmals enthalten,
aber auf alle Fälle liegen sie auf einem Zyklus.
Dann hat er sich überlegt, dass so eine starke Zusammenhangskomponente
immer ein Zusammenhänger Teilbaum ist in GP.
Das hat er auch schon erkannt. Ja,
das ist ja die Eigenschaft, die wir gerade so ausgenutzt haben,
dass die starken Zusammenhangskomponenten immer quasi in einem Rutsch bei Aufruf von DFS Visit erkannt werden.
Wenn das so ist, dann gibt es in so
einer Komponente irgendwie auch immer so eine Wurzel.
Die haben wir auch gerade in dem Beweis und in dem Algorithmus schon gesehen,
den ersten Knoten dieser Komponente, den wir besuchen,
den gibt es.
Und dann hat er sich überlegt, dann müsste es eigentlich
einen Weg geben,
diese Wurzel zu bestimmen
Wenn man jetzt noch mal schaut auf die B-Zusammenhangskomponenten in den
ungerichteten Grafen, da gab es ja die Gelenkknoten.
Das waren die Knoten, wo die B-Zusammenhangskomponenten begann.
So, hier ist jetzt die Situation analog. Wir suchen jetzt
nicht Gelenknoten, sondern wir suchen diese Wurzeln der Teilbäume,
die jeweils eine starke Zusammenhangskomponente beschreiben. Okay, also,
wie Zusammenhangskomponenten sind definiert über einfache Zyklen in den ungerichteten Grafen,
die starken Zusammenhangskomponenten über Zyklen in gerichteten Grafen?
Also da gibt es
eine Analogie
Und sowohl die B-Zusammenhangskomponenten als auch die starken Zusammenhangskomponenten bilden
immer bezüglich des DFS-Baums, GP, einen zusammenhängenden Teilbereich.
Unser Ziel ist es jetzt also, diese Wurzel zu identifizieren.
Also zu sagen, wir wollen in den DFS-Algorithmus eine Lampe einbauen,
die angeht, immer dann, wenn wir eine Wurzel sehen.
Die müssen wir identifizieren. Genauso wie wir beim BFS-Algorithmus
eine Lampe hatten, die immer angegangen ist,
wenn der Gelenkknoten,
wenn Gelenkknoten aufgetaucht ist
So, das Erste, was man mal zeigen muss,
ist, dass die Aussage, die wir hier sehen,
über den Zusammenhang tatsächlich auch stimmt,
also dass die starken Zusammenhangskomponenten wirklich auch immer zusammenhängt sind.
Und das sagt dieses Celemma, das ist ein relativ einfacher Beweis,
also wenn Z eine starke Zusammenhangskomponente in einem Graf G ist,
dann ist der durch Z-induzierte Subgraf-GP auf alle Fälle zusammenhängt.
Also wir haben jetzt in unseren Grafen eine große Komponente,
ja, das ist die Komponente C und irgendwie sind in dieser Komponente natürlich Knoten drin und diese Knoten,
die liegen Ja, die bilden quasi einen, diesen Baum,
das ist unser Baum hier, das ist unser GEP.
Das ist also der Tiefensuchbaum. Ja,
hier ist ausgehend unsere Wurzel, es sind gerichtete Kanten,
die laufen also hier immer nach unten.
Ja, so sieht es ungefähr aus. Und die Aussage ist,
dieser Baum, den wir hier in dieser Komponente haben,
der ist zusammenhängend. Ja, es ist nicht die Situation,
dass wir irgendwie hier aus dieser Komponente herauswandern und nachher hier wieder hereinwandern.
Das ist verboten, das Lied wird es
nicht geben
Okay, wie kann man das sehen, dass das tatsächlich so ist?
Also es ist jetzt egal, wie wir anfangen,
von welchem Knoten wir anfangen, dieser Baum ist immer zusammenhängend.
Naja, wir sehen das wieder an unseren weißen Faden.
Das heißt, wir betrachten den Knoten Erg, der Knoten Er ist
der erste Knoten hier oben, wo wir die Komponente erstmals besuchen.
Und weil es der erste ist, wissen wir,
dass alle Knoten weiß sind. Ja, hier,
die haben wir alle noch nicht gesehen aus der Komponente,
weil es immer Pfade gibt, ist ja eine starke Zusammenhangskomponente,
gilt das Theorien der weißen Pfade.
Das heißt, diese Knoten sind alle nachfahren
Jetzt denkt man im ersten Augenblick, klar,
das ist man schon fertig, weil wenn die alle nachfahren sind,
dann sind die alle hier in dem Baum.
Das stimmt aber jetzt nicht so ganz,
weil es könnte natürlich durchaus sein,
dass wir so eine Komponente verlassen und dann wieder eintreten,
dann wäre so ein Knoten ja auch ein Nachfahre von dem Knoten wäre hier.
Und das müssen wir noch zeigen, dass das nicht sein kann.
Das kann man auch ganz schnell sehen. Denn mal angenommen,
es gibt einen Knoten Y, der gehört nicht zu der Komponente,
das ist jetzt mal der hier unten.
Die gehört nicht dazu. Der ist Nachfahre von er,
der ist Nachfahre von er.
Wir laufen hier so runter und dann kommen wir mal
irgendwann zu
Ja, und es gibt jetzt einen anderen Knoten X,
der gehört wieder zu der Komponente.
Und der ist ein Nachfahrer von Y. Das mal hier weg.
Also wir gehen jetzt davon aus, dass es diese Kante hier gibt.
Und das wäre unser Knoten X. Der liegt wieder drin.
Also wir verlassen die Komponente an den Knoten Y
und gehen dann irgendwie wieder da rein.
Und das müssen wir ausschließen, dass das sein kann.
Und das kann man aber ganz schnell sehen, woran kann man das sehen?
Also es gibt ja einen Pfad von R nach Y,
nämlich über Baumkanten hier lang.
Und es gibt einen Pfad von Y nach X
über Baumkanten,
also hier wieder rein
Und dann gibt es einen Pfad von X nach R. Wir
können von X wieder zurück nach R. Warum gibt es diesen Pfad?
Den gibt es deswegen, weil X und er sind ja in der
gleichen starken Zusammenhangskomponente. Und wenn wir wieder zurückkommen nach R,
dann kommen wir von R auch wieder nach X und dann,
Entschuldigung, von R wieder nach Y, können wir wieder hier hin.
Und dann liegen R und Y auf einem Zyklus.
Und damit können wir von R Y erreichen und von Y
auch wieder
Damit sind Y und er in einer starken Zusammenhangskomponente.
Und deswegen ist das ein Widerspruch, das kann gar nicht auftreten.
Okay, daraus resultiert aus diesem Widerspruch tatsächlich,
dass diese Teilbäume immer zusammenhängend sind.
Egal wie wir die Tiefensuche organisieren. Es gibt immer einen
ersten Knoten, den wir betrachten, einer starken Zusammenhangskomponente.
Und dann werden wir in dem tiefen Suchbaum so einen zusammenhängenden
Bereich haben. Das ist die Grundvoraussetzung dafür,
dass man mit einem DFS-Durchlauf überhaupt eine Chance hat,
den starken Zusammenhang zu berechnen
Okay, jetzt wollten wir irgendwas tun, um hier diese Wurzeln zu finden.
Die kriegen jetzt einen Namen. Wenn wir also eine starke Zusammenhangskomponente Z haben,
das steht hier hier, ich habe die ECC,
das ist jetzt Z. Ja, so,
und wir haben unseren tiefen Suchwald, dann wissen wir,
es gibt jetzt also so eine Wurzel,
das ist der Knoten mit minimaler Discovery-Side.
Und der heißt ab jetzt, das ist das,
das ist RP von dieser Komponente Z. Ja,
das ist dieser Knoten hier,
wo der DFS-Baum beginnt.
Ja,
der kriegt jetzt also
einen Namen
Müssen sich jetzt merken, der kommt gleich noch häufiger vor.
Das ist also die Wurzel von Z in dem DFS-Baum,
ja, in GP. Und den wollen wir jetzt finden,
ja, also wir wollen, wie gesagt, dem DFS-Algorithmus,
das die Lampe angeht, wenn wir die so einen Knoten sehen,
ja, weil dann wissen wir, aha, die Knoten,
die hier drunter sind, also nicht alle,
die da drunter sind, ihr könnt natürlich noch andere Komponenten drunter kommen,
aber zumindest haben wir hier einen Startknoten für eine starke Zusammenhangskomponent.
So, und wenn Sie sich jetzt noch ein bisschen erinnern an die B-Zusammenhangskomponenten,
dann kriegen Sie jetzt wahrscheinlich das ein oder andere Déjà-vu-Erlebnis,
weil tatsächlich diese beiden Algorithmen sehr eng
miteinander verwandt sind
Bei den B-Zusammenhangskomponenten gab es so eine Struktur-Lämmer, an dem man gesehen hat,
wie die Rückhanden einen Einfluss haben auf diese Gelenknoten.
Da wussten wir nämlich immer, dass die Rückhanden nicht
weiter nach oben gehen dürfen, wie der Knoten selbst.
Und dann wussten wir, das ist ein Gelenknoten. Und hier hat man eine ähnliche Situation, die ist nur komplexer, da wir sowohl Rückhanden als auch Kreuzkanten haben. Also das ist das Entscheidende Lämmer,
an dem man sehen kann,
wo diese Wurzeln liegen in dem DFS-Wald, wenn also GP,
so ein DFS-Tiefensuchwald ist, Covery- und Finishing-Zeiten, D&F,
dann können wir einen Knoten als Wurzel identifizieren und dann können wir sagen,
ein Knoten V ist Wurzel einer starken Zusammenhangskomponente, genau dann,
wenn zum Zeitpunkt F&V, also wenn der Knoten abgearbeitet ist,
gilt, und zwar für alle Nachfahren,
U von V, es existiert keine Rückkante,
UW und W ist ein Vorfahrer von V und es existiert keine Kreuzkante,
UY mit W ist Ach so, sodass W,
das ist die Wurzel, sagen wir mal, also zweiter Teil,
es existiert keine Calls-Kante, UY mit der Eigenschaft,
dass die Wurzel der Komponente zu der Y gehört,
die nennen wir W,
ein Vorfahrer von Vs.
Das sind die zwei Bedingungen, die gelten müssen dafür,
dass wir so eine Wurzel identifiziert haben.
So, die müssen wir uns beide wieder anschauen.
Und wie immer fangen wir mit dem einfacheren Teil an.
Das ist ja eine genau dann, wenn-Beziehung hier, das heißt,
wir müssen zeigen, dass Wenn es eine Wurzel ist,
dann gelten diese beiden Bedingungen.
Und wir müssen zeigen, dass wenn die beiden Bedingungen gelten,
dann ist es auch eine Wurzel.
So, wir fangen mit der einen Richtung an.
Das heißt, wir sagen, wir haben mal eine Wurzel.
Und in diesem Bildchen hier ist die Wurzel,
die wir betrachten, V. Ja, dieser Knoten hier.
Ja, sieht jetzt gar nicht so aus,
weil hier oben ist so viel anderes gemalt.
Eine V ist also der Knoten, den wir betrachten,
von denen wir herausfinden wollen, ob es eine Wurzel ist oder nicht.
Und V hat jetzt einen direkten Vorgänger, das ist V-Strich.
Es ist der Knoten auf dem DFS-Baum,
der genau da drüber liegt und dem wir genau VV entdeckt haben
So, wir müssen jetzt zeigen, dass der Knoten V
keine Wurzel sein kann, wenn eine dieser beiden Kantenexes jetzt.
Also angenommen, es gibt so eine Rückkante, UW,
das ist Fall 1, das ist die Kante hier,
die rote Kante, die hier oben nach W zurückführt.
Also angenommen, es gibt so eine Kante UW,
dann gilt, es gibt einen Pfad von V nach U,
von V hier runter nach U, über Baumkanten,
dann gibt es die Rückkante von U nach W,
die führt uns hier oben hin zu einem Vorgänger von V,
ja,
W ist Vorfahrer von V,
war die Bedingung
Dann können wir wieder Baumkanten langlaufen von W nach V-Strich und von V-Strich nach V.
Also KNV nicht die Wurzel einer starken Zusammenhangskomponente sein,
denn auch wie V-Strich gehört schon zur gleichen starken Zusammenhangskomponente und den
haben wir schon vor V entdeckt.
Kann also nicht sein. Das heißt, so eine Rückkante kann nicht existieren.
Und für die Kreuzkante können wir was Ähnliches zeigen. Mal angenommen,
wir haben so eine Kreuzkante, die läuft von U nach Y,
also von V wieder im Baum nach unten,
zu einem U. Dann hier ist diese blaue Kante zu Y.
Und Y liegt jetzt in einer anderen starken Zusammenhangskomponente mit
einer Wurzel
Wenn das ein starker Zusammenhangskomponente ist, dann gibt es einen
Pfad von Y nach W. Das ist ja ein starker Zusammenhang.
Jeder mit jedem ist durch Pfade verbunden. Und von we,
weil das ein Vorgänger ist von Pfarro,
können wir dann zu V-Strich laufen und dann zu V.
Und damit ist auch klar, dass V in diesem Fall
keine Wurzel sein kann. Kann nicht sein.
Und dementsprechend ist klar, dass Wurzeln diese Eigenschaften haben.
Aber reicht das auch? Reicht es zu prüfen,
dass diese beiden Bedingungen gelten? Und dann wissen wir,
dass wir eine Wurzel haben
So, das ist wieder ein bisschen schwieriger zu zeigen.
Wir müssen sagen, es gibt kein Szenario, wo wir einen Knoten-V-Strich haben.
Der Vorgänger ist, wenn diese beiden Bedingungen gelten, ja.
Okay, also erster Fall. Erster Fall sind die Rückhanden,
also wir sagen mal,
V-Strich ist jetzt also ein Elternknoten von V,
so wie eben gerade auch.
Und wir gehen mal davon aus,
V sei nicht
die Wurzel
Ja, also die Komponente geht weiter nach oben.
Dann gibt es einen Pfad von V nach V-Strich.
Dann können wir irgendwie zu V-Strich zurückkommen. Wir wissen,
wir können vom V-Strich nach V kommen über die Baumkante.
Aber wir kommen auch von V zu V-Strich, wenn es nicht
die Wurzel ist. So, der Pfad von fauner Vorstrich
, der muss eine Rück- oder Kreuzkante enthalten. Ja,
wir laufen von V und über Baumkanten kommen wir nur im Baum weiter nach unten,
über Vorwärtskanten kommen wir auch nur weiter nach unten.
Das heißt, an irgendeiner Stelle müssen wir eine Rückkante oder eine Kreuzkante haben.
Und wir betrachten jetzt also diesen Pfad und wir betrachten wie immer
Die erste Rück- oder Kreuzkante, die wir sehen auf dem Pfad.
So, erster Fall ist, wir haben eine Rückkante.
Wenn wir eine Rückkante haben, ist die Situation wieder ganz einfach. Wenn wir eine Rückkante haben,
dann ist weh, also die geht von U nach weh,
dann ist weh ein Vorfahrer von U,
ist eine Rückkante, aber kein Nachfahrer von V,
weil wir haben ja gerade den Knoten-V so gewählt,
dass es der erste war auf diesem Pfad,
der entsprechend dem Pfad die verlässt.
Und damit ist weh ein Vorfahrer von V und das
kann nicht sein
Doch, das kann sein. Und deswegen ist UW eine Rückkante,
gemässfalle eins des Lämmers. Also wir haben gesagt, V
ist keine Wurzel. Und deswegen haben wir eine Rückkante identifiziert,
die genau diese Eigenschaft hat, die wir hier in dem Lämmer angegeben haben.
Wir haben eine Rückkante gefunden, UW,
die geht zu einem Vorfahren von V. Okay,
und jetzt kommen die Kreuzkanten, die sind ein bisschen schwieriger,
da müssen wir nochmal zwei Unterfälle unterscheiden.
Jetzt haben wir also diese Rückkante,
da gibt es jetzt auch wieder ein Bildchen,
also wir gehen von V-Strich nach V und dann hier runter
nach U und von U haben wir jetzt eine Kreuzkante zu
einem Knoten
Bei Kreuzkanten gilt generell, dass die Discovery-Seite,
also wenn die von A nach B geht,
dann ist die Discovery-Zeit von A immer größer als die von
B. Sonst wären das keine Kreuzkanten.
Das war dann die Bedingung, die wir mal definiert hatten,
um sie überhaupt Kreuzkante zu nennen.
In unserem Fall bedeutet das die Discovery-Zeit von U ist größer
als die Discovery-Zeit von Y. Also Y gehört irgendwie zu einer Komponente,
die wir schon gesehen haben.
Sonst wäre das hier keine Kreuzkante. So, wir betrachten mal
die Wurzel von dieser Komponente zu der Y gehört.
Das ist die gelb-markierte und die Wurzel ist ja eher
Und die, die kann nicht Vorfahrer von V sein,
weil wenn die das wäre,
dann hätten wir ja schon wieder den Pfad gefunden.
Und wir müssen jetzt also zwei Fälle unterscheiden. Also die
Wurzel ist Nachfahre von V-Strich. Irgendwo hier unter V-Strich hängt
dann diese Wurzel. Was gilt dann? Dann gilt,
wir haben einen Pfad von V-Strich nach Y,
weil wir von V-Strich über V hier runter und dann
nach Y gehen können.
Und wir haben einen Pfad von Y
nach V-Strich
Das haben wir entsprechend vorausgesetzt und damit ist eher nicht
die Wurzel der Zusammenhangskomponente und das wäre ein Widerspruch.
Okay, und der zweite Fall ist hier dargestellt,
wir haben also die entsprechende Wurzel und gehen davon aus,
sie ist nichts Nachfahre von V-Strich,
die haben also gar nichts miteinander zu tun, die beiden hier.
Den ganz getrennten Teilbäumen, ja, dann wissen wir auch,
dass die Zeitintervalle zwischen Discovery und Finishing Zeit für diese Wurzel hier,
das ist die, die diese Komponente beschreibt,
die sind absolut das Jugend,
die haben nichts miteinander zu tun mit der Discovery und Finishing Zeit,
hier für den Knoten-V-Strich.
Das folgt aus dem Klammerungstheorie. Und wir wollen jetzt aber
einen Pfad haben von Y noch V-Strich. Und das kann gar nicht sein,
denn wir haben zwar eine Kreuzkante, die uns zu Y führt,
aber jetzt bräuchten wir noch eine zweite Kreuzkante,
die uns wieder in diese Richtung führt.
Und das kann nicht sein. Weil Kreuzkanten ja immer
nur von einem Knoten mit großer Discovery-Zeit zu einem Knoten,
mit kleiner Discovery-Zeit führt und diese Komponenten haben wir zuerst entdeckt.
Deswegen hat die kleinere Discovery-Zeiten, kann also nicht sein.
Und damit wissen wir, dass die Discovery-Zeit von dem Knoten,
von der Komponente Y, die ist ja kleiner als die von Knoten V., sprich.
Und das führt uns wieder zu dem Widerspruch. Okay,
also unsere Annahme war falsch. Und damit ist die Wurzel tatsächlich
ein Vorfahrer von V. Und das ist jetzt genau so eine Rückkante,
wie wir sie im Lemma definiert haben. Das heißt,
wir haben gezeigt, dass wenn so eine Rückkante existiert oder
wenn so eine Kreuzkante existiert, dann wissen wir,
dass der Knoten V nicht Wurzel einer starken Zusammenhangskomponente ist.
Anders gesprochen, wenn es keine Rückkante existiert und keine Kreuzkante
diese Art,
dann ist es
eine Wurzel
Das ist das Entscheidende, was hier herausgefunden wurde.
Und mit diesem Kriterium können wir jetzt wieder eine Eigenschaft berechnen in einem Grafen mit der Tiefensucher,
um zu zeigen, dass wir eine Wurzel haben.
Es ist wieder ein Low-Point, genau wie bei dem BFS-Algorithmus,
der Low-Point, Q von V, also Q heißt er in diesem Fall.
Und hier steht wieder, über was wir minimieren müssen,
um den Low-Point zu berechnen.
Wir haben also einen Tiefensuchwald mit Discovery und Finishing-Zeiten D&F.
Und wir betrachten einen Knoten V und mit NP,
von V die Menge der Nachfolger in dem Baum. Und dann müssen,
dann können wir den Low-Point wie Folge definieren,
dass das Minimum über die Discovery-Zeit des Knotens vereinigt mit die Menge aller Nachfahren,
dieses Knotens V und dann nehmen wir D von W und wir haben eine Kante von U nach W,
dann das ist eine Rückkante und V ist ein Nachfahre von W.
Das ist genau die Rückkante,
wie wir sie in Fall 1 unserer Liste selber mal definiert
haben und dann brauchen wir noch die Kreuzkanten,
die sehen wir hier,
also U ist wieder der Nachfahre von V. Wir nehmen die Discovery-Zeit von Y,
UY ist eine Kreuzkante und diese Kreuzkante hat er ja noch eine Eigenschaft.
Und zwar muss V ein Nachfahre sein von der entsprechenden Wurzel
der Komponente, in der Y enthalten ist.
Das ist quasi nur eine Umformulierung von dieser Bedingung.
In die Normenklatur so, dass wir entsprechend im DFS-Algorithmus diesen
Low-Point berechnen können. So, jetzt muss man noch zeigen,
dass tatsächlich diese Low-Points angeben, wo die Wurzeln sind.
Das heißt, GPs, also es sind Tiefensuchwald,
Discovery und Finishing-Zeiten haben wir V, ist die Wurzel einer starken Zusammenhangskomponente.
Und zwar genau dann, wenn die Finishing-Zeit,
wenn zum Zeitpunkt der Finishing-Zeit gilt, dass die Discovery-Zeit gleich dem Low-Point entspricht.
Also wir zeigen, dass nachdem wir die ganze Komponente betrachtet haben,
dass es keinen Weg gab, um von dem Knoten weiter unten zu einem Vorgänger zu kommen.
Ja, warum ist das so? Wir wissen,
dass Q von V zum Zeitpunkt des Finishings abgeschlossen ist.
Wir minimieren nur über die Nachfolger. Ja, ist also
dann beendet. Und Q von V beschreibt eben die Discovery Avery
Zeit des ältesten Vorgängers, den wir von V aus erreichen können.
Und wenn da keiner ist,
der jünger ist als der Knoten V selber, dann wissen wir,
dass V eine Wurzel ist und da eine starke Zusammenhangskomponente beginnt.
So, jetzt denkt man, wer eigentlich fertig,
man ist eigentlich auch fast fertig.
Allerdings hat man noch ein kleines Problem. Wir müssen natürlich
jetzt noch überlegen, wie können wir das hier berechnen?
Die Rückkarten, das ist relativ einfach.
Aber hier ist noch so eine komplizierte Bedingung drin,
die gelten muss für diese Kreuzkanten.
Wir dürfen nämlich nur über Kreuzkanten minimieren,
die diese Eigenschaft haben
Jetzt gucken wir nochmal hier, ne? Das ist nämlich die Wurzel dieser Komponente,
wo die Kreuzkante hinführt, ein Vorfahre von V ist.
Also nur diese Kreuzkanten interessieren uns, die wir hier sehen.
Und die anderen dürfen, über die anderen dürfen wir nicht minimieren.
Ja, wie kann man das herausfinden, dass diese Kreuzkante so aussieht?
Also wir laufen ja dann nicht die Kreuzkante lang und gucken mal nach. Diese Kreuzkante hier,
die hat eine spezielle Eigenschaft, die Eigenschaft ist nämlich,
dass dieser Knoten, dass die Wurzel noch nicht,
wenn wir gerade mal nachgucken,
Die ist genau dann der Fall,
wenn der Knoten V zur Betrachtung der Kante UY noch auf dem Stack liegt.
Also wir können über die Eigenschaft,
dass etwas noch auf dem Stack liegt, das heißt,
der Knoten noch grau ist, entscheiden, dass V so ein Nachfolger ist.
Gemäß dieser Definition. Okay, das heißt,
der Algorithmus sieht wie folgt aus. Wir haben Discovery Finishing-Zeiten,
wir haben die Low-Points, wir haben die starken Zusammenhangskomponenten,
die wir zuordnen wollen. Wir haben einen Stack,
den Stack werden wir verwenden, um uns zu merken,
welche Knoten noch nicht bearbeitet wurden und noch keine starken Zusammenhangskomponentennummern haben.
Und wir haben eine Variable, die heißt On-Stack.
Damit können wir schnell prüfen, ob ein Knoten noch auf
dem Stapel liegt oder nicht.
Der DFS beginnt, Knoten kriegen alle die Nummer null,
On-Stack wird auf Falls gesetzt. Wir initialisieren unseren Stack,
der ist am Anfang leer. Die aktuelle SCC-Nummer ist null.
Unsere Uhr wird auf null gestellt. Wir gehen hier durch
die Knotenmenge durch, wenn die Discoveryzeit null ist,
haben wir den Knoten noch nicht gesehen
DFS Visit auf für den Knoten U mit dieser aktuellen SCC-Nummer,
die werden wir dann im Laufe der Zeit aktualisieren.
Okay, so, die Uhr wird hochgedreht,
die Discovery-Zeit wird festgelegt, der Low-Point wird initialisiert mit der Discovery-Zeit.
Wir legen den Knoten U auf den Stack drauf,
dieser Stack verwaltet alle Knoten, die noch keiner Zusammenhangskomponente zugeordnet wird.
Die On-Stack-Variable wird für U auf True gesetzt,
das heißt, wir wissen, wir merken uns,
dass U noch auf dem Stack liegt ab jetzt.
Jetzt sondieren wir die Adlizenzliste,
laufen hier durch
Und betrachten die Knoten der Reihe nach,
wenn der Zielknoten V aus der Attitizenz des Null ist,
die Discovery-Zeit davon, dann wissen wir,
Knoten haben wir noch nicht gesehen.
Baumkante, wir rufen DFS Visit auf.
Wenn wir von diesem DFS Visit-Aufruf zurückkommen,
haben wir den Low-Point des Kindknotens berechnet und über den müssen wir minimieren.
Das machen wir hier. Es ist Minimum über alle Kinder
. Anderenfalls, jetzt haben wir keine Baumkante,
also der Els-Teil sind jetzt vorwärts
rückwärts Kreuzkanten
Wir interessieren uns nur für alles Weitere,
wenn die Discovery-Zeit des Knotens V hier kleiner ist als der
Low-Point von U, weil wenn er das nicht ist,
dann würde ja ein Minimieren nichts bringen.
So, dann gucken wir nach,
wenn F&V gleich null ist, die Finishing-Zeit ist null,
das heißt, der Knoten ist grau, dann haben wir eine Rückkante.
Oder Finishing-Zeit ist größer null und wir haben die On-Stack-Variable,
ist True, dann wissen wir, wir haben eine Kreuzkante,
gemäß unserer Definition.
Dann minimieren wir, wir wissen schon,
die Discovery-Zeit von V ist kleiner als Low,
das heißt Low von U
setzen wir dann auf diese Discovery-Zeit
Okay, das ist schon alles um den Low-Point zu berechnen,
dieser Teil. Dann haben wir alle Knoten besucht.
Wir können dann gucken am Ende ist der Low-Point von U,
den haben wir jetzt vollständig berechnet.
Gleich der Discovery-Zeit von U, dann haben wir eine Wurzel gefunden.
Jetzt müssen wir noch die Knoten ausgeben. Wir erhöhen also
die Nummer für die aktuelle, starke Zusammenhangskomponente.
Jetzt bemühen wir unseren Stack. Wir holen also Knoten vom Stack herunter,
setzen die On-Stack-Variable auf Falls, um zu markieren,
dass da nichts mehr auf dem Stack,
also der Knoten nicht mehr auf dem Stack liegt.
Dann setzen wir die SCC-Nummer von dem Knoten V
auf diese Nummer Carency
Und das machen wir so lange,
bis wir an dem Knoten U angekommen sind,
an der im aktuellen Knoten, welcher die Wurzel entspricht.
Okay, und ganz am Ende können wir noch die Finishing-Zeit setzen.
Und dann sind wir fertig. Das ist ein ziemlich,
also es ist ein leicht aufzuschreibender Algorithmus,
der aber in sich sehr komplex ist, ja.
Also ich finde den persönlich sehr schwierig auch zu verstehen,
dass das wirklich funktioniert. Und dazu muss man eben
diesen Beweis Schritt für Schritt nachvollziehen, um zu sehen,
dass genau diese Kreuz- und Rückhande existieren muss.
Und im zweiten Schritt muss man sehen, dass dieser Low-Point
genau das berechnet, das Vorhandensein dieser Kreuz- und Rückhandlung.
Wenn man das eingesehen hat, wenn man das verstanden hat,
dann ist auch klar, dass der Algorithmus hier funktioniert.
Das ist also ein sehr schöner Algorithmus, der eben auch starken Zusammenhang berechnen kann.
So, hier nochmal unser Beispiel vom Anfang, sehen hier wieder Discovery und Finishing-Zeiten.
In Rot sehen Sie jetzt die Baumkanten. Wenn der Algorithmus durchläuft,
dann hätte er am Ende Diese Low-Points, ja,
die sind in diesen grauen Kästen angegeben, das sind die Low-Points dieser Knoten.
Es ist ganz hilfreich, das mal einmal selber mal zu machen,
um zu sehen, wie die entsprechend berechnet werden.
Und sie sehen jetzt genau die grünen Wurzeln, haben die Eigenschaft,
das Low-Point und Finishing-Zeit, hier ist ein Fehler.
Das ist, nee, ist kein Fehler. Übereinstimmen,
das Low-Point und Discovery-Zeit übereinstimmen, also eins hier, die zwei.
Und das sind genau die Wurzeln. Das sind die Wurzeln,
das sind die grünen Knoten hier,
wo dann jeweils die starken Zusammenhangskomponenten entdeckt werden
Ja, okay. So, das war's zum Thema
starken Zusammenhang. Wir haben jetzt zwei Algorithmen gesehen.
Jetzt kann man noch mal drüber nachdenken. Wann benutzen wir
jetzt den ein und wann benutzen wir jetzt den anderen?
Also beide sind ja offensichtlich in dem Mehrzeitalgorithmen.
Der Algorithmus, den wir gerade gesehen haben, hat,
wie gesagt, einen entscheidenden Vorteil.
Sie brauchen die Kanten, nicht umzudrehen. Sie können einmal
auf dem Grafen arbeiten, der kann statisch bleiben,
sie müssen den nicht verändern und manipulieren.
Das ist ein Riesenvorteil
Aber auch, und sie brauchen nur einen DFS-Durchlauf.
Es ist aber, wie gesagt, eher ein kosmetischer Vorteil.
Aber wenn man dann genau entscheidet, hat auch dieser Algorithmus einen Nachteil.
Und der Nachteil dieses Algorithmus ist, der Stack, den wir brauchen.
Wir brauchen eine zusätzliche Datenstruktur, wo wir die Knoten speichern.
Das heißt, der Algorithmus hier verbraucht doch signifikant mehr Speicher
als der andere, den wir gerade gesehen haben.
Wenn Sie in Grafa mit Millionen von Knoten und wenn die große Zusammenhangskomponenten haben,
dann wird dieser Stack eben entsprechend auch groß.
Das heißt, der hat einen höheren Speicherbedarf.
Das ist das, was man bezahlt,
mehr oder weniger dafür
Ja, gut. Meines Wissens gibt es keinen Algorithmus,
der mit einem DFS-Durchlauf und ohne zusätzlichen Speicher,
also der nicht der Stack selber von der Tiefensuche oder irgendwas
konstantes ist wie ein paar Variablen, dass es den,
gibt es nochmal ein bisschen entsprechend nicht im Moment.
Okay, gut, ja, dass die Laufzeit O von
V Plus E rauskommt, ist glaube ich klar.
Wir haben eine Tiefensuche,
die hat Linearzeit und die Stack-Operation kosten alle konstante Zeit und
alle Rechenoperationen für den Low-Point benötigen ebenfalls konstante Zeit im Algorithmus,
also hat man O von V Plus
Okay, also zusammenfassend zum Thema Tiefensuche.
Wir können mit der Tiefensuche viele Strukturfragen auf Grafen beantworten.
Wir haben insgesamt jetzt sieben Anwendungen gesehen, der Tiefensuche,
sowohl in Gerichteten als auch ungerichteten Grafen. Was wir insbesondere machen können,
und das ist die wichtigste Anwendung der Tiefensuche ist,
den starken Zusammenhang, den B-Zusammenhang,
die einfachen und den einfachen Zusammenhang in normalen,
ungerichteten Grafen,
können wir mit der Tiefbesuche in den ER-Zeit bestimmen
Und warum ist das so ein zentraler Algorithmus, dass wir
diese Zusammenhangskomponenten bestimmen können? Dass es deswegen so wichtig,
weil wir bei sehr großen Grafen häufig das Problem haben,
ja, die Laufzeiten werden lang für schwierigere Algorithmen.
Und wenn wir erstmal in der Lage sind, das Ganze in Komponenten zu zerlegen,
haben wir viele kleine Grafen, was die Laufzeitanforderungen entsprechend dann reduzieren kann.
Ja, das wird man, damit man mal wieder sehen. Okay,
was wir auch gesehen haben,
was wir uns aber nicht genau angeschaut haben, ist,
dass man Grafplanarität in den Jahrzeit testen kann, also gucken kann,
ob ein Algorithmus oder ob ein Marfplanar zeichenbar ist.
Das ist etwas, was man im Hinterkopf behalten soll,
weil über diese Frage stolpert man in der Praxis ab und zu mal,
dass man sich fragt, kann man irgendwie den Grafenplanar auf der Ebene,
in der Ebene darstellen.
Also ohne Kantenüberkreuzung. Ja, es war okay,
das war es zum Thema Tiefensuche und wir werden jetzt,
wir werden jetzt einsteigen in das dritte Kapitel,
Und uns von der tiefen Suche zur breiten Suche bewegen.
Wir werden wieder damit beginnen,
dass wir ein paar Sachen aus dem dritten Semester wiederholen.
Ja, ich werde ihn nochmal, das mache ich wieder
ein bisschen zügiger. Wir werden mal die, ja,
Breitensuche nochmal kurz angucken. Und dann die kürzesten Wegeberechnungen,
da gibt es ja verschiedene ganz grundlegende Algorithmen,
die werden wir auch nochmal ganz kurz wiederholen.
Bis zum Diaxtra-Algorithmus, den sie sicherlich alle kennen.
Also das ist der Wiederholungsteil
Und wir werden uns dann in diesem Kapitel damit beschäftigen,
wie wir das All-Pairs-Problem lösen können.
Also wie wir kürzeste Wege zwischen allen Knotenpaaren in einem Schritt
berechnen können. Das wird sozusagen die Neuerung sein,
die wir dann hier in diesem Kapitel betrachten werden.
Ja, okay. Zur breiten Suche. Und zum kürzesten
Wegeproblem, was eng damit verknüpft ist. Also kürzeste Wegeprobleme
haben wir ständig in Grafen. Immer wollen wir wissen,
wie wir von einem Punkt zum anderen kommen können und dabei
möglichst wenig Kanten durchlaufen
Müssen. Das sind Logistikprobleme wie Routenplanung, Telekommunikationsfragestellungen in Datennetzwerken.
Im Chip-Layout haben sie solche Wegeprobleme, wenn sie Leiterplatinen designen.
In der Robotik haben sie ohne Ende Wegeprobleme.
Also Wegeprobleme und die Bestimmung kürzester Pfade,
die treten in der Praxis ganz, ganz vielfältig auf.
Dementsprechend gut sind die auch analysiert und deswegen
können wir die auch für die allerallermeisten Probleme schnell berechnen.
Wir haben als allererstes immer, ja,
können wir
die Probleme klassifizieren
Das erste Kriterium, was wir uns immer anschauen sollen,
sind, die kannten, sind, sie kannten Gewichte.
Also was steht an den Kanten dran? Entweder sind die Uniformen,
das bedeutet, jede Kante hat die gleiche Länge,
also eine Einheitslänge, das kurz in der Praxis eher selten auf.
Meistens stehen da schon irgendwelche Längen oder Kosten oder irgendwas dran.
Oder sie sind positiv, das heißt, alle Kantengewichte sind größer, gleich null.
Oder sie sind beliebig. Das sind die drei Szenarien,
die wir haben können. Und das Problem wird schwieriger,
je weiter wir nach rechts gehen. Uniform ist am einfachsten,
positives, noch einfach, aber schon schwieriger und beliebige Kantengewichte,
als auch negative ist am schwierigsten.
Glücklicherweise treten in der Praxis eine negative Kanntengewichte selten auf,
weil sie unserem Gefühl von Distanz nicht entsprechen.
Also wir haben, wenn wir über Distanzen nachdenken, in der Regel
immer nur positive Werte. Ja, dann das Zweite,
was man sich anschauen sollte, ist die Eigenschaften des Grafen.
Wir haben einen allgemeinen Graf. Manchmal haben wir einen Deck,
einen gerichteten adzyklischen Grafen. Wenn wir einen Deck haben,
ist das Problem der kürzesten Wegeberechnung viel einfacher.
Und es ist der Deikstra-Algorithmus, interessanterweise viele gar nicht.
Der DEIX-Tra-Algorithmus ist nicht das effizienteste Verfahren, zu kürzeste Wegeberechnung
auf Decks. Also wenn wir keine Zyklen haben,
das werde ich gleich auch noch mal zeigen,
können wir kürzeste Pfade in der Nährzeit berechnen mit einem ganz einfachen Algorithmus.
Und da sollte man kein DEIX dranwenden in dem Fall.
Ansonsten haben wir allgemeine Grafen, da haben wir dann beliebige Zyklen dran.
So, und das dritte Kriterium, nachdem wir unsere Probleme klassifizieren,
ist, welche kürzesten Pfade interessieren uns?
Da gibt es das Single-Pair-Problem, also sie haben ein Startknoten,
ein Zielbroten das Problem von einem Navigationsgerät zum Beispiel und wollen
den kürzesten Pfad vom Staat zum Zielknoten wissen.
Alle anderen kürzesten Pfade interessieren sie nicht. Dann haben wir das Problem,
wo sie von einem Startknoten die gützenden Wege zu allen anderen Knoten bestimmen wollen.
Dieses Problem heißt dann Single Source. Und dann haben wir das Problem,
wo wir die kürzesten Pfade zwischen allen Knotenpaaren wissen wollen,
dass es dann all pairs.
Die drei Varianten unterscheiden wir. Natürlich kann man,
wenn man ein Single-Pair-Algorithmus hat, den N-Mahl benutzen,
um Single Source zu lösen und wir können ein Quadrat mal nutzen,
um All Pairs zu lösen.
Aber das ist definitiv nicht geschickteste Art und Weise, das zu tun.
Deswegen sollte man diese Probleme unterscheiden und sich vorher überlegen,
was man eigentlich tun will. Dass am meisten betrachteste Problem
ist Single Source und auch der Deikstra-Algorithmus löst ja das Single Source-Problem.
Ja, formell beschrieben hat man also einen Graf,
man hat Kantengewichte, die also jeder kannte, ein Gewicht weh zuordnen.
Das ist eine reellwertige Zahl. Gesucht ist es dann der
kürzeste Pfad zwischen den Knoten aus V und dazu muss man erstmal sagen,
was ist denn die Länge oder das Gewicht eines Pfades?
Und dann nimmt man also ein Pfad von V0 V1 bis VK
und das Gewicht eines solchen Pfades ist eben die Summe der Kantengewichte,
die man der Kanten, die man durchläuft.
Und das Gewicht des kürzesten Pfades ist dann eben das Minimum darüber
und das beiden wir im Folgenden immer als Delta UV bezeichnen.
Also U und V sind zwei Knoten und wir sagen Delta
UV ist das Minimum über die Gewichte aller Pfade P,
wobei wir bei dem U-Beginn dann einen Pfad P haben,
der uns zu V führt.
Ja, und wenn so ein Pfad nicht existiert,
was ja sein kann, dann gehen wir das davon aus,
dass die Länge oder das Gewicht des kürzesten Pfades Unendliches.
Ich werde meistens von Länge sprechen,
formell kann man natürlich vom Gewicht sprechen,
weil man an den Kanten formell erkannten Gewichte und keine Kanten Längen hat,
aber ja, das kann man jetzt in diesem Fall natürlich synonym verwenden.
Okay, was macht die Breitensuche? Die Breitensuche zusammengefasst ist,
wir markieren wieder die Knoten, weiß-grau, schwarz.
Am Anfang sind alle Knoten weiß. Zum Zeitpunkt der ersten
Entdeckung wird der Knoten jeweils dann grau gefärbt.
Der Knoten wird dann schwarz entfernt, wenn alle Adjazentenknoten bereits entdeckt wurden.
Das heißt, wenn die nicht mehr weiß sind
Start ist ein beliebiger Knoten S und iterativ besuchen wir alle Nachbarn von S.
Und jetzt kommt der eine Unterschied zur tiefen Suche.
Wir besuchen von S aus erst alle Nachbarn von S,
bevor wir dann die Nachbarn der Nachbarn von S betrachten.
Das realisieren wir mit einer Warteschlange und wir haben jeweils einen Vorgänger oder
einen Vater von U und wenn der Knoten U erstmals über eine Kante VU besucht wird,
dann wird V entsprechend der Vorgänger von U.
Und dadurch kriegen wir wieder einen Baum Und das ist jetzt
der BFS-Baum, der Tiefensuchbaum mit Wurzel-S.
Und die Distanz von U bezeichnen mit der Distanz von U-Bezeichnen,
werden die Pfadlänge von der Wurzel-S bis U in den BFS bauen.
Das ist der Pseudocode-Code, der Tiefensuche. Wir beginnen,
wir färben alle Knoten, weiß. Wir setzen die Discovery-Zeit
jedes Knotens auf unendlich. Entschuldigung, jetzt komme ich selber
durcheinander gekommen. D ist jetzt keine Discovery-Zeit mehr.
D ist jetzt eine Distanz. Also bei der Tiefensuche war
D immer die Discovery-Zeit. D ist jetzt die Entfernung, die Pfadlänge,
die wird auf unendlich gesetzt und Pi von Uwe schreibt den Vorgänger,
das wird auf Nil gesetzt, der Knoten S wird grau gefärbt,
dann wird DVS auf Null gesetzt.
Das ist die Quelle, der Knoten, an denen wir starten,
die Wurzel und die hat keinen Vorgänger.
Wir haben jetzt eine Warteschlange, die wird hier initialisiert,
der Knoten S wird in die Warteschlange gehängt,
solange diese Schlange nicht leer ist,
das ist diese Wildschleife hier,
wird der Knoten nach dem anderen aus der Cue herausgenommen
Wir sondieren die Adjazenzliste, wenn der Knoten, den wir sehen,
noch weiß ist, wird der Knoten grau gefärbt.
Wir sitzen rätzen die Distanz von V auf die Distanz von U plus 1.
Wir setzen den Vorgänger von V auf den Knoten U.
Wir hängen den Knoten V in unsere Warteschlange rein und das machen wir so lange,
bis wir alle Knoten sondiert haben und im Anschluss wird der Knoten U hier schwarz gefärbt.
Stiefensuche, Beispiel, hier Staatsnoten S, der wird grau,
hat die Zeit null, die beiden Kanten werden durchs Laufen,
die kriegen die Zeit eins, die Nachbarn der Nachbarn kriegen zwei,
die Nachbarn, die Nachbarn kriegen dann irgendwann drei und dann geht das so weiter.
Ich glaube, das muss ich jetzt nicht mehr Schritt für Schritt durchgehen,
den Algorithmus sollten sie eh kennen. Okay, so,
das heißt, es entsteht ein BFS-Baum bei der Breitensuche,
wir haben Kanten, die wir durchlaufen,
wenn wir den Knoten erstmalig sehen, das sind die Kanten dieses BFS-Baums,
wir haben die Knotenmenge, das sind alle Knotenmengen,
die wir bei dem Durchlauf des BFS antreffen und besuchen und
Und der Graf GP beschreibt entsprechend einen Baum. Warum ist
das ein Baum? Naja, er ist zusammenhängend,
weil für alle Knoten V existiert ein Pfad von V über
die Wurzel bis zur Wurzel, indem wir einfach diese Vorgängerknoten jeweils besuchen.
Und er enthält keine Zyklen, weil jeder Knoten wird natürlich nur einmal erstmalig entdeckt.
Und deswegen kann es gar keine Zyklen geben.
Laufzeit ist O von N plus M, also Linearzeit,
Initialisierung ist O von V, also oder O von N,
also Anzahl der Knoten.
Und jeder Knoten wird einmal eine Q eingefügt und einmal wieder entnommen.
Und in der Wildschleife wird jede Art der Zensliste
einmal durchlaufen
Und dadurch kommen wir zu einer Laufzeit von zehnmal ein Zerknoten
plus zweimal zehnmal ans Zerkanten, also O von V plus E.
Vielleicht noch einen entscheidenden Detailunterschied zwischen Tiefensuche und Breitensuche.
Und zwar bei der Tiefensuche ist es üblich, dass immer dann,
wenn wir so einen rekrusiven Abstieg gemacht haben und dann wieder zurückkommen,
wir dernach weiter die Knoten prüfen,
ob wir noch einen nicht gesehenen Knoten haben.
Und dann würden wir mit dem weiterlaufen. Das könnte man
bei einer breiten Suche natürlich im Prinzip auch machen.
Also es könnte jetzt zum Beispiel sein,
wir hier auch Zusammenhangskomponenten haben, die irgendwie getrennt sind und
die wir jetzt von einem Startnoten-S nicht erreichen können.
Nur, wenn man von der Breitensuche per se spricht,
dann macht man das nicht. Ja,
also eine Breitensuche läuft nur bei einem Knoten los und guckt,
welche Knoten kann ich von diesem Knoten aus erreichen.
Und wenn es dann Knoten gibt, die ich nicht erreichen kann,
dann bleiben die eben weiß und der Algorithmus terminiert.
Das ist keine Notwendigkeit, das könnte man auch anders machen,
aber so ist die Breitensuche erstmal definiert als Suchprozess.
Ja, also eine Breitensuche besucht damit nicht notwendigerweise jeden Knoten.
Es sei denn, dass sie explizit für die Knoten,
die sie dann noch nicht gesehen haben,
diese Breitensuche wieder neu starten
Ja. Okay, das dazu. Breitensuche kann die kürzesten Pfade bestimmen.
Ja, warum ist das so? Wir können mit dem Delta,
also wir gehen davon aus, wir haben uniforme Kantenlängen,
also keine Kantengewichte, alle Kanten sind gleich lang.
Dann bezeichnen wir mit Delta mal die minimale Anzahl Kanten eines
Pfades von S nach U, das haben wir eben schon so definiert.
Und wenn es einen kürzesten Pfad nicht gibt, dann ist das unendlich.
Und das Delta ist dann entsprechend der kürzeste Abstand oder der kürzeste Pfad.
Also das Delta selber, der kürzeste Abstand und der Pfad von nach U,
der diesen Abstand realisiert, ist der kürzeste Pfad.
Ja, die breiten Suche berechnet kürzeste Pfade.
Das sieht man an der Struktur kürzester Pfade. Das heißt,
wenn wir einen Graf haben, es ist ein Startknoten,
wir betrachten eine Kante von UNAV, dann gilt immer,
dass der kürzeste Abstand von S nach V kleiner gleich,
dem kürzesten Abstand von S nach U sein muss plus eins.
Warum? Weil, naja, wenn der kürzeste Abstand von S
nach U ist in irgendein gewisser Pfad bis zur U,
da gibt es aber eine Kante von UNAV.
Das heißt, es gibt einen Pfad entlang des kürzesten Fahrdes
von S nach U und dann die Kante UV,
also eine Kante länger und das kann dann maximal die Länge
des kürzesten Fahrdes von S nach V sein.
Die Breitensuche beschränkt Delta SU von oben, das heißt,
wir wissen, dass die D-Werte, die wir berechnen,
immer größer sind, als die kürzesten Abstände.
Warum? Weil die D-Werte reale Pfadlängen beschreiben, das heißt,
die beschreiben immer die Länge des Pfades entlang des BFS-Baums
und weil sie überhaupt die Länge eines Pfades bestimmen,
ist die Länge natürlich größer gleich der Länge des minimalen Falles
Die Warteschlange Kuh hat eine spezielle Struktur,
die ab und zu wichtig ist bei der breiten Suche.
Es ist eine zweite Sache, die viele schnell mal so gar nicht wissen,
was also, worüber wir mal kurz nachdenken muss.
Und zwar, wenn man sich mal darüber Gedanken macht,
welche Knoten sind denn jetzt in dieser Warteschlange drin, dann kann man feststellen,
dass es in dieser Warteschlange nur zwei verschiedene D-Werte geben kann.
Ja, alle Knoten, die da drin sind.
Und zwar sind die bezüglich des D-Werts aufsteigend sortiert,
in der Warteschlange. Und der letzte Wert in der Warteschlange,
also der D-Wert des letzten Knotens, der Warteschlange,
ist höchstens um eins größer als der vorherige,
als der Kopf der Schlange, ja, als D von V1. Das heißt,
es gibt nur die Werte der Pfadlänge des Startknotens,
V1 und D von V1 plus 1.
Das sind die einzigen beiden D-Werte, die vorkommen für alle Knoten,
die in der Schlange sind, ja. Okay,
wenn wir, das ist das BFS-Theorie als nächstes,
wir haben also den Grafen,
wir haben einen Startnoten nach Durchführung des BFS,
gilt für alle Knoten V, die wir von S erreichen können.
Die Knoten sind schwarz, erstens, und zweitens,
die berechnete Distanz entspricht den kürzesten Faden, den kürzesten Abständen.
Und wir haben mit der Relation, mit Pi hier entsprechend
die kürzesten Pfade von S nach V auch berechnet.
Müsste man jetzt noch beweisen, weil ich die Dinge hier
nur noch mal zusammenfasse, mache ich das jetzt an dieser Stelle hier nicht.
Kürzeste Pfade haben Eigenschaften, sie haben die Eigenschaft der optimalen Teilstruktur besagt,
dass wenn wir einen Graf haben, das ist jetzt ein gewichteter,
gerichteter Graf, mit den Kantengewichten W.
Und wir haben einen kürzesten Pfad von V1 über V2 und
so weiter bis VK. Dann gilt für alle Teilpfade,
die hier drin sind, dass sie ebenfalls kürzeste Pfade sein müssen.
Also jeder Teil eines kürzesten Pfades ist wieder kürzester Pfad.
Ist eigentlich logisch, dass das so sein muss. Kann natürlich auch ganz leicht zeigen,
wenn es also zwischen einem Knoten VI und VJ noch einen alternativen Pfad gebe,
der kürzer wäre,
dann würde dieser kürzeste Pfad natürlich auch den Abstand von V1 bis VK hier verringern.
Deswegen darf das nicht vorkommen. Kürzeste Wege haben also
die Eigenschaft optimaler Teilstrukturen. Und das ist eine,
das ist die zentrale Eigenschaft, die genutzt wird,
wenn wir kürzeste Wege berechnen.
Immer wieder
Dann kann man sich generell fragen,
was passiert denn eigentlich, wenn ich negative Kantengewichte habe?
Im Prinzip erst mal nichts. Also auch die kürzesten Pfadlängen und Abstände
sind auch für negative Kantengewichte definiert. Die werden im Zweifelsfall
minus unendlich. Und zwar werden sie mal dann minus unendlich,
wenn es auf dem Pfad von UNAV ein Zyklus negativer Gesamtlänge gibt.
Ja, also negative Kantenlängen per se sind noch nicht schlimm.
Ja, also hier in meinem Beispiel zum Beispiel hat der
Knoten B hier, der hat die Gesamtdistanz minus eins von
dem Startknoten
S, da gibt es drei, das kostet drei Zeiteinheiten,
um von hier nach hier zu kommen und dann minus vier Einheiten, um von A nach W zu kommen.
Dann kommt da eine minus eins raus, ja. Aber wenn wir eine Situation haben,
wo wir einen Zyklus haben, der in der Gesamtlinge negativ ist,
zum Beispiel zwischen den Knoten E und F,
da haben wir drei Einheiten,
um von F nach E zu kommen und minus sechs Einheiten,
um von E nach F zu kommen,
dann haben wir einen Zyklus negativer Länge und mit jedem Zyklusdurchlauf hier verringert sich die Entfernung,
ja, weil wir bei jedem Zyklusdurchlauf Die Entfernung von F um drei Einheiten verringern.
Und dann wirst du immer weniger, weniger, weniger.
Deswegen ist die Entfernung hier minus unendlich. Und das gilt
natürlich auch für alle Knoten, die hinter F folgen,
weil wir diesen Zyklus hier entsprechend beliebig oft durchlaufen können,
um dann irgendwann zur G zu kommen.
Ja. Okay, das passiert bei negativen Kantengewichten.
Ja, wo kommen negative Kantengewichte vor? Ich weiß nicht,
ob sie auf ein Beispiel kommen, so was mir so immer einfällt,
ist, wenn man irgendwie zum Beispiel an so Kanten,
wenn man so zum Beispiel Höhen dran schreibt.
Und man sagt hier, man hat zum Beispiel
so eine Wanderkarte mit so verschiedenen Hütten oder was.
Und dann sagt man, hier muss man drei, drei,
weiß ich nicht, 300 Meter hochgehen und hier kommen wieder 400 Meter runter gehen.
Das wäre zum Beispiel so ein Fahrtproblem,
wo sie positive und negative Kantengewichte haben,
wo sie dann die Länge des minimalen Pfades als die minimale Höhendifferenz angeben können,
auf so einem, in so einem Grafen würden aber keine negativen Zyplen auftreten.
Ja? Ich würde mal sagen, warum bei dir jetzt
nicht zwei rauskommt, sondern... Bei D kommt nicht zwei raus,
weil die Wohle hier einen Fehler hat, würde ich mal sagen,
oder?
Ja, klar,
es ist natürlich zwei
Ja, das haben sie gut erkannt. Ja,
das ist zwei. Haben sie noch einen Fehler gefunden,
der Rest stimmt aber, ne? Das ist tatsächlich zwei
. Okay. Wenn alle Knoten die Eigenschaft haben,
dass sie nicht voneinander erreichbar sind, dann ist die Pfadlänge unendlich,
ja? Und... Entschuldigung, jetzt sag ich was,
jetzt haben sie mich gerade so rausgebracht.
Also, wenn ich eine Menge von Knoten habe,
die ich von dem Startknoten aus nicht erreichen kann,
dann ist die Pfadlänge für alle Knoten unendlich.
Und das ist unabhängig davon, ob die noch einen negativen Zyklus bilden, ja?
Weil von S aus komme ich nicht zu diesen Knoten,
deswegen sind die alle unendlich von der Partlänge
Ja. Okay. Gut. Ja. Kürzeste Wege kann ich
entsprechend als die Teilgrafen beschreiben, die die kürzesten Wege angeben.
Und das sind immer Bäume. Warum sind das Bäume?
Also wenn ich von einem Startnoten ausgehe, sind das
immer Bäume. Warum? Wegen der optimalen Teilstruktureigenschaft sind,
weil ich ja, wenn ich einen kürzesten Pfad habe,
gilt ja für alle Wege auf diesem Pfad,
dass sie ebenfalls kürzeste Pfade sind.
Und deswegen habe ich automatisch immer
einen Baum
Also, Vorstrich ist also die Menge der erreichbaren Knoten.
Gestrich bildet einen gerichteten Baum mit Wurzel S. Und, ähm,
und für alle Knoten V, ähm, weiß ich,
dass entlang dieser Kanten in dem Baum wir einen kürzesten Pfad haben,
der uns von S nach V führt.
Beachten Sie, dass genau wie auch bei der tiefen Suche,
da habe ich immer darauf hingewiesen,
ist es auch hier so,
dass der Baum der kürzesten Pfade nicht
eindeutig sein muss
Also es kann unterschiedliche Pfade gleicher Länge geben,
die dann alle kürzeste Pfade sind und dann ist es nicht
eindeutig und dann können unterschiedliche Lösungen, natürlich alle korrekt sein.
Gut, wie berechnet man nun kürzeste Pfade, wenn man Kantengewichte hat? Und zwar macht man das mittels einer sogenannten Relaxationsoperation,
die Relaxationsoperation steht hier ganz unten auf der Folie,
die läuft zwischen, betrachtet zwei Knoten, UNV,
die über eine Kante miteinander verbunden sind und betrachtet die Gewichtsfunktion weh und die Relaxationsoperation macht folgendes,
wenn also die bisherige berechtete Distanz von Knoten V größer ist,
als die Distanz zu Knoten U plus die Pfadlänge von U nach V,
dann setzen wir die neue Distanz für den Knoten V auf D von U plus WUV.
Und dass der Vorgänger wird an der Stelle,
also der Vorgänger von Pfoten, Knoten V wird dann Knoten, U.
Hier ist die Relaxationsoperation gezeigt. Es gibt immer zwei Fälle,
der Fall, der erste Fall ist,
wenn Hier die Bedingung wahr ist,
dann wird der Pfad tatsächlich in der Länge verkürzt für den Knoten V und es wird ein neuer Vorgänger gesetzt,
wenn die Bedingung falsch ist, passiert gar nichts.
Und die Knoten behalten ihre Distanzen und auch die Vorgängereigenschaft.
Das ist die eine zentrale Funktion, die immer genutzt wird.
Wir werden also im Markanten betrachten und über die Kanten relaxieren und
dementsprechend Pfade entsprechend anpassen. Die Distanz, die wir berechnen,
ist also immer eine obere Schranke und je mehr kannten wir betrachten,
umso stärker nähern wir uns dem tatsächlichen kürzesten Pfad zwischen
zwei Knoten
An. Am Anfang ist es unendlich und am Ende der Berechnung
haben wir entsprechend die kürzesten Pfade bestimmt.
Die Initialisierung kürzester Wegeprobleme sieht dann eben so aus,
dass sich alle Distanzwerte am Anfang auf unendlich setze, die Vorgänger auf Nilsätze,
also auf nicht definiert und der Knoten S kriegt den Wert null.
Kürzeste Wege haben eine Reihe von Eigenschaften, die erzähle ich
Ihnen noch schnell. Ob der Fehler noch da ist,
habe ich noch nicht geprüft, das muss ich nochmal gucken hier,
das sage ich Ihnen in der nächsten Vorlesung nochmal,
ob der noch drin ist oder nicht.
Es war in dem Alpenkorn, war da immer ein Fehler.
Wir haben die die Dreiecksumgleichung, die zu jedem Zeitpunkt gilt.
Das heißt, für alle kannten UV muss gelten,
der kürzeste Pfad von S nach V ist kleiner oder gleich
dem kürzesten Pfad von S nach O plus.
Das kannten Gewicht von U nach V, ja,
also, ja, das ist, glaube ich, klar.
Wir haben die Pfad-Relaxationseigenschaft, die man streng genommen beweisen muss. Das heißt,
wenn wir einen Pfad haben mit der folgenden Liste von Knoten,
die wir besuchen, VH0 bis VK,
und wir relaxieren die Kanten in der Reihenfolge dieses Pfades,
dann gilt danach das D von V, dem kürzesten Pfad entspricht.
Und hier haben wir noch die generellen Eigenschaften der kürzesten Pfadalgorithmen,
Idee von V-Werte fallen monoton und nähern sich D von SV an.
Es gibt die No-Path-Property, das heißt, D von V bleibt unendlich,
wenn es keinen Pfad gibt. Die Konvergenzeigenschaft, ja,
sage ich Ihnen dann nächstes Mal, und die Vorgänger-Teilgraf-Eigenschaft,
das bedeutet, dass jeder Teilgraf wiederum kürzester Pfad ist,
haben wir eigentlich schon gesehen.
Und das sind die wichtigsten Eigenschaften und auf der Basis basieren unsere kürzesten Pfade-Algorithmen.
Und am Mittwoch werde ich noch zehn Minuten eine Viertelstunde darauf investieren,
nochmal schnell den Bellmann fort und den Dike-Stralgorithmus zu zeigen.
Und dann werden wir uns mit dem All-Pairs-Problem dann beschäftigen.
Und das war es dann für heute
Ja, das entscheidende Lämmer, was wir hier haben, ist das folgende. Wenn wir zwei Komponenten haben,
also zwei starke Zusammenhangskomponenten,
die heißen jetzt C- und C-Strich in einem Grafen und das sind unterschiedliche,
ja, also klar, C- und C-Strich zwei verschiedene Komponenten,
dann gilt für alle Kanten,
die von U nach V verlaufen,
dass und U ist in der ersten Komponente C und V
ist in der zweiten Komponente C-Strich,
dass die Finishing-Zeit der Komponente C größer ist als die Finishing-Zeit
der Komponente C-Strich
Und das haben wir in abgewandelter Form gerade schon gesehen,
als wir uns überlegt haben, warum die topologische Sortierung korrekt ist.
Da haben wir ein fast ähnliches Argument, ein ganz ähnliches
Argument gesehen wie das hier. Das ist nämlich fallende Finishing-Zeiten
etwas mit Richtungen von Kanten in Decks zu tun haben.
Und der Graf, den wir betrachten,
der Graf zwischen den Komponenten ist ja ein Deck.
So, das müssen wir erst mal zeigen, dass das so ist.
Und dazu brauchen wir einen folgenden Beweis. Wir machen jetzt
eine Fallunterscheidung danach, wann wir welche Komponente entdecken.
Also nach welchen Zeiten
Ich gucke immer so auf die Uhr,
weil wir noch so zwei, drei Minuten haben.
Ja, ganz kommen wir da jetzt nicht mehr durch.
Naja, ich fange mal an, dann habe ich auch
was zum Wiederholen nächstes Mal. Also der erste Fall ist,
wir entdecken erst die Komponente C und dann die Komponente C-Strich.
Das ist hier dargestellt. Also wir kommen irgendwo von hier
oben in den Grafen. Ja,
das ist jetzt eine Baumkante und dann, zack,
finden wir den Knoten X und der Knoten X ist der erste Knoten,
der Komponente C in der Tiefensuche.
Und dann laufen wir irgendwie durch,
finden dann alle Knoten und die Komponente C-Strich wird offensichtlich später
erkannt,
Und wir haben ja so eine Kante, die läuft von U nach V.
So, und jetzt sagen wir, dass die Finishing-Zeit von
der Komponente C größer ist als der von DLC-Strich.
Warum ist das so? Das kann man auch relativ leicht sehen. Also,
wenn X in der Komponente ist,
und X ist der erste Knoten der Komponente,
dann ist D von X gleich D von C. Also,
das ist der Knoten, der das Minimum bestimmt.
Und zu dem Zeitpunkt, wo wir X erkennen,
sind alle anderen Knoten hier noch weiß,
denn die Komponente ist die Striche haben wir noch gar nicht gesehen.
Ja, und die Komponente C haben wir auch noch nicht gesehen.
Und weil es diese Kante gibt von UNAV,
gibt es einen Pfad von X zu jedem beliebigen Knoten W in dieser Komponente C-Strich.
Und deswegen sind alle Knoten