Okay, gut, ich glaube, wir können loslegen.
Im Laufe der Vorlesung muss ich mir noch überlegen,
wie sich diese Tafel da wieder runterkriege.
Das scheint irgendwie für große Professoren gemacht zu sein.
Jetzt haben sie eine Idee. So, wir sind bei
der tiefen Suche angekommen. Also wir sind in den Graf-Algorithmen
. Wir haben in der letzten Vorlesung uns länger überlegt,
was man mit einer breiten Suche machen kann und sind da, haben dann festgestellt,
dass mit der breiten Suche schon kürzeste Pfade bestimmen kann,
vorausgesetzt, wir zählen einfach nur die Anzahl der Kanten entlang der Pfade.
Und haben uns dann eine Alternative Vorgehensweise angeschaut, wie wir
einen Grafen durchlaufen können. Das war die Tiefensuche.
Die Tiefensuche funktioniert wie Volf. Das ist ein rekrusives Schema,
wobei wir einfach die Knoten nach und nach besuchen.
Und bei jedem Knoten uns erst einmal zu einem Nachbarknoten,
den wir noch nicht besucht haben, bewegt haben,
bevor wir den Rest der Addizenzliste überarbeiten.
Also wir tauschen im Prinzip, ist der Breitensuche nur
die Warteschlange aus durch einen Stapel.
Der Stapel, den sieht man in dem Algorithmus nicht,
weil wir
einen rekrusiven Algorithmus verwenden
Quasi den Programmstapel benutzen, um uns zu merken,
welche Knoten wir gerade betrachten.
Hier ist nochmal der Pseudo-Code der Tiefensuche. Wir sehen hier
den nicht-rekusiven Teil. Wir markieren alle Knoten zunächst als weiß.
Wir haben eine Uhr, die registriert. Zu welchem Zeitpunkt
wir welchen Knoten besucht haben. Und dann betrachten wir alle Knoten nacheinander.
Wir schauen, wenn dieser Knoten noch weiß ist,
dann rufen wir unsere rekusive Routine DFS Visit auf.
Die rekusive Routine hier färbt den Knoten zunächst einmal grau.
Dann erwidert die Uhr diskret einen Schritt weitergezählt.
Wir merken uns die Discovery-Zeit, also D von
Die Entdeckungszeit. Dann betrachten wir die Addizenzliste des Knotens. Uh,
wenn wir einen Knoten V finden, der noch weiß ist,
dann haben wir den nicht gesehen, dann merken wir uns,
dass uh, der Vorgänger von V ist.
Und wir besuchen reklusiv diesen Knoten V. Wenn wir das mit allen Knoten hier gemacht haben,
dann haben wir keinen Knoten mehr in der Addizenzliste, den wir noch nicht betrachtet haben.
Wir färben dann diesen Knoten uh, schwarz,
der ist vollständig abgearbeitet und wir merken uns den Zeitpunkt der vollständigen Abarbeitung,
indem wir unsere Uhr wieder einen Schritt weiter drehen und uns hier die Finishing Time in dem RAF speichern.
Was ist das Vorgehen? Hier war das Beispiel, das haben wir uns natürlich schon angeguckt in der letzten Vorlesung. Die Knoten,
wir beginnen hier mit irgendeinem, das ist ja der Knoten-U hier oben,
der kriegt die Discovery-Zeit 1 und dann folgen wir den Kanten,
kriegen also weitere graue Knoten,
die kriegen alle ihre Discovery-Zeiten und irgendwann haben wir keine Kante mehr,
die uns zu einem weißen Knoten zurückführt,
die Knoten werden dann vollständig abgearbeitet, sie werden dann schwarz,
wir laufen die Rekursion wieder zurück,
die Knoten werden alle schwarz und irgendwann haben wir dann so einen Punkt erreicht,
wo wir alle Knoten auf der, die wir rekursiv betrachtet haben,
bearbeitet haben, wir gehen wieder zurück zu dem,
zu dem nicht rekursiven Teil, schauen,
ob es noch weiße Knoten gibt,
da würden wir den Knoten weh hier finden,
Dann würde der grau und wir würden die Rekursion wieder starten.
Der kriegt also seine Zeit, wir sehen so eine Kante hier,
die führt uns aber nur zu einem schwarzen Knoten,
würden wir nicht langlaufen.
Wir entdecken hier den Knoten-Set als letztes,
würden dann hier noch diese eine Schleife analysieren und dann würden
auch diese Knoten schwarz und dann wäre der Algorithmus terminiert.
Das Resultat dieses Algorithmus wären zum jetzigen Zeitpunkt erstmal nur diese Zeiten,
die wir berechnet haben, die in den Knoten stehen.
Also Discovery und Finishing-Zeiten und dann müssen wir überlegen,
ob wir was Sinnvolles damit tun können
So, zunächst einmal haben wir die Struktur des Algorithmus betrachtet und versucht,
Eigenschaften zu charakterisieren, zu bestimmen. Und das Wichtigste,
was wir da gesehen haben, war dieses Klammerungstheorien und das Klammerungstheorien besagt,
dass wenn wir die Zeitintervalle betrachten zwischen Erkennungszeit und abschließender Betrachtung,
also zwischen D und V, dann können diese Zeitintervalle sich zwar vollständig enthalten,
sie können auch das jung sein, aber sie können sich nicht so partiell überlappen.
Das ist der Inhalt des Klammerungstheoriums plus der Tatsache,
dass wenn sie so eine wenn sie so eine Enthaltung haben,
also wenn das eine Zeitintervall in dem anderen enthalten ist,
dann wissen sie auch was über das Vorgänger-Nachgänger-Nachfolger-Verhältnis im DFS-Baumt,
der ja entsteht, wenn sie also die Erkannten durchlaufen und dann wissen sie entsprechend,
dass das eine, also wenn U in V enthalten ist,
dass U dann auch ein Nachfahre sein muss von V.
Im Beweis gucken wir uns nicht nochmal an,
aber hier das Bild vielleicht nochmal,
also wir haben hier die verschiedenen Knoten,
hier ist nochmal ein anderes Beispiel,
wir haben hier wieder verschiedene Discovery und Finishing-Zeiten,
nachdem so ein DFS durchgelaufen ist,
die rot unterlegten Kanten hier sind die Baumkanten
Und wir können diesen Graf einfach jetzt anders darstellen,
indem jeder Knoten so ein Zeitintervall wird.
Und dann sehen wir, dass diese Zeitintervalle entweder vollständig ineinander enthalten sind.
Also Z ist vollständig in S enthalten. Und Z ist
auch ein Nachfolger von S. Oder sie sind komplettes Jung,
so wie Tee und S.
Die haben gar nichts miteinander zu tun.
Und dann stehen die auch nicht in einem Vorgänger-Nachgänger-Nachfolger-Verhältnis zueinander.
Okay, das war das Klammerungstheorium,
was wir als erstes gesehen haben
Jetzt, das nächste Strukturtheorien, was es gibt über
die Tiefensuche, das ist das der weißen Pfade.
Das werden wir uns noch anschauen und danach werden wir anfangen,
irgendwas Praktisches mit der tiefen Suche zu machen. Diese beiden Theorien,
die brauchen wir, um dann nachher zu zeigen, dass,
was immer wir berechnen wollen mit der tiefen Suche, auch korrekt ist. So,
dieses Theorien der weißen Pfade lautet wie folgt,
in einem DFS Wald ist V, ein Nachfahre von U,
genau dann, wenn es zum Zeitpunkt D von U,
also in dem Augenblick,
wo wir den Knoten U das erste Mal sehen,
einen Pfad gibt,
also der fängt hier mit U an und dann geht er zu irgendwelchen Knoten W1,
wie 2 und so weiter und der letzte Knoten dieses Pfades ist der Knotenfahrer Genau.
Es ist also ein Pfad von UNAV mit der Eigenschaft,
dass alle Knoten entlang dieses Pfades die Farbe Weiß haben.
Beachten Sie, das ist ja eine genau dann Van-Beziehung. Also wir müssen beides zeigen,
wir müssen zeigen, also V ist Nachfahre von, uh,
da müssen wir zeigen, es gibt so einen weißen Pfad,
Fahrt mit weißen Knoten und wir müssen auch zeigen,
wenn es so einen Pfad mit weißen Knoten gibt,
dann wird U auch MWV auch ein Nachfahre von U.
Es sind so beide Richtungen betrachten.
Zuerst mal wieder
die Intuition
Das Ganzen stellen sich einfach vor, wir haben mal so
eine Tiefensuche und wir sind an irgendeinem Knoten-U angekommen,
den haben wir jetzt, den betrachten wir jetzt.
Hier ist unser Knoten-U und wenn wir jetzt in der Tiefensuche weiterlaufen,
dann gibt es irgendwie eine, wird der Algorithmus weiterlaufen.
Irgendwann ist hier unser Knoten-V, den wir betrachten. Okay,
so, wenn die alle weiß sind, ja,
dann wissen wir schon, die Tiefensuche geht so vor,
Sie wird von dem Knoten aus den nächsten weißen Knoten besuchen.
Und dann macht sie mit dem Knoten weiter. Und dann
wird sie von da wieder den nächsten weißen Knoten besuchen.
Sie geht so schnell in die Tiefe wie möglich.
Und das bedeutet, sie wird irgendwie diesem Pfad folgen.
Irgendwie wird es, selbst wenn es hier noch andere gibt,
wird es irgendwie diesem Pfad folgen,
dann wird es den Knoten V für die tiefen Suche den Knoten V finden.
Und dann haben wir entsprechend diese Vorgänger-Nachfolger-Relation. Die entstehen muss.
Also das ist die Beziehung zwischen der Vorgänger-Nachfolger-Relation und den weißen Knoten
entlang des Pfades. Das ist eigentlich ganz natürlich,
das zu erwarten ist, dass die in einem solchen Verhältnis
stehen müssen
Okay, wenn man das ein bisschen formaler aufschreibt,
sieht das so aus,
also der erste Teil des Beweises ist V, ist Nachfahre von U.
Also wir wissen jetzt, wir haben so eine Teilbeziehung.
Das heißt, wir haben den Knoten U,
wir haben also eine Menge von Kanten und irgendwo hier
ist der Knoten V.
Das sind alles Baumkanten in unserem tiefen Suchwald
Wir wissen für alle Nachfahren von W,
SD von W, größer als D von U. Klar,
wir Klammerungstheorien, den haben wir jetzt als allererstes entdeckt.
Der hat die kleinste Discovery-Zeit. Das ist die minimale Zeit
. Und die anderen, die erkennen wir alle später.
Das heißt, die Discovery-Zeiten dieser anderen Knoten ist größer.
Deswegen müssen wir alle Knoten, die hier sind,
haben die Farbe weiß. Alle Knoten hier sind weiß.
Und weil die alle weiß sind, haben wir genau diesen weißen Pfad.
Das ist eigentlich völlig klar, dass der existieren muss.
Der zweite Teil ist ein bisschen aufwendiger
Den Beweis in der anderen Richtung führen wollen. Wir gehen jetzt
davon aus, es gibt so einen weißen Pfad. Ja,
wir haben also den Knoten U entdecken ihn und haben diesen Knoten,
diesen Pfad zu V und alle Knoten sind noch weiß.
Ja, wir wissen schon,
D von V ist größer als D von U. Ja,
klar, weil den Knoten entdecken wir zuerst.
Den haben wir noch nicht gesehen. So,
jetzt müssen wir zeigen, dass unser tiefen Suchalgorithmus dafür sorgt,
dass V ein Nachfahrer wird von U in dem Baum.
Was machen wir
Wir nehmen das Gegenteil an. Ja,
also sie sagen, V ist kein Nachfahrer von Urs.
Irgendeinem Grund sei das jetzt nicht der Fall. Dann betrachten wir
den Pfad und wir sagen mal, V sei der erste Knoten,
der kein Nachfahrer sei von U, der diese Eigenschaft hat.
Die Beweisstrategie kennen sie schon. Haben wir letztens schon gesehen. So,
ist also der erste Knoten und dann ist weh der Vorgänger.
Wie wäre dann also dieser hier? Hier gibt es zwar
so eine Kante, es gibt ja diesen weißen Pfad,
aber irgendwie wird der nicht nachfahren.
Ja, aus irgendeinem Grund. So, was gilt dann
? Die Finishing-Zeit von W ist kleiner gleich der Finishing-Zeit von
U,
Der wird zuerst entdeckt, dann kommt irgendwann mal der Knoten,
dann ist die Discovery-Zeit von W. Und dann geht das irgendwie
weiter und dann kommt die Finishing-Zeit von W, dann wird das Intervall geschlossen.
Und irgendwann kommen wir hier oben wieder an und dann kommt die Finishing-Zeit von U.
Also in der Reihenfolge werden die Zeiten dann gesetzt.
Wir wissen, hier gibt es aber eine Kante von WNAV,
weil da war ja der weiße Pfad. So,
das bedeutet, die Discovery-Zeit von V muss kleiner sein als
die Finishing-Zeit von W. Also wenn wir den Knoten W betrachten,
nachdem wir ihn entdeckt haben,
werden wir diese Kante anschauen,
hier runter zu diesem Knoten und dann werden wir
die Discovery-Zeit von diesem Knoten V setzen
Deswegen ist die kleiner als die Finishing-Zeit von W. Erstmal bearbeiten wir V,
bevor wir W abschließen, weil hier ja die Kante existiert. Okay,
so, dann wissen wir, DVU ist kleiner als DVN V,
DVN V ist aber kleiner als er von W. Ja,
und das ist kleiner gleich die F&U,
weil das wird ja erst abgeschlossen, wenn wir,
also U wird erst abgeschlossenes Intervall, werden W vollständig abgeschlossen ist.
Und dann können wir mit dem Klammerungstheorium sofort sehen,
dass wir einen Widerspruch haben,
das Intervall von U ist eine Obermenge des Intervalls von V. Ja,
und damit wissen wir,
nach dem Klammerungstheorium U ist ein Vorfahrer von V. Oder wenn wir es dann umdrehen,
V ist ein Nachfahrer von U und das ist ein Widerspruch und deswegen muss das so sein.
Also ganz einfach, bei der Tiefensuche funktioniert das so,
wenn sie irgendeinen Zeitpunkt angucken, sie betrachten einen Knoten,
der ist gerade grau, sie sehen eine Menge von Anagnoten,
die sind alle noch weiß.
Alle Knoten, die weiß sind und die sie mit einem
Pfad erreichen können von dem Knoten, den sie betrachten,
die werden alle Nachfolger im Baum, im DFS-Bahn.
Das ist ein wichtiges Theorien, das ist, es erscheint so relativ naheliegend,
dass es so sein muss, ist es auch.
Es ist aber für uns ein wichtiges Theorien,
was wir brauchen, um die Korrektheit von verschiedenen Algorithmen
zu zeigen
So, jetzt haben Sie wahrscheinlich schon bemerkt,
dass die Kanten in den Beispielen immer Buchstaben hatten hier.
Und zwar ist es so, dass diese Kanten
eine spezielle Struktur haben, die machen irgendwas Besonderes.
Und deswegen kann man die charakterisieren und ihnen diese eine Eigenschaft zuordnen.
Wenn wir mal dieses Beispiel nochmal anschauen, dann sehen wir
die rot unterlegten Kanten, das sind die Baumkanten gewesen.
Also die, die wir durchlaufen haben, um die Knoten zu entdecken.
So, und jetzt habe ich den gleichen Grafen mal anders dargestellt.
Und zwar habe ich den so dargestellt,
dass die Baumkanten immer von oben nach unten zeigen und sie
wirklich so Bäume sehen, so kleine zwei Bäume entstehen hier.
Und die anderen Kanten habe ich einfach wieder eingezeichnet,
sodass der Graf vollständig ist. Und jetzt sieht man,
dass diese Kanten eine spezielle Struktur haben.
Da gibt es zum Beispiel diese Kante hier,
die von S nach W. Und diese Kante von S nach W ist eine Art Abkürzung.
Also wenn wir den Baumkanten folgen, dann könnten wir
von S nach Z laufen und von Z nach W.
Und wir kamen aber auch eine direkte Kante von S nach W.
Das war so eine Art Abkürzung und diese Eigenschaft haben nicht
alle Kanten. Das sind nur spezielle Kanten wie diese hier
. Das ist sogar die einzige in dem Fall,
in diesem Beispiel.
Also diese Kante ist keine Abkürzung
Also es gibt keinen Weg entlang vom Baumkarten, den wir abkürzen mit
dieser Kante. Das ist eine sogenannte Vorwärtsskante, ja,
also Typ F für Forward, ja, das ist eine Vorwärtskante.
Wenn wir uns die anderen Kanten angucken,
dann ist die nächste sehr charakteristische, diese Kante mit dem Label B,
ja, das ist eine sogenannte Rückwärtskante, eine Backedge.
Diese Kante führt uns im Baum wieder zu einem Knoten,
der Vorgänger ist, also der weiter oben liegt.
Wir kommen also wieder ein Stück nach oben zurück, ja,
und hier würden wir von U wieder zurückkommen zu T,
das ist auch ein Vorgänger.
Auch diese Eigenschaft haben nicht alle Kanten,
also diese Kante, diese F-Kante hat diese Eigenschaft nicht.
Hier kommen wir nicht den Baum wieder weiter nach oben diese anderen Kanten,
die noch übrig bleiben haben, diese Eigenschaft auch nicht.
Okay, wenn wir die vorwärts und die Rückkanten markieren,
dann bleibt noch eine Menge übrig.
Alle die, die wir jetzt irgendwie noch nicht markiert haben.
Und das sind sogenannte Querkanten oder Kreuzkanten,
also im englischen sprachiger Literatur ließen sie meistens Cross-Edge.
Hier in der deutschen Übersetzung halt Querkante. Ich werde wahrscheinlich
ab und zu Kreuzkante sagen, es ist so in meinem Sprachgebrauch drin,
ist also dann immer das Gleiche. Die haben jetzt also einen C und wir sehen hier,
gibt es verschiedene von diesen Kreuzkanten und die sind einfach,
haben erstmal die Eigenschaft, sie sind nicht 1 bis 3,
das sind keine Baumkante, keine Rückkante, keine Vorwärtskante,
also der Rest, ja, und die sind Kreuzkanten,
ja,
die sind so wie visulianen in unserem DFS-Urwald.
Die gehen so kreuz und quer zwischen den Ästen unserer Bäume
, ja. Wenn man hier mal genau hinschaut,
dann gibt es streng genommen zwei unterschiedliche Arten von Kreuzkanten,
die werden aber nur nicht mehr unterschieden.
Sie sehen, es gibt die einen Kreuzkanten wie diese,
die laufen innerhalb des gleichen Baums zwischen zwei Ästen hin und her.
Und es gibt tatsächlich auch noch andere Kreuzkanten,
so wie diese hier, die laufen zwischen zwei unterschiedlichen Bäumen
Lang. Ja, also die gibt es auch noch.
Also streng genommen könnte man die jetzt auch noch unterscheiden,
das macht man aber nicht.
Also das, was strukturell für uns sehr wichtig sind,
sind Rückwärtskanten und Vorwärtskanten, die brauchen wir immer.
Das sind einfach unsere Abkürzungen hier und die Rückhanden sind die Sachen,
die uns sozusagen in den Baum wieder weiter nach vorne bringen.
So, was wir jetzt also machen können,
wir können jetzt unseren Tiefensuchalgorithmus erweitern,
sodass ja alle Kanten entsprechend markiert mit B,
Cs und Fs.
Also die Baumkanten sind sowieso klar, wir sind auch markieren,
aber die andere Markierungen können wir auch machen
Das werden wir jetzt gleich tun. Aber bevor ich das mache,
noch dieser eine Hinweis, der ganz wichtig ist,
man muss sich immer darüber im Klaren sein,
dass eine Tiefensuche nur, also dass die nicht wirklich deterministisch ist,
weil die Knotenreihenfolge ist immer, kann immer anders sein.
Also es ist nicht gesagt, dass wir mit Knoten S beginnen.
Wir hätten genauso gut mit Knoten Z anfangen können oder mit Y.
Wenn wir das machen, dann ändern sich die Eigenschaften dieser Kanten.
Die bleiben nicht gleich. Also wenn wir mit Z anfangen,
dann würde S die Kante von S nach Z definitiv klar keine Baumkante werden.
Die Eigenschaften ändern sich. Das heißt,
wir haben zwar, wenn der DFS durchgelaufen ist,
können wir jeder kannte eine Eigenschaft zuordnen und sagen,
du bist Rückkante, du bist Baumkante, du bist vorwärtskante.
Aber das ist keine Eigenschaft, der kannte im Graf per se.
Man kann nicht sagen, egal was wir tun,
das ist immer eine Rückkante oder so.
Das geht nicht. Und das muss einem immer klar sein.
Das ist wirklich nur ein Durchlauf des DFS,
der produziert diese Kantenlabel,
wenn der DFS in einer anderen Reihenfolge durch den Grafen gelaufen wäre,
dann wären auch die Label anders an den Kern
Okay, gut. So, wie können wir die jetzt erkennen?
Hier stehen die Regeln. Also wir werden jetzt einfach unsere Discovery und Finishing-Zeiten nehmen,
um zu erkennen, welche Kante, was ist.
Wir fangen an. Also wir gucken immer genau den Zeitpunkt an,
wo ein Knoten grau ist und wir dann die Kante von UNAV durchlaufen.
Also der DFS-Algorithmus steht jetzt gerade an Knoten.
U und sondiert die Kante von UNAV.
Das ist der Zeitpunkt, den wir betrachten. So,
und dann gucken müssen wir zu dem Zeitpunkt ist U immer grau.
Wir sind gerade in der Schleife, wo wir die Adjazenzste des Knoten USB betrachten.
Der ist auch garantiert grau. Okay, so,
jetzt gucken wir einfach, was für eine Farbe der Knotenfrau hat zu diesem Zeitpunkt.
Wenn der zu dem Zeitpunkt weiß es, dann haben wir eine Baumkante.
Hier ist das Beispiel, hier ist der Knoten-U,
wir betrachten die Kante von U nach V,
V ist in diesem Augenblick weiß und wir wissen,
aha, das muss eine Baumkante sein,
wir werden jetzt im nächsten Schritt diese Kante durchlaufen und mit Knoten V die tiefen Suche fortsetzen.
Das ist eine Warmkante. Okay, wenn der Knoten
V grau ist, dann haben wir eine Rückkante.
Das Beispiel ist in diesem Bild dargestellt,
hier sehen wir eine Rückkante,
die hat den Label
Eine Backedge. Also immer, wenn der Knoten,
wo wir hinzeigen, grau ist, dann bedeutet das ja,
der ist noch auf unserem Stapel, auf unserem Rekursionsstapel.
Das heißt, den haben wir noch nicht vollständig abgearbeitet und wir sind
quasi einem weißen Pfad gefolgt, um zur Uhr zu kommen.
Irgendwann werden wir hier wieder hochlaufen und dann wird dieser Knoten V
auch mal irgendwann schwarz. Jetzt ist er grau, das heißt,
es gibt einen Pfad von Baumkanten, von V runter nach U.
Und das bedeutet, das muss eine Rückhande sein.
Also wir müssen uns nur die Farbe angucken von dem anderen Knoten,
wenn die grau ist, Ist eine Rückkante.
Okay, also weißes Baumkante, grau ist Rückkante.
Dummerweise haben wir jetzt nur noch eine Farbe, schwarz.
Und wir müssen noch die Vorwärts- und die Kreuzkanten unterscheiden.
Also wir können jetzt nicht, das ist jetzt nicht so
ein einfaches Kriterium. Jetzt müssen wir nochmal genau gucken,
wie wir vorwärts und die Kreuz- oder Querkanten unterscheiden können.
Woran man das sehen kann, ist an den Discovery-Zeiten.
Also wenn wir Fall 3, wir sind bei dem Knoten,
uh, wir betrachten die Kante zu Knoten V hier runter.
Das ist offensichtlich, kann man am Bild sofort sehen,
eine Vorwärtskante. Also hier ist der Baum,
Also die roten Kanten sind der Baum und hier können wir runterlaufen,
das ist also eine Abkürzung, ist eine Vorwärtskante.
Die Regel ist, wenn die Discovery-Zeit von U1 kleiner ist,
als die Discovery-Zeit von V4, dann ist es immer eine Vorwärtskante.
Das prüfen wir, was können wir auch sofort erkennen,
Theorien 227, ist unser Klammerungstheorien. Wir wissen dann nämlich,
DVU ist kleiner als D von V und dann kommt als nächstes die,
und die Finishing-Zeit von U, die ist später,
weil der ist ja noch grau.
Das heißt, die Finishing-Zeit von V, die liegt da drin,
wir haben das eine Intervall V enthalten in dem von U und dann wissen wir sofort,
dass U ein Vorfahre von VS oder Frau ein Nachfahre von U.
Und dann muss das ja eine Vorwärtskante sein. Okay,
bleibt einfach nur noch der vierte Fall,
wenn die Discovery-Zeit von U größer ist, als die Discovery-Zeit von V.
Das ist hier dargestellt. Hier ist U,
hier oben rechts in der Ecke, vierter Fall.
Und hier sehen wir einen Fall,
da ist die Discovery-Zeit 9 und hier gucken wir auf einen Knoten
vollständig abgearbeitet und die Discovery-Zeit ist kleiner.
Das heißt, den haben wir schon rings abgehakt.
Wenn das der Fall ist, dann wissen wir mit dem Klammerungstheorien,
dass U und V nicht in einem Vorgänger-Nachfolger-Verhältnis zueinander stehen.
Und das ist die Eigenschaft einer
Kreuz- oder Querkante
Ja. Okay, das heißt, wir können ganz leicht
an der Farbe und an der Discovery-Zeit erkennen, welchen Kantentyp wir vor uns haben.
So, ich habe den Pseudo-Code jetzt ergänzt, dass wir das machen. Wir haben jetzt also einen Array,
das heißt Type, was wir jetzt füllen,
während der Algorithmus läuft und das, da schreiben wir,
das wird indiziert mit einer Kante und da steht dann entweder Tree,
Backforward oder Crossdrin, je nachdem, ob es eine Baumkante,
eine Rückhante, eine Vorwärtskante oder eine Kreuzkante ist.
Das ist nur noch die Übersetzung von den Regeln,
die ich Ihnen gerade gesagt habe, also hier sehen wir der Knoten,
wir sind bei der Sondierung des Knotens U und gucken uns die Addizenzliste an,
wir betrachten einen Knoten V und zu dem Zeitpunkt entscheiden wir,
was wir tun und wir sehen, aha,
der ist weiß, wenn der weiß ist,
wissen wir, wir haben eine Baumkante,
das können wir markieren und dann laufen wir in DFS Visit rein.
Anderenfalls gucken wir, ob die Farbe grau ist,
wenn die Farbe grau ist, wissen wir, aha,
das ist eine Rückkante und dann tragen wir hier entsprechend Rück,
also Back ein und anderenfalls schauen wir,
ob die Discovery-Zeit U kleiner ist als die von V,
dann wissen wir,
dass wir eine Vorwärtskante haben und sonst haben wir eine
rückwärts Kante
Achten Sie, wenn Sie diesen Pseudo-Code angucken, die Bedeutung der Els-Anweisung hier.
Wenn das Elster nicht stehen würde, würde das nicht funktionieren.
Denn betrachten wir einfach nur hier mal die Tatsache der Rückhanden,
wenn hier das Els nicht wäre,
würden sie nie eine Rückhande entdecken,
weil sie würden hier V betrachten.
Sie sehen V ist weiß, dann gehen sie hier rein in den Algorithmus.
Hier wird er vollständig abgearbeitet und wenn sie hier rauskommen,
ist der Knoten schon schwarz.
Und dann können sie das nicht mehr erkennen.
Das heißt übrigens wichtig,
dass die Els hier stehen
Okay, am Ende wird der Knoten schwarz, Finishing-Zeit wird gesetzt. Okay,
damit können wir also diese Kanten zuordnen und wir werden jetzt im Folgenden
noch dringend benötigen, diese Kanten-Label, spezielle Kanten davon jedenfalls.
So, so eine Tiefensuche können wir natürlich auch anwenden auf ungerichteten Grafen. Ja,
wir haben das jetzt auf Gerichteten gemacht,
wir werden es gleich auch sofort wieder auf Gerichteten tun und Probleme auf gerichteten Grafen lösen,
aber der Vollständigkeit halber einmal natürlich die Betrachtung,
was passiert bei ungerichteten Grafen,
wenn ich also an jeder Stelle immer die Kante von UNAV und von V nach UH habe.
Da stellt man fest, dass Es ist nicht mehr alle Kantentypen gibt.
Die können nicht mehr auftreten, weil wir für jede Kante
auch immer die Kante in umgekehrter Richtung haben. Und was wir sehen können,
ist, also die Kante wird immer zweimal betrachtet,
einmal von UNAV und einmal von V nach U. Wir gehen davon aus,
dass wir die Klassifikation machen,
wenn wir die Kante das erste Mal betrachten.
Und dann gibt es nur noch zwei Arten von Kanten,
nämlich Baumkanten, die gibt es immer, wo wir die Rekursion durchlaufen.
Und dann gibt es noch Rückkanten. Die anderen Kantentypen können nicht
mehr existieren. Warum? Also wir haben die Kante von UNAV
und dann wissen wir, gehen wir mal davon aus,
wir betrachten erst die von UNAV und dann die von V
So, wenn die Kante in Richtung UV eine Baumkante ist,
dann wäre die in Richtung VU eine Rückkante.
Wir sagen, wir betrachten nur die Kanten zum Zeitpunkt,
wenn wir von U nach V laufen.
Wenn das eine Rückkante ist, dann wäre die in umgekehrter
Richtung eine Vorwärtskante. Das zeigt einfach, wir können,
wir müssen uns entscheiden, wann wir die Kante klassifizieren.
Und wir sagen, wir machen das beim ersten Zeitpunkt,
also wir betrachten uns UV, den Zeitpunkt,
wo wir von UNAV laufen.
So, was ist denn jetzt, wenn,
wenn die Kante von UNAV eine Vorwärts- oder eine Kreuzkante ist
In dem Fall wäre V ja schwarz. Also der KnotenV wäre schwarz,
der wäre ja schon vollständig abgearbeitet. Das würde aber bedeuten,
dass wir die Kanten von V ja alle schon betrachtet haben.
Das heißt, insbesondere haben wir die Kante von V nach U
schon betrachtet. Und wenn wir die schon betrachtet haben,
dann würde das ja dem widersprechen, dass wir sagen,
dass wir erst die von U nach V angucken und dann die von V nach U. Das heißt,
wir finden überhaupt keine Kante, die wir,
wenn wir sie das zweite Mal durchlaufen,
uns zu einem schwarzen Knoten führt.
Entschuldigung, wenn wir sie das erste Mal durchlaufen zu einem
schwarzen Knoten führt
Okay, gut, das bedeutet, wenn wir einen ungerichteten Grafen haben,
dann gibt es in dem Grafen nur Baum und Rückkannten.
Die schwierigeren Fälle von Vorwärts- und Kreuzkanten treten nur auf in gerichteten Grafen.
Okay, so, zurück zu den gerichteten Grafen.
Ich habe Ihnen schon gesagt, wir werden heute hochrelevante Probleme lösen.
Dieses hier haben Sie wahrscheinlich jeden Morgen. Wir versuchen eine
topologische Sortierung herzustellen. Eine topologische Sortierung funktioniert wie folgt.
Sie haben Abhängigkeiten zwischen Knoten, die werden dargestellt durch gerichtete Kanten.
Hier haben wir unser Bekleidungsproblem und es gibt die diverse Abhängigkeiten,
die sie gleich sofort wieder anzweifeln dürfen.
Wir werden jetzt als gegeben hinnehmen, ja. Also die Abhängigkeit ist,
dass sie ihre Hose anziehen werden,
erst nachdem sie ihre Unterhose angezogen haben und auch die Schuhe erst anziehen,
nachdem sie die Hose und vorher noch die Unterhose angezogen haben, ja.
Das heißt, es gibt Abhängigkeiten, nicht alles hängt von allem ab.
Die Uhr können sie umtun, wann sie wollen.
Ich gehe mal auf aus einer Armbanduhr.
Und ja, und andere Sachen sind nicht so wichtig.
Also auch Socken haben zwar ein Verhältnis zu den Schuhen,
aber wir haben keine Relation zwischen den Socken und der Hose.
Das können sie machen, wie sie wollen. Okay,
so, das heißt,
es entsteht ein Abhängigkeitsgraf zwischen diesen Knoten und die große Herausforderung ist jetzt,
die eine Reihung zu finden, die mit diesen Abhängigkeiten kompatibel ist.
So eine Sortierung in einem Grafen, also eine Sortierung der Knoten
, nennen wir eine topologische Sortierung in dem Grafen
Topologische Sortierung hier an diesem kleinen Spielbeispiel, ist ein extrem wichtiges Problem.
Also bei allen Arten von betriebswirtschaftlichen Prozessen haben wir Abhängigkeiten,
betriebswirtschaftliche Prozesse, Produktionsprozesse insbesondere. Da haben sie Abhängigkeiten,
wenn sie Autos bauen oder Flugzeuge oder sonst was,
bei Logistikproblemen haben sie diese Abhängigkeiten, dass sie sagen,
sie müssen erst den Container A entladen,
bevor sie Container B entladen,
solche Dinge.
Und sie müssen eine Reihung finden. Und immer,
wenn wir also solche Systeme haben,
müssen wir
dieses topologische Sortierungsproblem lösen
Okay, das sollte genug Motivation sein, selbst wenn Sie sagen,
dass Sie in der Lage sind, sich alleine anzuziehen.
Gut, also, was ist eine topologische Sortierung,
eine topologische Sortierung ist für uns folgendes. Wir haben also einen Graf,
das ist ein gerichteter Graf und wir suchen eine bieaktive Funktion.
Pi, die den Knoten, Nummern zuordnet,
und zwar von eins bis Betrag von V, die Anzahl der Knoten,
die wir haben. Und diese Nummerierung soll so sein,
dass für alle Kanten in dem Graf, die wir haben,
Pi von V immer kleiner ist als Pi von W,
also Piece, unsere Reihung, genau wie beim Sortieren,
da hieß das auch immer Pi, so bieaktive Abbildung,
die sagt, was das kommt als erstes,
was kommt als Letztes.
Und die Kanten dürfen immer nur in eine Richtung verlaufen,
dass diese Ordnung berücksichtigt wird,
also Pi von V kleiner ist als Pi von V. Okay,
so, da stellst du natürlich die erste Frage, geht das immer?
Ja, offensichtlich geht das nicht immer. Und zwar geht es dann nicht,
wenn unser Graf eins Kreis oder ein Zyklus hat. Ja,
weil wenn er einen Kreis hat,
wenn wir so eine zyklische Abhängigkeit hätten zwischen den Objekten,
das kann ich hier natürlich nicht reinkonstruieren,
sonst hätten wir ein echtes Problem,
dann dann wird man das nicht lösen können.
Ja, und das ist die erste Eigenschaft, die wir sehen müssen.
Es geht nur in speziellen gerichteten Grafen, nämlich gerichtete Grafen ohne Kreise.
Diese gerichteten Grafen ohne Kreise haben einen speziellen Namen,
habe ich auch schon mal erwähnt, das sind sogenannte Decks,
Decks ist eine Abkürzung und steht für Directed Accycly Graph.
Also die sind, das sind die Grafen,
auf denen wir entsprechend eine topologische Sortierung durchführen können. Okay,
so, das erste, was wir also zeigen müssen,
ist, dass das so ist, das heißt,
wir müssen zeigen, aha, also wir haben jetzt einen Graph,
einen gerichteten Graph und für diesen Graphen existiert eine topologische logische Sortierung,
genau dann, wenn G keine Kreise enthält, also ein Deck ist.
Ist auch wieder ein genau dann, wenn. Wir müssen also zeigen,
es existiert eine popologische Sortierung, daraus folgt,
er hat keine Kreise und wir müssen zeigen, er hat keine Kreise,
daraus folgt, es gibt eine topologische Sortierung in beiden Richtungen.
Okay, der erste Teil ist wieder einfach, mal angenommen,
es gibt eine topologische Sortierung und das sei dann Pi und
wir hätten jetzt tatsächlich so einen Kreis oder Zyklus,
der wir uns von V1,
so V2,
so V3,
dann irgendwann zu VR und
dann wieder zu V1 führen würde
Dann wissen wir, aha, alle Kanten entlang in dem Graphen,
die folgen ja der kleineren Relation bezüglich der topologischen Sortierung. Das heißt,
B von V1 muss kleiner sein als Pi von V2,
es gibt ja eine Kante von V1 nach V2,
Pi von V2, muss kleiner sein als Pi von V3,
weil es gibt ja eine Kante zwischen V2 und V3 und so weiter.
Und jetzt haben wir einen Kreis, das heißt,
am Ende ist das Pi von VR das gleiche VP von V1,
weil V1 oder der letzte Knoten des Zyklus hier wieder der erste ist.
Und dann haben wir natürlich einen Widerspruch,
wir können das gar nicht sortieren,
das geht nicht
Okay, das ist klar. So, aber gibt es tatsächlich immer
eine topologische Sortierung, sobald wir keine Zyklen mehr haben?
Vielleicht könnte es irgendwie ein Geflecht von Kanten geben,
was wir trotzdem irgendwie nicht in eine Reihung bringen können.
Das ist jetzt der etwas schwierigere Beweis. Und da gehen wir
wie folgt vor. Wir werden diesen Beweis algorithmisch führen.
Wir werden also einen Algorithmus angeben, der uns hilft,
zu zeigen, dass diese topologische Sortierung existiert.
Es ist ein Novum. Bisher haben wir immer Beweise geführt,
um die Korrektheit eines Algorithmus zu zeigen.
Jetzt benutzt man Algorithmus, um die Korrektheit einer Aussage
zu zeigen
Gut, wie machen wir das? Was wir erst mal zeigen müssen,
ist, dass wenn wir einen Dack haben,
einen gerichteten arzyklischen Grafen,
dann muss es einen Knoten U geben mit der Eigenschaft,
dass er ein Indigreenol hat.
Also das heißt, er hat keine eingehenden Kanten.
Der kann auch ausgehende Kanten haben, aber er hat keine, die da reingehen.
Irgendwo in dem Grafen muss es diesen Knoten geben.
Mindestens einen davon. So, wie kann man das machen?
Wie kann man das sehen, dass das so ist?
Aber das sehen wir wie folgt. Wir bilden den
transponierten Graf, ein transponierter Graf entsteht,
indem sie
die Kanten einfach alle umdrehen
Knotenmenge ist gleich und die Kante, die vorher von UNA
fauge, die geht dann von V und nach oben.
Sie drehen die einfach alle um. So, den Graf,
den betrachten wir und jetzt benutzen wir einen Algorithmus.
Der Algorithmus heißt Search Source, also wir suchen eine Quelle.
Eine Quelle ist so ein Knoten, da werden wir hinlaufen
und wir können von da uns nicht weiter bewegen.
Das ist so, als wenn sie im Fluss immer gegen
die Flussrichtung entlanglaufen und dann suchen sie die Quelle,
nämlich den Punkt, von dem aus sie nicht weitergehen können.
Unser Algorithmus, der funktioniert, ist ganz einfach,
der funktioniert wie folgt. Wir suchen uns einen ganz beliebigen
Knoten aus, irgendeinen,
völlig egal welchen
Dann betrachten wir die Addiatienzliste dieses Knotens. Wir nehmen,
beliebig einfach die erste Kante, die wir haben.
Das ist, wie gesagt, der transponierte Graf.
Deswegen, da steht dir das T, so die Addiatienzliste,
nur alles umgedreht. Und dann nehmen wir diese Kante,
die geht von V nach W und dann sagen wir,
okay, jetzt betrachten wir weh. Und dann,
ne, dann wird V wird zu weh,
wir machen diese Wildschleife weiter, wir gehen wieder die Addizenzliste,
suchen uns die erste beliebige Kante,
die wir finden und dann gehen wir da wieder hin.
Okay, und das machen wir immer so weiter.
Und irgendwann würde diese Wildschleife terminieren. Und die Behauptung ist,
wenn die dann terminiert,
dann haben wir offensichtlich einen Knoten mit einer leeren Addizenzliste,
ist ja diese Bedingung
nicht mehr erfüllt hier
Ja, und das ist ja,
also da wäre dann entsprechend der,
da wäre jetzt der Outigree, also die ausgehenden Kanten wären leer,
aber wir haben ja auch alle Kanten umgedreht.
Das heißt, dieser Genoten hat keine eingehenden Kanten.
Der hätte genau die Eigenschaft, die wir brauchen. Das heißt,
dass dieser Algorithmus uns einen Knoten liefert mit Eingangsgrad 0,
das ist klar. Das einzige, was man noch zeigen muss,
ist, dass der auch irgendwann mal terminiert.
Also dass er irgendwann auch aufhört. Sonst würden wir einfach
unendlich lange warten darauf, dass der uns irgendwas sagt.
Dass der endet, ist leicht zu zeigen
Denn wir wissen, dass entlang dieser Sequenz von Knoten,
die wir betrachten, jeder Knoten nur einmal auftreten kann.
Warum kann er nur einmal auftreten? Angenommen, der würde ein zweites Mal auftreten,
dann hätten wir einen Zyklus gefunden, einen Kreis.
Wir werden dann irgendwie bei V losgegangen, werden dann irgendwie,
ja, hätten dann mit so einem zufälligen Bewegungsablauf,
wären wir jetzt von V zu anderen Knoten gegangen.
Die hatten irgendwelche Knoten gesehen. Und die Aussage ist,
dass dieser Pfad hier jeden Knoten nur einmal enthält,
mal angenommen, hier würden wir einen Knoten weh nochmal sehen
Ja, wir kämen jetzt nochmal weh.
Das wäre ja der Gleiche dann wie dieser hier.
Und dann hätten wir einen Zyklus. Und wir wissen,
wir haben einen Graf ohne Zyklen. Und das war ja
die Bedingung, gehst ein Dag, ein Graf ohne Zyklen.
Das weiß dieser Algorithmus hier terminiert und der liefert uns eine,
eine Quelle. Gut, damit haben wir gezeigt,
jeder Graf hat mindestens eine von diesen Quellen.
Und hier steht der Begriff nochmal. Also diese Knoten heißen Quellen,
also die, die keine eingehende Kante heißen,
heißen Quellen und die,
die keine ausgehender heißen,
die heißen senken
Ja, das ist einfach ein Begriff. So,
jetzt haben wir aber noch nicht gezeigt, dass unser Satz korrekt ist,
dass es immer so eine topologische Sortierung geben muss.
Wie kann man das jetzt sehen? Wir machen einen Induktionsbeweis über
die Größe des Graphen. Ja, wir sagen, okay,
wir haben also eine gewisse Anzahl von Knoten und unser Beweis wird so laufen,
wir machen eine Induktion über die Anzahl der Knoten.
Am Anfang haben wir vielleicht einen Graph mit nur einem Knoten,
das ist der Induktionsanfang, Betrag von VWR 1,
PIV und V ist eins,
das ist jetzt unsere topologische Sortierung,
ein Graph mit einem Knoten hat immer eine topologische Sortierung
Okay, gehen wir mal davon aus, er hat keine Schleifen.
Dann ist es wieder schwierig, aber wenn der die nicht hat,
hat er eine topologische Sortierung. Eine Schleifhat wäre auch kein Deck.
Okay, also, das ist der einfache Fall.
So, jetzt haben wir ein beliebig großen Grafen.
Wir haben einen großen Grafen und in dem großen Grafen,
der hat jetzt Betrag von VfLe Knoten und innerhalb dieses Grafen
wissen wir, dass es so eine Quelle geben muss.
Also, das ist alles unser Graf,
ganz viele Knoten, das ist G,
also hier sind überall Knoten und Kanten drin
So, innerhalb dieses Graphen gibt es offensichtlich eine Quelle. Das machen wir,
indem wir alle kalten Kanten umdrehen und wir folgen dann entsprechend zum Pfad zurück,
ja, irgendwie in dem Graphen und finden dann einen Knoten,
der die Eigenschaft hat, dass aus diesem Knoten nur Kanten herausgehen.
Dass dieser Knoten existiert, das wissen wir. Und den Knoten,
den nennen wir hier U. Ja, das ist unser Knoten-U.
Das ist unsere Quelle. Was wir jetzt machen, ist,
wir nehmen einfach mal einen neuen Graphen-V-Strich, der diesen Knoten-U nicht enthält.
Ja, also unser neuer Graph-G-Strich, der hat einfach diesen Knoten nicht.
Und das, was dann rauskommt, ist der neue Graf G-Strich.
Ein Knoten weniger. Und zwar diesen Knoten hat er weniger.
Okay, so, was machen wir? Der Knoten hat einen,
der Graf hat einen Knoten weniger,
nach Induktionsvoraussetzungen existiert eine topologische Sortierung, die nennen wir mal P-Strich.
So, und jetzt konstruieren wir die topologische Sortierung. P,
was wir einfach machen, ist, wir sagen,
okay, also das P von V,
für alle Knoten hier in diesem Grafen hier, ja,
das setzen wir auf eins.
Wenn das gerade der Knoten U ist, also der hier,
der kriegt Den Label 1. Damit können wir nichts verkehrt machen,
weil da geht ja keine Kante rein. Also wenn der die kleinste Nummer kriegt,
können wir schon mal zumindest hier lokal keine Eigenschaften verletzen,
weil alle kannten, die hier rausgehen, hätten,
würden zu den Knoten gehen, die hätten dann ja größere Nummern,
weil die Nummerierung beginnt ja mit eins und dann kann nichts passieren.
Und was machen wir sonst? Also wenn das nicht so ein Knoten ist,
dann nehmen wir die Nummer eins plus P-Strich von V,
also die topologische Sortierung, die wir vorher hatten.
Also für zwei beliebige Knoten hier in unseren Grafen,
die will ich jetzt nicht Keine Ahnung,
nenne ich jetzt X und Y, sagen wir einfach,
okay, die hatten ja eine topologische Sortierung,
P-Strich von X und P-Strich von Y. Und denen gebe ich jetzt eine neue und sage einfach,
okay,
das P von X ist P-Strich von X plus 1 und das Pi von Y ist dann nichts weiter,
als das Pi-Strich von Y plus 1. Mache ich einfach größer.
Okay, diese Kante hier, die hat in G-Strich auch schon existiert,
weil die in Gestrich existiert hat, folgt, ja,
die topologische Sortierung,
die befolgt ja diese Kante
Das heißt, wir wissen, wie Strich von X muss kleiner sein als P-Strich von Y.
Und wenn wir auf beiden Seiten eins addieren,
dann ist das natürlich auch immer, immer noch der Fall, dass das kleiner ist. So,
okay, und damit wissen wir schon,
dass wir eine korrekte topologische Sortierung haben,
wenn die Kante von U nach V verläuft,
also hier diese Quelle verlässt, dann wissen wir,
dass P von U gleich eins ist und das ist kleiner als eins plus irgendwie eine Zahl,
die größer ist als eins aus der alten topologischen Sortierung und dann erfüllen wir die topologische Sortierungseigenschaft.
Wenn wir eine beliebige andere Kante haben zwischen V und W,
bei mir heißen die in dem Bild X und Y,
Dann gilt, dass wir vorher die topologische Sortierung berücksichtigt,
dass wir vorher eine korrekte topologische Sortierung hatten,
also P-Strich von V war kleiner als wie Strich von V,
wenn wir auf beiden Seiten eins addieren.
Es ist immer noch der Fall. Und dann wissen wir,
dass P, eine korrekte topologische Sortierung ist.
Es gibt keine einzige Kante, die in den Knoten U hineingeht.
Also können wir an der Stelle keine Eigenschaft verletzen. Okay,
also wir haben gesehen, sobald ein Graf keine Zyklen hat,
dann können wir ihn topologisch sortieren.
Dann können wir die Knotenmenge topologisch sortieren
Okay, wie machen wir es denn jetzt? Jetzt brauchen wir ja noch,
jetzt wissen wir nur, dass es geht,
aber wir haben noch keinen Algorithmus, der das tut.
So einem so ein Element haben sie gerade gesehen, wie man vorgehen könnte.
Ich könnte zum Beispiel in dem Grafen sagen,
ja super, ich suche mir so eine Quelle,
die Quelle kriegt die Nummer, die erste Nummer eins,
nehmen sie die weg, suchen sich wieder eine Quelle,
geben sie die Nummer zwei, nehmen sie weg,
suchen sich wieder eine Quelle,
geben sie die Nummer drei und so weiter.
Dann kriegen sie eine korrekte topologologische Sortierung. Ist aber kein schöner Algorithmus,
weil der Algorithmus hätte ja quadratische Laufzeit,
weil im ungünstigsten Fall laufen sie immer hier so willkürlich
durch den ganzen Graf,
so sollte man das
natürlich nicht machen
Es gibt einen sehr schönen Algorithmus,
der ist in den Jahrzeit, macht der so ähnlich funktioniert,
den werde ich ihn jetzt hier nicht zeigen,
aber den können sie sich wahrscheinlich auch leicht selber herleiten,
was sie einfach machen ist.
Sie merken sich für jeden Knoten den Indigree.
Also wie viele Kanten gehen in den Knoten rein?
Das zählen sie einfach. Um sie einfach so ein Array,
da zählen sie das drin, da gehen fünf Kanten rein,
da gehen sieben Kanten rein.
Und wenn sie dann einen Knoten als Quelle identifiziert haben und rausnehmen,
betrachten sie die Addizenzliste und setzen die Zähler eins runter.
Und dann brauchst du sie immer nur gucken,
ob sie dabei irgendwo einen Knoten haben mit Indigree 0,
dann nehmen sie den als nächstes.
Und dann kriegt man einen sehr schönen Linearzeitalgorithmus für eine
topologische Sortierung
Das ist die eine Variante, wie man das machen kann.
Das ist, wie gesagt, ein sehr schöner Algorithmus.
Aber man kann es auch mit einer tiefen Suche machen.
Eine Tiefensuche gibt uns auch eine topologische Sortierung. Hier ist
unser Beispiel. Und jetzt müssen wir erst mal gucken.
Das Erste, was wir ja machen müssen, ist,
wir müssen erst mal zeigen, dass unsere Tiefensuche überhaupt ein Deck erkennt.
Weil wenn wir jetzt mit dem eine topologische Sortierung hinkriegen wollen,
dann müssen wir natürlich erst mal zeigen,
dass er mindestens schon mal die Fälle entdeckt,
eine typologische Sortierung existiert.
Das müssen wir als allererstes tun. Und das machen wir
wie folgt. Wir können ja unsere Kanten labeln.
Und das, was wir sehen können, ist,
dass es einfach nur von den Rückhanden abhängt.
Die Rückhanden entscheiden darüber, ob wir einen Dag haben oder keinen Deck.
Und es ist auch ganz naheliegend, ich sage ihnen nochmal,
das alte Beispiel, wo ist es?
Hier, da waren die Kanten markiert. Also sie haben
die Baumkanten, die Baumkanten erzeugen überhaupt keinen Zyklus.
Die können keinen, also sie können gar keinen Zyklus erzeugen.
Selbst wenn man die Richtung nicht beachtet,
das ist ja ein Baum oder ein Wald
Die Vorwärtskanten erzeugen auch keinen Zyklus, weil die Vorwärtskanten kürzen ja,
also die Vorwärtskanten gemeinsam mit dem Baumkanten erzeugen keinen Zyklus,
weil sie kürzen ja nur ab.
Das heißt, sie erzeugen keinen Möglichkeit, hier einen Kreislauf zu bilden.
Die Rückhanden erzeugen tatsächlich einen Zyklus. Wir gehen die Baumkanten
lang und dann die Rückkante wieder hoch und haben einen Zyklus.
Also Rückhanden haben offensichtlich was mit Zyklen zu tun.
Die Frage ist, was ist mit den Kreuzkanten?
Und die Kreuzkanten erzeugen auch keinen Zyklus. Also das sehen
sie hier in dem Beispiel sofort. Also kein dieser Kreuzkanten
bildet zusammen mit den mit den Baumkanten hier einen Zyklus.
Sie können natürlich einen Zyklus bilden mit einer Kreuzkante.
Sie können zum Beispiel hier lang laufen, die Rückkante und dann hier wieder lang.
Aber egal, wie sie es machen, jeder Zyklus,
den sie konstruieren, enthält mindestens eine Rückkante.
Das ist das, was das folgende Lämmer besagt. Okay,
das besagt, okay, wir haben einen ungerichteten Grafen, das ist komisch.
Wir haben einen gerichteten Grafen, das ist ein Tippfehler.
Einen gerichteten Grafen G und das ist ein Back,
genau dann, wenn er ein DFS-Durchlauf,
wenn in einem DSRFs durchläuft,
keine Rückkante detektiert wird.
Das ist die Eigenschaft
Okay, Teil 1. Wieder ist es eine genaue dann Van-Beziehung.
Das heißt, wir haben ein Deck, dann gibt es keine Rückkante.
Teil 2 ist, wir haben keine Rückkante, dann ist es ein Deck.
Also Teil 1, G ist ein Deck.
Und angenommen, wir haben eine Kante E und diese Kante
geht von UNAV und das ist eine Rückkante.
So, was ist dann passiert? Also wir haben,
wir gucken entlang dieses Zyklus, wir kommen irgendwie in diesen,
Entschuldigung im Zug, das kennen wir ja noch gar nicht.
Wir gucken uns die Kante an, diese Rückkante an.
Wir wissen, V ist ein Vorgänger von
Und dann gibt es entsprechend einen Pfad von V nach O.
Und wir haben dann die Kante von U
nach V. Und dann haben wir einen Zyklus.
Also die Baumkanten bilden einen Pfad von U nach V und
die Rückkante schließt diesem Pfad zu einem Zyklus.
Also das heißt, wenn eine Rückkante da ist,
haben wir auch keinen Deck. So, mal angenommen,
wir haben einen Zyklus. Jetzt müssen wir zeigen,
dass dann auf alle Fälle eine Rückkante existiert,
dass wir die Zyklus nicht bilden können durch Baumkante und Vorwärtskante
und Kreuzkante
Wie machen wir das? Okay,
wir haben einen Zyklus C, der ist hier dargestellt,
das ist also eine ganze Reihe von Knoten,
die bilden so einen Zyklus und wir schauen uns den Knoten an,
den wir bei der tiefen Suche als erstes betrachten werden.
Wir laufen also jetzt irgendwie durch unseren Graf und dann sehen
wir diesen Knoten als ersten Knoten von diesem Zyklus.
Könnte auch der sein, ist egal, bei mir ist jetzt der hier.
Deswegen kriegt er hier so eine Eins da drin,
der Knoten wird jetzt grau und zum Zeitpunkt,
wo der grau wird,
sind die anderen Knoten des Zyklus ja alle noch weiß,
weil das ist ja der erste Knoten des Zyklus,
den wir betrachten
Die alle noch weiß. Und wir haben unser Theorien der weißen Pfade.
Jetzt wissen Sie, wofür wir das brauchen. Das bedeutet,
alle Knoten, die wir erreichen können,
das ist insbesondere dieser Zyklus hier,
werden alle Nachfahren von dem Knoten mit der Nummer 1. Wir werden alle nachfahren,
insbesondere der Knoten, er, hier, der letzte,
entlang des Zyklus, wird auch ein Nachfahre von dem Knoten 1.
Und wenn wir den hier betrachten, dann werden wir die Kante von EA
zu 1 sondieren betrachten und werden einen grauen Knoten sehen,
weil es ist ja ein Nachfahre von dem Bluten 1.
Und wenn wir einen grauen Knoten sehen,
haben wir
eine Rückkante erkannt
Deswegen muss es eine Rückkante geben. Oder aufgeschrieben,
also wenn C ein Zyklus ist,
V1 bis VR plus 1, was wiederum dem V1 erspricht.
Ein Zyklus und V1 ist der eher Knoten,
der zuerst in dem DFS-Durchlauf entdeckt wird.
Das ist der erste Knoten hier. Zum Zeitpunkt D
von V1 sind alle Knoten entlang des Zyklus weiß.
Nach dem Theorien der weißen Pfade werden alle Knotennachfolger vom V1,
wenn VR abgearbeitet wird, ist der Knoten V1 noch grau.
Und dann wird VR nach V1 eine Rückkante.
So, hier sehen Sie die Auflesung des Rätsels, ja,
oder zumindest eine Variante, wie das Ganze funktionieren könnte.
Es gibt viele verschiedene Lösungen, also topologische Sortierungen sind nicht eindeutig,
also in den meisten Fällen sind sie nicht eindeutig.
Und was sie sehen an dieser topologischen Sortierung ist,
alle kannten, gehen immer von links nach rechts,
ne, und das muss natürlich auch so sein,
weil nur wenn sie von links nach rechts gehen,
also es ist die Ordnung hier der Knoten, ne,
wenn sie von links nach rechts gehen,
erhalten sie ja die topologische Sortierung,
wie erfüllen sie die Bedingungen der topologischen Sortierung,
Und damit ist auch klar, dass natürlich keine Zyklen auftreten können.
Weil ein Zyklus würde ja bedeuten, eine Kante geht wieder in diese Richtung.
Okay, so, also, das ist eine gültige Lösung.
Wir können das sofort überprüfen, weil alle kannten von links nach rechts gehen.
Hier unten sehen Sie Nummern. Diese Nummern sind Discovery und
Finishing-Zeiten eines DFS-Durchlaufs. Und an denen können sie,
ist schon langsam ein geübtes Auge, haben schon einige Beispiele angeguckt,
können sie sofort sehen, wie dieser spezielle DFS gelaufen ist.
Sie sehen sofort, okay, angefangen haben wir aus
irgendeinem Grund mit dem Hemd. Und von dem Hemd aus
haben wir diese Kante sind wir dieser Kante gefolgt zur Krawatte.
Und dann dieser Kante zum Jackett, also eins,
zwei, drei. Hier haben wir keine ausgehende Kante,
also der kriegt, der hat die vier gekriegt,
Finishing Zeit 5. Dann wieder zurück zu dem Knoten.
Dann haben wir den Gürtel hier gesehen,
der hat die Discovery Zeit 6 Finishing Zeit 7 wieder zurückgegangen,
Finishing Zeit 8, mit irgendeinem beliebigen anderen Knoten weitergemacht.
Das war die Uhr, neun, Finishing Zeit 10,
weil es gibt gar keine Kante, ja,
mit irgendeinem Knoten weitergemacht, das war die Unterhose.
Sie sehen übrigens, das kommt,
dieses Beispiel kommt aus dem Bayerischen,
ja,
da redet man immer von Unterhosen
Okay, also die haben wir zuerst, Discovery,
Zeit 11, Hose 12, dann kommen die Schuhe,
13, das Finishing zahlt 14, 15,
16 und dann hauen wir die Socken.
Okay. Die Knoten stehen hier in einer speziellen Reihenfolge,
die sie den Nummern, von den Nummern ablesen können.
Ich weiß nicht, ob sie die schon gesehen haben.
Ja, die stehen hier nicht willkürlich,
sondern in einer speziellen Reihenfolge und zwar die Reihenfolge,
die wir haben,
ist die Reihenfolge fallender Finishing-Zeit
Ja? Also, hier sind wir 18, 16,
15, 14, 10, 8, 7,
5, 4. Ja, also Reihung ist die,
der auf einer Fallen, der fallenden Finishing-Zeit.
Und die Behauptung ist jetzt, dass wenn wir einen DFS-Durchlauf
machen durch den Graphen, Discovery in Finishing-Zeiten merken,
dass die Reihenfolge fallender Finishing-Zeiten immer eine topologische Sortierung der Knoten ist.
Okay, das werden wir gleich zeigen,
dass das so ist und hier zunächst mal der Algorithmus,
das rote und grüne ist das,
was den unseren Algorithmus hier von einem Standard-DFS unterscheidet.
So ein paar Sachen brauchen wir nicht. Wir müssen die
Uhr brauchen wir nicht. Wir brauchen auch nicht die Vorgängerrelation
. Das können wir alles streichen. Und das Einzige,
was wir machen müssen, ist, erstens,
wenn wir einen grauen Knoten sehen in der Sondierung,
dann müssen wir, wir haben eine Rückkante.
Dann haben wir ein Problem. Dann geben wir eine Fehlermeldung aus und sagen,
dass unser Graf G kein Dack ist oder sie können auch sagen,
dass er einen Zyklus hat und dass wir intopologisch nicht sortieren können.
Ansonsten machen wir nur Folgendes am Ende der Sondierung,
wenn der Knoten schwarz wird,
dann kriegt er ja normalerweise
seine Finishing-Zeit
Speichern wir gar nicht, sondern wir nehmen eine Liste und
schreiben da diesen Knoten U an den Anfang der Liste,
schreiben wir den da rein. Für die,
die das ausgedruckt hat, bei ihnen steht nur List Insert,
das ist nicht falsch, aber ich habe das,
habe hier noch Hatting geschrieben, um ganz sicher zu gehen,
dass sie wissen, dass man es vorne reinschreibt in die Liste,
das ist nämlich wichtig, dass man es vorne reinschreibt.
Okay, gut. So,
so machen wir das und dann ist offensichtlich klar,
in dieser Liste L hier, da stehen die Knoten,
weil wir immer vorne einfügen mit einer absteigenden Reihung bezüglich der Finishing-Zeiten.
Also je später wir ihn entdecken,
umso weiter vorne steht er in dieser Liste
Und jetzt müssen wir noch zeigen, dass das tatsächlich eine sopologische Sortierung ist.
Es ist die Korrektheit des Topological Sword-Algorithmus, so hieß der hier.
Es ist ein Tiefensuch-Algorithmus. Angenommen, G ist ein Deck,
dann berechnet Topological Logical Sword in L, also in dieser Liste,
eine topologische Sortierung der Knoten in V, die da vorkommt.
Okay, warum ist das so? Also wir haben gesehen,
L enthält alle Knoten in der Reihenfolge fallender Endzeiten,
also Finishing-Zeiten, F&U.
Das sieht man dem Algorithmus sofort an,
weil wir vorne in die Liste L einfügen
So, jetzt müssen wir zeigen, dass das einer topologischen Sortierung entspricht.
Also betrachten wir eine beliebige Kante, die geht von U
nach V. Und was wir zeigen, ist,
dass die Finishing-Zeit von U größer ist, als die Finishing-Zeit von V.
Und wenn das der Fall ist, dann wissen wir,
weil wir haben ja die Knoten sortiert nach fallenden Finishing-Zeiten,
dass wir tatsächlich eine topologische Sortierung gefunden haben.
Wir benutzen dazu einfach, dass Klammerungstheorien und überlegen uns,
was für Kantentypen gibt es denn.
Also mal gucken, die Kante von U nach V,
das könnte zum Beispiel
eine Baumkante sein
Wenn das eine Baumkante ist, dann würde ja,
wäre der Knoten V noch weiß und wenn wir von U nach V laufen,
würde die Discovery-Zeit von V gesetzt und danach die Finishing-Zeit von U. Das heißt,
V liegt zwischen uns und zwischen der Discovery in Finishing-Zeit von U,
ja,
die Finishing-Zeit von U ist dann offensichtlich größer als sie von V. Wir wissen ja,
die Intervalle dürfen sich nicht so überlappen.
Dann ist, wenn die Discovery-Zeit von V im Intervall-U ist,
dann ist die Finishing-Zeit auch in dem Intervall.
Das sagt uns das Klammerungstheorie. Wenn wir eine Vorwärtskante oder
eine Kreuzkante haben, dann ist der Knoten V
schon schwarz
Naja, wenn der schwarz ist, dann ist die Finishing-Zeit
dieses Knotens ja schon gesetzt. Und den Knoten U sondieren
wir ja gerade noch, der hat noch keine Finishing-Zeit.
Deswegen ist die Finishing-Zeit von U größer als die Finishing-Zeit von
V. Dritte Fall ist, wir haben eine Rückhande.
Bei der Rückhande hätten wir ein Problem, haben wir aber nicht,
weil wir wissen ja, wir haben ein Deck.
Also wir haben keine Rückhanden. Also ein gerichteter adzyklischer Graf
hat keine Rückhanden. Das heißt, dieser Fall tritt nicht
auf. Okay, damit haben wir gezeigt,
wenn wir die Knoten nur nach fallender Finishing-Zeit sortieren,
erhalten wir eine topologische Sortierung in dem Grafen.
Die erste sehr nützliche Anwendung der Tiefensuche
Gut. Jetzt kommen wir zu einem zweiten Problem,
was wir mit tiefen Suche lösen wollen.
Und das wird ehrlicherweise ein etwas schwierigerer Fall als das,
was wir gerade gesehen haben. Was wir machen wollen,
ist, wir wüssten gerne den starken Zusammenhang.
Starker Zusammenhang habe ich am Anfang mal einmal erwähnt,
was das ist. Nur noch mal zusammengefasst, also UV,
mit so einem Pfeil dazwischen, beschreibt jetzt im Folgenden die Pfadrelation.
Pfadrelation heißt, es gibt einen Pfad von UNAV.
Wir können bei U starten und uns im Graf fortbewegen und
V erreichen
Sagt dieser Pfeil hier. Und eine starke Zusammenhangskomponente ist die
maximal große Menge an Knoten,
also eine Teilmenge der Knoten, mit der Eigenschaft,
dass es immer Pfade gibt zwischen all diesen Knoten.
Also für alle Knoten, UV aus der Komponente gibt es
einen Pfad von UNAV und der Vollständigkeit halber auch ein Pfad von V nach U.
Also sie können jeden Knoten von jedem anderen aus erreichen.
Das ist eine starke Zusammenhangskomponente. Ja, warum interessieren uns die?
Naja, wenn sie so Pfadprobleme haben und wissen wollen,
wie Dinge miteinander zusammenhängen,
dann brauchen sie
starke Zusammenhangskomponenten
Wenn sie die Struktur von Freundschaftsnetzwerken in Facebook analysieren,
sollten sie sich für starke Zusammenhangskomponenten interessieren.
Weil sie haben vielleicht viele Pfade in Facebook,
keine Ahnung, alle sind Freunde von Angela Merkel,
aber sie haben nicht so viele Zusammenhangskomponenten.
Zusammenhangskomponenten sind wahrscheinlich ein Indiz für vielleicht...
Sie schütteln jetzt schon den Kopf. Für echte Freundschaften,
weil sie dann immer die Situation haben, dass UV kennt.
Und zumindest gibt es eine Kette von Personen,
so dass am Ende über diese Kette auch V wieder kennt.
Okay, könnte ein Grund sein, warum man sich für Zusammenhangskomponenten interessiert.
Es gibt viele Gründe und ich würde mal sagen,
der Hauptgrund ist ein struktureller.
Bei ganz vielen Algorithmen auf Grafen. Es gibt ja viele Probleme,
die man auf Grafen lösen kann. Ist es so,
diese Probleme werden häufig gelöst auf Zusammenhangskomponenten und dann für den Graf,
für den Rest des Grafen kann man die Lösung dann extrapolieren und kann den Rest machen.
Weil wenn man die Zusammenhangskomponenten rausgenommen hat, dann gibt es keine Zyklen mehr.
Wenn man die Zusammenhangskomponenten als eine Einheit betrachtet,
dann bleiben keine Zyklen übrig. Und Grafen ohne Zyklen,
sind immer viel einfacher, wenn wir Probleme lösen wollen.
Und effizienter in der Lösung. Wir können Probleme darauf effizienter
lösen. Deswegen suchen wir diesen starken Zusammenhang. Okay,
so, Strukturen von diesen starken Zusammenhangskomponenten. Es gibt drei
ganz wichtige Eigenschaften. Das erste ist, die Knotenmenge V,
die zerfällt in eine Menge des Jungter starker Zusammenhangskomponenten.
Also sie können die Menge, die Knotenmenge nehmen und sie können die zerlegen.
Und jeder Knoten gehört zu einer starken Zusammenhangskomponente.
Das ist ganz klar, dass das so sein muss.
Mal angenommen, das wäre nicht so.
Wir hatten zwei Komponenten,
C1 und
Und die hätten einen nicht leeren Schnitt. Also die Schnittmenge wäre ungleich
der leere Menge. Da gibt es also so einen Knoten-V,
der ist in der Schnittmenge. Das kann nicht sein,
weil wenn wir jetzt einen beliebigen Knoten-U aus C1 nehmen und
einen beliebigen anderen Knoten W aus C2,
dann gibt es ja einen Pfad von UNAV.
UNV sind ja in der gleichen Zusammenhangskomponente. Es gibt einen Pfad von V
nach W., weil die beiden in der gleichen Zusammenhangskomponente sind.
Und es gibt natürlich auch einen Pfad von WNAV,
weil die in der gleichen Zusammenhangskomponente sind und von V nach U. Das heißt,
jeder Knoten in C1 ist verbunden oder können wir erreichen von jedem Knoten in W und umgekehrt.
Und deswegen sind C1 und C2 auch die gleiche Zusammenhangskomponente
Okay, deswegen gibt es keine Knoten, die zu zwei starken Zusammenhangskomponenten gehören.
Die zweite Eigenschaft ist ein Graf und sein transponierter Graf,
also wenn wir alle Kanten umdrehen, haben die gleichen starken Zusammenhangskomponenten.
Wenn ich in einem Grafen alle Kanten umdrehe,
dann erinnert sich nichts an dem starken Zusammenhang, weil wenn es im Pfad,
wenn U und V in der gleichen starken Zusammenhangskomponente sind,
dann gibt es den Pfad von UNAV und es gibt den Pfad von vorn nach oben.
Okay. Gut, und drei ist,
wenn wir jetzt einen neuen Grafen bilden,
der hat einen Namen, das ist ein sogenannter Komponentengraf,
wo die Knoten die starken Zusammenhangskomponenten sind Und die Kanten bilden wir,
indem wir sagen, es gibt eine Kante zwischen C und D,
das ist ein gerichteter Graf, zwischen C und D,
genau dann,
wenn es in der Komponente C ein Knoten U gibt und in der Komponente D,
ein Knoten V und UV ist in der Kantenmenge.
Das ist ein Komponentengraf, ich zeige Ihnen gleich ein Beispiel.
Und dieser Komponentengraf, das ist die wichtige Eigenschaft 3,
ist ein Deck. Der hat keine Zyklen mehr. Der darf
gar keine Zyklen haben, weil wenn der Zyklen hat,
dann könnten Sie nämlich wieder zeigen,
dass diese Zusammenhangskomponenten eigentlich eine große Zusammenhangskomponente sind und der Beweis
ist genau analog zu denen,
die er hier steht
So, hier ist ein Beispiel, das werden wir auch
gleich noch sehen, wenn wir den Algorithmus weiter betrachten.
Für den starken Zusammenhang. Wir sehen hier acht Knoten,
die haben die Label von A bis H.
Und sie sehen hier die starken Zusammenhangskomponenten. Hier steht grau,
das liegt daran, weil sie das wahrscheinlich in schwarz-weiß ausdrucken,
wenn sie es ausgedruckt haben.
Das ist ja natürlich hier dieses Blau, hast du ja gemeint.
Okay, das sind also diese Flächen hier, das sind
die starken Zusammenhangskomponenten. Also das ist eine starke Zusammenhangskomponente,
beiden gehören zu einer, die beiden und der Knoten selber.
Hier gehört auch zu einer starken Zusammenhangskomponente, zu einer eigenen.
Und sie können das überprüfen, dass das so ist.
Sie sehen, also die gehören alle zusammen,
weil sie können von A nach B,
sie können auch von B über E zurück nach A.
Und das gilt für die anderen Knoten auch.
Sie können jeden Knoten von jeden erreichen. Das ist also
eine starke Zusammenhangskomponente. Das gilt für CD und für FG
auch. Naja, und H ist ein Einzelknoten.
So, und jetzt könnte man sich fragen, okay,
aber vielleicht müssen wir ja noch irgendwas zusammenfassen.
Aber sie sehen jetzt hier,
sie können zum Beispiel zwar von C aus G erreichen,
aber von dieser Komponente kommen sie nicht wieder zurück
Es gibt keine Kante, die sie hier wieder zurückführt.
Sie können zwar von der Komponente hier rüber,
aber hier kommen sie nicht von hier nach da.
Also offensichtlich geht das nicht in diesem Beispiel.
Wenn Sie das jetzt gerade hier noch lesen,
das habe ich gelöscht, weil wir es hier nicht brauchen.
Das ist noch von der anderen. Wir werden das Bild
nochmal sehen. Da stehen dann die Discovery in Finishing-Zeiten drin.
Okay, so, wir transponieren den Graf.
Transponieren bedeutet, alle Kanten werden umgedreht.
Die Kante wird irgendwo die, alle Kanten werden umgedreht.
Und wir sehen, der Zusammenhang an dem Zusammenhang ändert sich nichts.
Wenn es vorher einen Weg von A nach B gab und
von B nach A, wir drehen alle Kanten nun,
gibt es natürlich jetzt auch einen Weg von B
nach A und von A
Ist klar, das ändert sich nicht. Also alle kannten einfach umgedreht,
gleiche Zusammenhangskomponenten. Hier sehen Sie den Komponentengraf, dieses Grafen.
Wir sehen starke Zusammenhangskomponente hier, A, B und E,
Sterknoten hier haben wir die starke Zusammenhangskomponente aus C und D.
Hier F und G, das ist der Knoten und hier Haar.
Und hier gibt es natürlich noch Kanten und die Kanten verlaufen
entlang dem ursprünglichen Graf, das war ja der Transponierte.
Und sie sehen, es gibt zum Beispiel eine Kante,
die führt uns von A, B, E nach CD.
Das ist diese hier
Das entspricht dann der Erkannte hier oder von A, B,
E nach SG, da gibt es sogar zwei Kanten, die werden dann abgebildet auf diese Kante.
Dieser Graf hier ist ein Deck, der hat keine Zyklen mehr. Wenn der einen Zyklus hätte,
man angenommen, es gäbe, ja, so, so,
mal angenommen, diese Kante würde jetzt von FG nach ABE laufen,
hier so hoch, ne, dann wüssten sie,
sie könnten jeden Knoten aus AWE,
von dem können sie alle Knoten aus CD erreichen,
aber sie können auch von den Knoten aus CD,
die Knoten ABE erreichen,
wenn sie diese Kante hier entsprechend folgen.
Da können keine Zyklen mehr auftreten.
Das ist hier entsprechend
ein Deck
Ja. Okay, so, diese Eigenschaften werden wir nutzen,
wir werden jetzt eine Tiefensuche beschreiben,
die den transformierten Graf verwendet und auch diesen Komponentengrafen hier betrachten dabei.
Bevor ich Ihnen den Algorithmus zeige, möchte ich mal,
denken Sie mal eine Minute drüber, na,
vielleicht haben Sie das jetzt auch schon die ganze Zeit,
wie man das Problem mit einer tiefen Suche lösen kann.
Das ist nämlich überhaupt nicht trivial, ja,
also wir werden gleich einen Algorithmus sehen,
den kann ich Ihnen in fünf Minuten beschreiben,
den können Sie danach in einer Viertelstunde implementieren,
aber zu sehen, dass der funktioniert, ist richtig schwierig.
Und den Algorithmus gibt es auch noch nicht so lange.
Also es ist so, dass man lange darüber nachgedacht hat,
wie man die starken Zusammenhangskomponenten überhaupt in linearer Zeit,
also in O von V plus E-Zeit,
durch lineares Pasen des Graphen einmaliges anschauendes Graphen überhaupt bestimmen kann.
Das war lange Zeit nicht klar, wie man das machen kann.
Heute gibt es zwei Tiefensuchalgorithmen, die das können, die das leisten.
Die funktionieren ganz unterschiedlich, die beiden Verfahren und trotzdem,
beides Mal ist es eine Tiefensuche,
was diese entsprechenden Zusammenhangskomponenten findet
Okay, so, hier kommt mein Algorithmus. Ich habe gerade
ein bisschen geschwindelt, wir brauchen streng genommen zwei Tiefensuchen.
Mit einer kriegen wir es nicht hin,
sondern wir brauchen zwei. Ich habe das so gemacht,
dass sich die rekusive Funktion zweimal verwende, hier und hier.
Und da gibt es so ein paar Parameter und einmal wird
nur der vordere Parameter verwendet und einmal die hinteren Beine.
Sie hätten auch zwei verschiedene rekusive Funktionen aufschreiben können. Okay,
also das ist der Algorithmus, Drongly Connected Components heißt der.
Und sie sehen klassische Tiefensuche. Wir starten,
alle Knoten werden weiß markiert, Zeitstempel hier,
welche benutzen wir, glaube ich,
gar nicht an der Stelle
Und auch einen Zeitstempel. Doch, den brauchen wir.
Klar. Zeitstempel wird auf null gesetzt. Wir gehen durch die Adjazenzl,
durch die Knotenliste durch, wenn der Knoten noch weiß ist,
rufen wir DFS Visit auf. Also wir haben erstmal nur
eine Tiefensuche gemacht. Während wir DFS Visit laufen lassen,
werden wir ein Array initialisieren, das heißt FV.
Das hat was mit den Finishing-Zeiten zu tun. Und zwar
möchten wir gerne die Reihung der Knoten nach Finishing-Zeiten haben.
Und darauf möchten wir gleich schnell zugreifen.
Deswegen erzeugen wir ein Array FV. Und in FV verweisen
wir auf das
auf den Knoten
Mit der Eaten Finishing-Zeit. Das ist ein Array und FV von I
wird der Knoten sein, der die Finishing-Zeit I erhalten hat.
Wir werden jetzt bei unseren Zeiten die Discovery-Zeiten gar nicht mehr registrieren,
dann haben wir nur Finishing-Zeiten und dann wissen wir,
es kaum die Finishing-Zeiten von 1 bis Betrag und V vor.
Können wir dann abbilden in dieses Array? Das ist der
erste Durchlauf. Wir machen gleich an dieser Stelle weiter,
ich zeige ihm mal eben erst die DFS-Visit-Routine, die kommt hier.
Das können Sie noch ignorieren, diese SCC-Sachen, die brauchen wir noch nicht,
sondern nur das F von V, der erste DFS-Lauf läuft.
Sie sehen wieder alles, ist Standard, Knoten mit grau gefärbt,
die Addizenz-Liste wird angeschaut, wenn wir einen weißen Knoten sehen,
machen wir den rekrusiven Aufruf, Knicks weiter, interessiert uns alles,
wir brauchen keine Vorgänger, alles speichern wir alles nicht.
Irgendwann wird der Knoten schwarz und danach gucken wir,
okay, wenn das FV ungleich nie ist,
erster DFS-Durchlauf, das ist jetzt gegeben,
dann erhöhen wir die unsere Uhr,
wir kriegen eine Finishing-Zeit und wir speichern in diesem Array FV bei diesem Zeitstempel den Knoten U.
Damit haben wir einen Array, wo wir wissen,
an der Position 1 steht der Knoten mit der kleinsten Finishing-Zeit.
Und dann kommt er mit der zweiten und
so weiter.
Nein, an Position 1 steht der Knoten mit der Finishing Time 1
, so muss man sagen. Dann ist es richtig,
okay, so, was wir jetzt erreicht haben, ist,
wir sind einmal durch den Grafen gegangen und haben dieses Array erzeugt, sonst nix.
Wir haben das nur gemacht, um dieses Array mit den Finishing-Zeiten zu kriegen.
Jetzt bilden wir den transponierten Grafen. Jetzt nehmen wir eine Funktion,
die dreht einfach alle Kanten um. Die können sie sich wahrscheinlich ganz schnell vorstellen,
der Vollständigkeit halber steht sie hier, sie gehen alle Knoten,
sie betrachten alle Knoten, da gehen sie in die Addizenzliste,
sie erzeugen eine neue Addizenzliste, die ist transponiert,
indem sie für den Knoten U,
den Knoten Fow da eintragen und dann Dann kopieren Sie diese neue Additzenzliste da dran.
Und dann haben Sie alle Kanten umgedreht. Das ist die
Funktion. So, jetzt machen wir eine zweite Tiefensuche.
Auf den neuen Grafen, wo wir alle Kanten umgedreht haben.
Am Anfang setzen wir wieder alle Knoten auf Weiß.
Das müssen wir ja machen. Die sind ja inzwischen alle
schon schwarz gefährdet, kriegen jetzt alle wieder die Farbe Weiß.
Jetzt benutzen wir diesen zweiten und dritten Parameter der DFS Visit-Funktion.
Wir setzen also die SCC-Nummer auf null.
Wir durchlaufen wieder die Knotenmenge mit dieser Vorschleife.
Allerdings machen wir jetzt einen,
machen wir jetzt etwas anderes
Sind wir die Knoten noch in einer beliebigen Reihenfolge durchlaufen.
Hier geben wir die Reihung vor. Wir gehen mit einer Vorschleife für
die Variablen I von der Anzahl der Knoten runter bis eins
rückwärts und wir betrachten die Knoten mit in der Reihenfolge der Finishing-Zeiten,
der fallenden Finishing-Zeiten.
Die Schleife, die Vorschleife geht rückwärts und wir betrachten nicht irgendeinen beliebigen nächsten Knoten,
sondern wir sagen, wir nehmen den nächsten Knotenbesuch der Finishing-Zeit.
Und wenn der weiß ist, okay, dann setzen wir
die SDC-Nummer um eins hoch und rufen unsere DFS Visit-Routine auf.
Also der Unterschied hier nur die Reihenfolge,
in der wir
die Knoten betrachten
So, DFS Visit, jetzt zum zweiten Mal, was passiert?
Also hier ist der ganz normale Algorithmus, Knoten wird grau gefährt,
Addezenzliste wird betrachtet, wir machen den recordiven Abstieg,
kommen irgendwann hoch, Knoten wird schwarz gefärbt.
Das FV ist Neil für den zweiten Aufruf,
also machen wir das, wir machen nichts weiter als diesen Knoten hier,
den wir gerade betrachtet haben, diesen Knoten-U, diese SCC-Nummer zuzuordnen.
Das heißt, hier wird eine SCC-Nummer erzeugt,
also die nächste Komponente wird erzeugt und alle Knoten,
die wir in diesem DFS Visit-Durchlauf betrachten,
von denen behaupten wir,
die gehören zur gleichen starken Zusammenhangskomponente
Und dann sind wir fertig. Das SCC ist,
also das Array, stehen also alle Zusammenhangskomponenten-Nummern der Knoten drin.
Das ist der Algorithmus und der berechnet starken Zusammenhang. Und ich hoffe,
Sie sehen, der ist leicht, das ist einfach,
das einfach zu implementieren, aber dass der funktioniert,
ist nicht so klar auf Anhieb, dass der wirklich funktionieren soll.
Übrigens ist der, hat ja auch einen gravierenden Nachteil,
kann ich sie gleich darauf hinweisen, so einen technischen Nachteil.
Ich habe ihn gerade, hatte gerade dieses Facebook-Beispiel.
Diese Operation auf realen Daten ist der Horror, wenn Sie sich vorstellen,
Sie haben so ein Abhängigkeitsgrab von Wie viel sind es?
800 Millionen Benutzern, die alle Freundschaftslisten haben,
mit im Schnitt 50 oder 100 Kontakten.
Und sie müssen den Grafen invertieren. Das ist abenteuerlich.
Das würden die wahrscheinlich, da werden sie,
das werden sie gar nicht machen können.
Da brauchen sie so viel Platz und so.
Es wird nicht passieren. Das heißt, dieser Algorithmus hat
an der Stelle einen ganz entscheidenden Nachteil. Der andere,
den ich ihnen hier nicht zeige, weil der ist in
dem Nachweis der Korrektheit nochmal einiges komplexer als das hier.
Der hat den Nachteil nicht. Also vor allen Dingen
hat er zudem auch nicht den Nachteil,
dass sie
zwei DFS-Durchläufe brauchen
Es gibt also einen, der war auch nur ein DFS-Durchlauf und der muss die Kanten nicht investieren und kann es trotzdem,
ja, der ist aber, wie gesagt,
den können sie sich so anlesen oder sie kommen in die Algorithmik-Vorlesung demnächst, dann zeige ich ihnen den.
Okay, aber wir gucken uns jetzt den hier an,
ja, also DFS-Durchlauf 1, Knoten werden sortiert,
fallende Finishing-Zeiten, danach machen wir den DFS-Durchlauf 2,
nee, wir drehen alle Kanten um,
dann machen wir den zweiten DFS-Durchlauf und dann,
wenn wir den zweiten DFS-Durchlauf machen, dann wissen wir,
oder behaupten wir einfach, dass immer dann,
wenn wir von dieser Hauptschleife in die Rekursion absteigen,
wir in dem Augenblick eine komplette Zusammenhangskomponente entdecken
Und wenn wir, dann kommen wir hier wieder an und dann erhöhen wir
diese Nummer und dann entdecken wir die nächste komplette Zusammenhangskomponente.
Ja. Okay. So, wenn der Algorithmus korrekt ist,
dann können wir tatsächlich die starken Zusammenhangskomponenten in linearer Zeit berechnen.
Dieser Algorithmus hat O von N plus M-Zeit. Gut,
so, um zu sehen, dass das richtig ist,
brauchen wir zwei Beweise, zwei Schritte.
Der erste Schritt zeigt das Verhältnis zwischen starken Zusammenhangskomponenten und den Endzeiten.
Der bezieht sich also auf den ersten Teil des Algorithmus
Warum wir eigentlich uns diese Endzeiten angucken. Um jetzt zu zeigen,
was gilt, müssen wir uns erstmal nochmal einigen.
Also wenn man einen Algorithmus betrachtet, hat man immer
das Problem, dass sich Variablen in der Zeit ändern.
Die haben immer, immer einen anderen Wert. Also wenn wir
jetzt im folgenden Nähe in den Beweis von Discovery und Finishing-Zeiten
reden, dann meinen wir immer die des ersten DFS-Durchlaufs.
Weil denen betrachten wir jetzt. Und dann haben wir ja so Mengen von Knoten,
also wenn wir eine Menge U von Knoten haben,
dann geben wir dieser Menge U auch eine Discovery-Zeit und das ist einfach die kleinste.
Das Minimum über alle Knoten, U, die der Menge enthalten sind,
der Discovery-Zeiten, das ist die Discovery-Zeit der Menge.
U, ja. Und die Finishing-Zeit ist einfach das Maximum, ja.
Also wir brachten eine Menge vom Knoten und irgendeinen Knoten ist ja der Erste,
den wir dann sehen, wenn wir die Menge betrachten,
die Discovery-Zeit dieses ersten Knotens ist die Discovery-Zeit der ganzen Komponente oder der Menge.
Und die Finishing-Zeit ist dann, naja,
das ist quasi die Zeit des gleichen Knotens, weil der Erste,
den betrachten, ist auch immer der Letzte, den wir ja betrachten,
weil über den Ersten erreichen wir die Komponente und irgendwann werden wir die wiederfalllassen.
Okay, was ist das Verhältnis zwischen starkem Zusammenhang und Endzeiten
Das steht hier, wenn wir also zwei starke Zusammenhangskomponenten haben,
C- und C-Strich, aus einem Grafen und es sind unterschiedliche.
Also C ist ungleich, C-Strich. Dann gilt,
für alle Kanten, UV, die zwischen C- und C-Strich verlaufen,
ist die Finishing-Zeit der Komponente C größer als die Finishing-Zeit der Komponente C-Strich.
Oh man, da brauchen wir ein Bild. Da kommt
gleich ein Bild, aber ich zeige Ihnen das nochmal.
Also wir haben zwei Komponenten, irgendwo in unserem Grafen.
Das ist die eine Komponente C und das ist die andere Komponente, C-Strich. Wir wissen,
es gibt irgendwie eine Kante von U nach V,
die geht von einem Knoten U,
der Knoten U ist also hier und der Knoten V,
der ist hier und es gibt eine Kante von U nach V,
ja, die kann es ja geben,
also sind zwei starke Zusammenhangskomponenten, was wir jetzt schon wissen,
ist natürlich, es gibt keine Kante in diese Richtung,
sonst wären das keine getrennten Zusammenhangskomponenten, das wissen wir schon.
Okay, so und dann sagen wir,
dass die Finishing-Zeit von C,
von der Komponente auf alle Fälle größer sein muss,
als die Finishing-Zeit von dieser Komponente
Das ist schon insofern keine so eine triviale Geschichte, weil sie müssen sich überlegen,
Finishing und Discovery-Zeiten sind ja nicht fest für einen Graf.
Also, das heißt, egal, was hier steht,
ist, egal, wie unsere tiefen Suche läuft,
egal mit welchem Knoten wir anfangen oder in welcher Reihenfolge wir die Addizenzlisten bearbeiten,
es wird immer gelten, das F von See größer ist als F&C-Strich.
Dementsprechend müssen wir jetzt auch eine Fallunterscheidung machen. Wir müssen irgendwie gucken,
in welcher Reihenfolge sehen wir den Knoten aus C- und C-Strich.
Und der erste Fall, den wir betrachten, ist,
Die Discovery-Zeit von C ist kleiner als die Discovery-Zeit von C-Strich.
Discovery-Zeit einer Komponente ist der Zeitpunkt, wo wir den ersten
Knoten dieser Komponente sehen. Und wer Algorithmus läuft,
jeder Knoten kriegt seine Discovery-Zeit und irgendwann,
das ist alles weiß, weißes Territorium,
irgendwann laufen wir hier rein und finden so einen ersten Knoten in dieser Komponente C. Das,
wenn der Knoten hier betrachtet wird, hat er einen Namen,
ja, X,
X,
genau,
X. Dann haben wir das D von X,
das wird dann gesetzt. Und das ist auch die Discovery-Zeit
der ganzen Komponente C. Weil es ist der erste Knoten
der Komponente
Den wir betrachten. Okay, was passiert jetzt?
Wie gesagt, alle anderen Knoten sind weiß.
Das ist ja der erste Knoten, den wir jetzt sehen.
So, wir betrachten irgendeinen anderen Knoten W aus C-Strich.
Irgendeinen. Den. Das ist der Knoten weh.
Und was wir wissen ist, alle Knoten sind weiß.
Das heißt, es gibt einen weißen Pfad,
der geht von X hierhin, weil das ist ja eine starke Zusammenhangskomponente.
Das heißt, es gibt einen Pfad von X nach
Es gibt einen Pfad und der ist weiß, weil alle knotenweiß sind.
So, dann kommen wir von x nach u. Wir wissen,
es gibt diese Kante von UNAV und das ist ja auch eine starke Zusammenmenmachskomponente.
Das heißt, hier gibt es auch wieder einen Pfad, der uns hier runterführt.
Und alle Knoten sind weiß. So, das heißt,
es gibt einen weißen Pfad von x nach u,
von u, nach V, von V nach W.
Damit ist W ein Nachfahrer von x. Ja, klar,
das sagt das Theorien der weißen Pfade.
Und dann wissen wir, dass die Finishing-Zeit von x von
der Komponente größer sein muss als die Finishing-Zeit von dieser Komponente.
Ja, weil wir haben die Discovery-Zeit von x
und dann haben wir das Zeitintervall und irgendwann haben wir mal
die Finishing-Zeit von
Und wir wissen aber, wie es sein Nachfahre,
ja, und das gilt für alle Knoten der Komponente.
Das heißt, hier irgendwo ist die Discovery-Zeit von W
und dann wird hier auch die Finishing-Zeit von W sein.
Weil, Klammerungstheorie, den Fall,
dass die Finishing-Zeit auf der Seite von F&X ist.
Gibt es nicht, ja. Intervalle sind immer ineinander enthalten. Okay,
das ist der einfache Fall,
wenn wir tatsächlich erst die Komponente C entdecken und dann C-Strich,
aber es ist ja gar nicht gesagt,
dass das in dieser Reihenfolge passieren muss.
Wir können ja die Knoten in irgendeiner Reihenfolge bearbeiten.
Kann sein, dass wir ersten Knoten aus C-Strich
entdecken,
Okay, jetzt sieht das Bild ein bisschen anders aus.
Was passiert ist, die Discovery-Zeit von C ist jetzt größer
als die Foncé-Strich. Können wir mal das Bild wieder auf
, dann kann man es besser verstehen.
Also, Situation ist wie folgt. Wir haben also hier wieder
unsere Komponente C. Wir haben hier wieder unsere Komponente C-Strich.
Wir haben unseren Knoten U, wir haben unseren Knoten V.
Was jetzt anders ist, ist, die Tiefensuche läuft jetzt
irgendwie so durch, alles weiß.
Und wir kommen hier rein
In diese Komponente zufällig. Und der Knoten,
den wir hier erreichen, der ist,
den nennen wir Y. Ist der Knoten Y,
ist der erste Knoten, den wir erreichen, aus dieser Komponente.
Wenn wir Y erreichen, ist genau wie eben alles andere
noch weiß. Alle Knoten sind noch weiß. Okay,
wir wissen, dass der Komponentengraf ein Dack ist.
Wenn der Komponentengraf ein Dack ist,
gibt es keine Kante,
die uns zurückführt
In die Komponente C. Weil es gibt ja die Kante von C nach C-Strich.
Deswegen kann es keine Kante geben von C-Strich nach C.
Die gibt es nicht. Kann ich auftreten. Das wissen
wir. Also die Bedingungen, also für alle Knoten
eher aus C-Strich und alle Knoten S aus C-Strich,
ist die Bedingung eher nach S falsch.
Also hier wäre irgendwo so ein Knoten Air und irgendwo so
ein Knoten S. Und diese Kante gibt es nicht.
So, was gilt zum Finishing-Zeitpunkt von Y?
Also wir haben jetzt Y betrachtet. So,
und jetzt von Y aus folgen wir jetzt allen weißen Faden,
laufen da überall lang
Und irgendwann kommen wir wieder zu Y zurück. Ja,
also wir haben den Discovery-Zeitpunkt Y und dann kommt die Finishing-Zeit
von Y am Ende, ja. Zu dem Zeitpunkt gilt,
dass für jeden Knoten aus der Menge C,
also irgendein beliebiger Knoten weh hier, der ist immer noch weiß.
Ja, warum ist der noch weiß? Ja,
der muss weiß sein, weil es gibt ja gar keinen
weißen Pfad, um den Knoten zu erreichen.
Kann es nicht geben, weil es gibt ja gar keine Kante,
die uns von dieser Komponente in diese Komponente führt.
Das heißt, der ist immer noch weiß.
Das heißt, wir setzen die Finishing-Zeit von Knoten Y und
irgendwann später werden wir mal den Knoten weh entdecken
Deswegen ist die Finishing-Zeit für den Knoten weh und für alle
Knoten aus der Komponente größer. Okay, damit haben wir
das gezeigt und wir wissen, dass das Theorien korrekt ist.
Und wenn wir jetzt den transponierten Graphen betrachten, dann müssen wir,
und diese Finishing-Zeiten beziehen sich nach wie vor aus dem ursprünglichen Grafen,
dann müssen wir hier nur die Relation umdrehen.
Wir drehen alle Kanten um und dann gilt,
wenn wir jetzt eine Kante UV in dem transponierten Graphen haben,
dann ist die Finishing-Zeit von C kleiner als die Finishing-Zeit von C-Strich.
Und beachten Sie jetzt diese Finishing-Zeiten,
beziehen sich nicht auf einen DFS im transponierten Graphen,
sondern immer noch auf den DFS in dem ursprünglichen Graphen.
Wenn wir danach die Kanten umdrehen,
dann gilt das entsprechend
Okay, so, das war Teil 1.
Und jetzt kommt Teil 2. Jetzt müssen wir zeigen,
dass wir tatsächlich in dem Algorithmus die SCCs aufzählen.
Gucken wir mal den Algorithmus an. Also wir betrachten jetzt
in unserem Graphen die Knoten in einer speziellen Reihenfolge, nämlich nach fallender Finishing-Zeit.
Fallende Finishing-Zeit hatte etwas mit topologischer Sortierung zu tun.
Das werden wir gleich noch sehen. Und wenn wir das machen,
dann kommen wir in so eine Komponente rein, hier,
ja, wir werden alle diese Knoten bearbeiten und sehen und sagen,
die gehören alle zu einer Komponente,
müssen wir zeigen,
dass das dann auch gilt,
Und wenn wir aus dem DFS-Aufruf zurückkommen, dann ist
die Komponente auch abgeschlossen. Das gilt es zu zeigen.
Das ist so. Und dass es so ist,
werde ich Ihnen aus Zeitgründen jetzt aber nicht mehr zeigen,
sondern das werde ich Ihnen dann am Freitag zeigen.
Das war es dann für heute