Graphen

Kürzeste Pfade in azyklischen Digraphen (Algorithmus 4.10)

AcyclicSP aus Sedgewick/Wayne, Algorithmus 4.10 (Listing 4.34) – kürzeste Pfade in einem kantengewichteten DAG, Knoten für Knoten in topologischer Reihenfolge relaxiert, auch mit negativen Gewichten, am Digraphen tinyEWDAG wie in Abbildung 4.77.

Lesestand

So geht’s: Beim Öffnen läuft der Testclient mit tinyEWDAG und Start s = 5, in derselben Lage wie in Abbildung 4.77. Die Zahlen an den Pfeilen sind die Gewichte. Zuerst berechnet Topological die Ordnung 5 1 3 6 4 7 0 2 (rechts unter der Tabelle). Dann kommt jeder Knoten genau einmal dran, in dieser Reihenfolge: Der laufende ist orange, die erledigten sind grün. relax prüft seine ausgehenden Kanten, die gerade geprüfte ist blau gestrichelt. Wird ein Weg kürzer, ändern sich edgeTo[] und distTo[] rechts. Rot sind Kanten, die nur vorläufig im Baum stehen, weil ihr Zielknoten noch nicht dran war. Schwarz ist der fertige Baum der kürzesten Pfade, grau gestrichelt sind Kanten, die nicht auswählbar sind (sie bringen nichts oder wurden verdrängt, wie 6→0 und 6→2). Am Ende zeigt die Ausgabe für jeden Knoten seinen Pfad, er leuchtet dabei orange auf. „ab s“ startet von einem anderen Knoten aus, „Zufalls-DAG“ erzeugt einen neuen azyklischen Digraphen, auch mit negativen Gewichten, „↺ Neu starten“ springt zurück zu tinyEWDAG.

Erklärung

Kürzeste Pfade in kantengewichteten azyklischen Digraphen (Algorithmus 4.10), Klasse AcyclicSP. Gleiche Aufgabe wie Dijkstra [39], aber für den Sonderfall ohne Zyklen (DAG). Dann geht es schneller, einfacher und sogar mit negativen Gewichten.

Idee: Die Knoten in topologischer Reihenfolge [34] relaxieren. Kommt man zu Knoten v, sind alle Kanten, die in v hineinführen, schon von ihren Vorgängern relaxiert worden. distTo[v] ist also bereits endgültig, bevor v seine eigenen Kanten weitergibt.

Der Code:

  • distTo und edgeTo wie bei Dijkstra, Anfang unendlich, distTo[s] = 0.
  • Topological aus Algorithmus 4.5, hier für kantengewichtete Digraphen überladen.
  • Dann einfach: für jeden Knoten in topologischer Reihenfolge relax(G, v). Fertig.
  • Keine Vorrangwarteschlange, kein marked[]. Jeder Knoten und jede Kante wird genau einmal angefasst, ein Knoten wird nie ein zweites Mal relaxiert.

Abbildung 4.77 (tinyEWDAG.txt, Start 5), topologische Ordnung 5 1 3 6 4 7 0 2:

KnotenWas passiert
5Kanten 5→1, 5→4, 5→7 kommen in den Baum
11→3 kommt hinzu
33→6 kommt hinzu; 3→7 nicht, 7 ist über 5→7 schon kürzer erreichbar
66→2 und 6→0 kommen vorläufig hinzu; 6→4 nicht
44→0 kommt hinzu und verdrängt 6→0; 4→7 nicht
77→2 kommt hinzu und verdrängt 6→2
00→2 bringt nichts
2letzter Knoten, keine ausgehenden Kanten

Ergebnis (Testlauf):

ZielAbstandPfad
00,735→4 (0,35), 4→0 (0,38)
10,325→1
20,625→7 (0,28), 7→2 (0,34)
30,615→1 (0,32), 1→3 (0,29)
40,355→4
50,00–
61,135→1 (0,32), 1→3 (0,29), 3→6 (0,52)
70,285→7

Man sieht gut, dass Kanten vorläufig in den Baum kommen und später ersetzt werden (6→0, 6→2). Bei Dijkstra wäre das nicht passiert, weil er nach Abstand und nicht nach topologischer Ordnung vorgeht. Das Ergebnis ist dasselbe.

Satz S: In einem kantengewichteten DAG findet dieses Verfahren die kürzesten Pfade in Zeit proportional zu E + V, also linear. Gewichte dürfen negativ sein, weil die Korrektheit nur von der Reihenfolge abhängt, nicht vom Vorzeichen.

Längste Pfade gibt es gratis dazu: Alle Gewichte negieren, kürzeste Pfade berechnen, Vorzeichen zurückdrehen. Das ist der kürzeste Pfad der negierten Kopie und zugleich der längste im Original. In Graphen mit Zyklen ist das Längste-Pfade-Problem dagegen schwer, im DAG ist es linear. Anwendung im Buch kurz danach: die Methode des kritischen Pfads für Ablaufplanung mit Dauern, etwa im Projektmanagement. Der längste Pfad durch die Aufgaben bestimmt die Mindestdauer des Projekts.

Vergleich der Kürzeste-Pfade-Verfahren bisher:

Dijkstra (4.9)Azyklisch (4.10)
Graphbeliebignur DAG
Gewichtenicht negativbeliebig
Reihenfolgenach Abstand (Vorrangwarteschlange)topologisch
ZeitE log VE + V

Stand Kapitel 4:

  • Algorithmus 4.1: Tiefensuche für Pfade [30]
  • Algorithmus 4.2: Breitensuche für kürzeste Pfade [31]
  • Algorithmus 4.3: Zusammenhangskomponenten [32]
  • Algorithmus 4.4: Erreichbarkeit in Digraphen [33]
  • Algorithmus 4.5: Topologisches Sortieren [34]
  • Algorithmus 4.6: Starke Komponenten nach Kosaraju [35], [36]
  • Algorithmus 4.7: Minimaler Spannbaum nach Prim [37]
  • Algorithmus 4.8: Minimaler Spannbaum nach Kruskal [38]
  • Algorithmus 4.9: Kürzeste Pfade nach Dijkstra [39]
  • Algorithmus 4.10: Kürzeste Pfade in azyklischen Digraphen [41]