Kürzeste Wege Problem Das Ergänzende Kapitel zu DAP 2 Bernchteine Elena Kürzeste Wege In kantenbewerteten Graphen war die Distanz zwischen zwei Knoten definiert als die Länge des kürzesten Weges zwischen ihnen. Gegeben sein ein Weg p = (v0 ,v1 ,... ,vn ) in einem Graphen G mit Kantengewicht w . Die Länge des Weges ist dann definiert als w(p) = n (vi-1 , vi ) i =1 Bezeichnen wir mit P ( u,v ) die Menge der Wege von Knoten u nach Knoten v, so ist die Distanz zwischen u und v ( u,v ) = min { w ( p ) p P ( u,v ) } falls P ( u,v ) sonst Ein kürzester Weg p von u nach v ist dann ein Weg p P (u,v) mit w( p ) = (u,v). Problemvarianten Das Problem der kürzesten Wege (KW-Problem) tritt in den folgenden Varianten auf: 1 1:1-KW-Problem (single-pair shortest-path problem) Gesucht ist der kürzeste Weg von einem Knoten u zu einem Knoten v. 2 1:n-KW-Problem (single-source shortest-path problem) Gesucht sind die kürzesten Wege von einem Quellknoten u zu allen anderen Knoten v. 3 n:1-KW-Problem (single-destination shortest-path problem) Gesucht sind die kürzesten Wege zu einem Zielknoten v von allen anderen Knoten u. 4 n:n-KW-Problem (all-pairs shortest-path problem) Gesucht sind kürzesten Wege zwischen allen Knoten des Graphen. Anmerkungen: • Problem 3 kann auf Problem 2 zurückgeführt werden (und umgekehrt), indem die Richtung der Kanten vertauscht wird. In ungerichteten Graphen sind die beiden Probleme identisch • Eine Lösung für Problem 1 fällt Teil eines Lösung für Problem 2 ab. Außerdem ist kein spezifischer Algorithmus für Problem 1 bekannt, der asymptotisch schneller wäre als die besten Algorithmen für Problem 2 Kanten mit negativen Gewichten Kanten mit negativen Gewichten sind zwar untypisch für KW-Probleme, können jedoch gelegentlich vorkommen. Sie sind tolerierbar, solange keine vom Quellknoten erreichbare Zyklen negativer Länge entstehen.Ein kürzester Weg wäre dann nicht mehr wohldefiniert. Einige Algorithmen verlangen nichtnegative Gewichte, andere tolerieren sie, d.h. sie produzieren ein korrektes Ergebnis, solange keine negativen Zyklen auftreten. Repräsentation kürzester Wege In der Regel interessiert man sich nicht nur für die Länge eines kürzesten Weges, sondern auch für den kürzesten Weg selbst. Bei dem 1:n - KW-Problem, das wir betrachten wollen, bietet sich wieder die Verwendung eines Feldes pred[ ] an, in dem für jeden Knoten v dessen Vorgänger u auf dem kürzesten Weg von einem Quellknoten s nach v gespeichert ist (vgl. Breitensuche) pred[ ] definiert einen Baum mit Wurzel s , der die kürzesten Wege zu allen anderen Knoten enthält. Wie wir sehen werden, genügen die kürzesten Wege dieser Baumstruktur. Eigenschaften der KW-Bäume Graph G=(V,E) –gewichtet, gerichtet; w : E R –Gewichtsfunktion, s – Quellknoten, G hat keine Zyklen negativer Länge 1. Nach der Initialisierung führt man eine Reihenfolge von Relaxationsschritten an der Kanten von G so, dass s eine Wurzel ist und aus dieser Wurzel s einen gerooteten Vorgänger-Teilgraph Gpred entsteht. 2. Nach der Initialisierung führt man eine Reihenfolge von Relaxationsschritten an der Kanten von G so, dass d[v]= ( u,v ) ist für alle v aus V. Daraus folgt, unser Vorgänger-Teilgraph Gpred – ein gerooteter aus der Wurzel s KW-Baum . Beispiel: Gerichteter Graph mit zwei unterschiedlichen KW-Bäumen mit Wurzel 0 Eigenschaften kürzester Wege Lemma: (Optimale Substruktur) Gegeben sei ein Graph G = (V,E) mit Kantengewicht w . Sei p = ( v0 ,v1 , ...,vn ) ein kürzester Weg von v0 nach vn . Sei pik = (vi , vi+1 ,... ,vk ) mit 0 i k n ein Teilweg von p. Dann ist pik = (vi , vi+1 ,… ,vk ) ein kürzester Weg von vi nach vk . Beweis: Wenn wir p zerlegen in Teilwege p0i , pik und pkn ,dann gilt: w(p) =w( p0i) + w( pik) + w( pkn) Wir nehmen nun an, es gebe einen Weg p'ik von v0 nach vk mit w(p'ik ) < w(pik ) . Dann ist p' = p0i , pik , pkn ein Weg von v0 nach vn mit w(p') = w( p0i) + w(p'ik) + w( pkn) < w( p0i) + w( pik) + w( pkn) = w( p) Dies ist ein Widerspruch zur Annahme, dass p ein kürzester Weg ist . Korollar: Gegeben sei ein gerichteter Graph G = (V,E) mit Gewichtsfunktion w : E R Angenommen , es gebe einen kürzesten Weg p von s nach v , der man so beschreiben kann : s P u v ( mit dem Knoten u und dem Weg p). Daraus folgt ,dass das Gewicht des KW von s nach v ist : ((s,v) – Distanz zwischen s und v (kürzester Weg von s nach v) (s,v ) = (s,u ) + w(u,v) Beweis: (s,v ) = w(p) = w(p ) + w(u,v) = (s,u ) + w(u,v) qed. Lemma: Gegeben sei ein GraphP‘ G = (V,E) mit Kantengewicht w und einem Ausgangsknoten s . Dann gilt für alle Kanten ( u ,v ) E : (s,v ) (s,u ) + w(u,v) Beweis : 1. Es gibt einen KW von s nach v , aber nicht über u (s,v ) < (s,u ) + w(u,v) ist. 2. Es gibt einen KW von s nach v über diesen Knoten u : (s,v ) = (s,u ) + w(u,v) qed. 3. Es gibt keinen KW von s nach v entweder über diesen Knoten u oder nicht über diesen Knoten u : (s,v ) < (s,u ) + w(u,v) ist. Relaxation Zum Begriff der Relaxation Wir verwenden während der Berechnung Schätzungen d(v) (obere Schranken) für die Distanz zwischen Anfangsknoten s und allen anderen Knoten v. Am Anfang werden alle oberen Schranken (mit Ausnahme von s) auf „unendlich“ gesetzt. Anschließend findet Schritt für Schritt ein Relaxationsvorgang statt: Es wird für eine Kante (u,v) geprüft, ob sich durch Berücksichtigung dieser Kante die bisher gefundene Distanz d(v) (d(v) =(s,v) ) von s nach v „entspannt“. void relax(index u, index v) { if (d[v] > d[u] + w(u ,v)) d[v] = d[u] + w(u ,v); pred[v]=u;} Eigenschaften der Relaxation 1. Nach der Relaxation an der Kante (u,v): d[v] <=d[u] + w(u,v). Beweis: a)Vor der Relaxation d[v] <=d[u] + w(u,v) - d[v] bleibt nach der Relaxation unverändert. b) Vor der Relaxation d[v] >d[u] + w(u,v) - nach der Relaxation d[v] =d[u] + w(u,v) . qed 2. d[v] = (s,v) = Das heißt, es gibt keinen KW zwischen s und v. 3. s u v - KW und Vor der Relaxation d[u] = (s,u) , daraus folgt nach der Relaxation d[v] = (s,v) . Beweis: Nach der Relaxation d[v] <=d[u] + w(u,v) = (s,u) + w(u,v) = (s,v) Nach Def. d[v]>= (s,v) , daraus folgt d[v] = (s,v) . qed Beispiel(Relaxation an der Kante (u ,v) ) u 5 2 5 2 u (a) v 9 u v u 7 5 2 v 6 5 2 v 6 (b) Im Fall (a) d[v] > d[u] + w[u,v] , d[v] wird gewechselt Im Fall (b) d[v] d[u] + w[u,v] , d[v] bleibt unverändert Dijkstras Algorithmus Die verschiedenen Kürzeste-Wege-Algorithmen unterscheiden sich im wesentlichen in der Reihenfolge, in der Kanten zur Relaxation aufgegriffen werden. Dijkstras Algorithmus geht davon aus,dass alle Kantengewichte nichtnegativ sind. • Alle Knoten werden nach ihrer bisher gefundenen Distanz d(v) aufsteigend geordnet in einer geeigneten Datenstruktur Q gehalten (am besten Prioritätswarteschlange mit Heap). • Der nächste Knoten u wird aus Q entfernt und alle von ihm ausgehenden Kanten werden „relaxiert“. • Bei den Endknoten v der Kanten (Nachbarn von u) wird u als Vorgänger im KWBaum eingetragen, falls sich die Distanz d(v) verringert hat, also (u,v) eine Baumkante wird • Der Vorgang wird solange wiederholt, bis Q leer geworden ist . Komplexität von Dijkstras KW-Algorithmus • Jede remove-Operation der Prioritätenwarteschlange benötigt O(log|V|) Schritte. Insgesamt werden |V| dieser Operationen benötigt. • Der Aufbau des Heaps der Warteschlange kostet O(|V|) • Die Modifikation der d -Werte (relax) impliziert ein Umordnen des Heaps, das in O(log|V|) Schritten durchgeführt werden kann .Höchstens |E| dieser Operationen sind erforderlich. • Wir erhalten also O(|V| log|V| + |E| log|V|) = O((|V|+|E|) log|V|) = O(|E| log |V|) Fords Algorithmus (Bellman-Ford) • Der KW-Algorithmus nach Ford setzt keine nichtnegativen Gewichte voraus. • Er benutzt ebenfalls das Relaxationsprinzip. Es wird also in jedem Iterationsschritt für jede Kante (u,v) geprüft,ob ihre Verwendung die Distanz vom Quellknoten v0 zu ihrem Endknoten v reduziert. Es wird so lange iteriert, bis keine Verbesserungen mehr möglich sind. • Bei Existenz negativer Zyklen würde dies bedeuten , dass der Algorithmus nicht stoppt. • Da jeder kürzeste Weg jedoch aus höchstens |V|-1 Kanten besteht, kann man sich überlegen, dass |V|-1 Iterationsphasen ausreichen, um kürzeste Wege zu berechnen (falls existent, d.h. vom Quellknoten zu ihrem Endknoten kann man höchstens |V|-1 Kanten besuchen , sonst haben wir einen Zyklus negativer Länge) • In jedem Iterationsschritt i entstehen Teillösungen, d.h. kürzeste Wege mit einer Kantenanzahl von i. BELLMAN-FORD(G , w, s) 1 initialize - single - source(G, s) 2 for i ← 1 to |V [G] | - 1 3 do for each edge (u, v) E [G] 4 do Relax(u,v,w) 5 for each edge (u, v) E [G] 6 do if d[v] > d[u] + w(u,v) 7 then return FALSE 8 return TRUE BELLMAN-FORD shortest path (V, E, vo , p) { // berechne SSSP von vo zu allen anderen Knoten // falls es keine Zyklen negativer Länge gibt, gib aus „nicht gefunden“ // sonst gib aus „gefunden“ und p enthält den kürzesten Weg for (each v V) { d[v] = ; p[v] = undefined; } d [v0]= 0; for (index i = 1; i <|V| -1; i ++ ) { for (each edge (u, v) E[G] ) { if (d[v] > d[u] + w(u,v) ) // Relaxation { d[v] = d[u] + w(u,v) ; p[v] = u ; }} // falls vorkommen, p enthält einen kürzesten Weg for (each edge (u, v) E[G] ) // überprüfe für negative Gewichte { if (d[v] > d[u] + w(u,v)) return FALSE ; return TRUE; }}} Beispiel (Bellman-Ford-Algorithmus für negative Gewichte) Beispiel (Bellman-Ford-Algorithmus für negative Gewichte) 6 -2 6 0 5 -3 8 -4 2 7 7 7 9 für i =1 Wir betrachten die vom Quellknoten 0 ausgehenden Kanten w(0,1)= 6 und w(0,3)=7 d[1] = 6 und d[3] = 7 Beispiel (Bellman-Ford-Algorithmus für negative Gewichte) 1 6 1 6 -2 6 0 5 2 7 3 7 0 -3 8 4 7 7 3 7 -4 2 9 4 -2 -4 2 7 5 6 -3 8 2 2 9 4 für i = 2 Wir betrachten die vom Quellknoten 0 über einen Zwischenort ausgehende Kanten w(0,1) , w(1,4) und w(0,3),w(3,2) d[4] = d[1] + w(1,4)= 6+(-4)=2 und d[2] = d[3] + w(3,2) =7+(-3)=4 Beispiel (Bellman-Ford-Algorithmus für negative Gewichte) 1 6 5 7 0 4 -2 -3 8 2 3 9 7 4 2 7 3 2 9 2 5 2 für i = 3 4 -2 6 -3 8 7 -4 7 2 7 3 2 9 4 Wir betrachten die vom Quellknoten 0 über 2 Zwischenorte ausgehende Kanten w(0, 3),w(3, 2),w(2,1) d[1] = d[2] + w(2,1)= 4+(-2)=2 7 -4 -4 7 0 6 2 6 -3 8 7 1 1 -2 6 0 5 2 4 Beispiel (Bellman-Ford-Algorithmus für negative Gewichte) 1 5 6 0 -4 7 2 7 3 2 9 5 4 0 4 -2 -3 8 4 -3 8 7 -4 7 2 7 3 9 7 -4 7 2 7 3 2 9 4 für i = 4 -2 6 7 0 4 2 2 -3 8 1 -2 6 7 9 3 4 5 6 2 7 2 2 -4 7 5 6 -3 8 0 2 1 -2 6 1 2 2 4 Wir betrachten die vom Quellknoten 0 über 3 Zwieschenorte ausgehende Kanten w(0, 3),w(3, 2),w(2,1),w(1,4) d[4] = d[1] + w(1,4)= 2+(-4)= - 2 Beispiel (Bellman-Ford-Algorithmus für negative Gewichte) 1 5 6 0 2 7 7 3 2 9 5 4 0 4 -2 6 -4 7 4 2 2 -3 8 1 -2 6 7 9 3 4 5 0 4 -2 6 2 7 2 2 -3 8 -3 8 7 -4 7 2 7 3 9 2 4 7 -4 -4 7 5 6 -3 8 0 2 1 -2 6 1 2 7 2 7 3 2 9 4 Nach der Relaxation haben wir keine vom Quellknoten 0 erreichbare Zyklen negativer Länge Komplexität des Ford-Algorithmus • Die Initialisierung besteht aus Q (|V|) Schritten • Die innere for-Schleife besteht aus der Relaxationsoperation (O(1)) und wird jeweils |E| mal ausgeführt, d.h. ihr Aufwand beträgt O(|E|) • Die äußere for-Schleife wird |V | - 1 mal ausgeführt, jeweils mit einem Aufwand von O(|E| ) , d.h. die geschachtelte for-Schleife ist mit O(|V|* |E|) zu kalkulieren • Die nachfolgende Prüfung auf Existenz negativer Zyklen kostet O(|E|) • Die Gesamtkomplexität des Ford-Algorithmus beträgt daher O(|V|* |E|) . Das n : n – KW - Problem (all - pairs shortest paths) • Kürzeste Wege zwischen allen Knoten des Graphen erhält man, indem man für jeden Knoten das 1 : n - Problem löst, d.h.den Dijkstra - oder den Ford-Algorithmus |V| mal aufruft. • Dies führt zu einer Gesamtkomplexität von O((|V|2*E ) (Ford-Algorithmus) bzw. O(|V|* |E|log|V|) (Dijkstra-Algorithmus) • Bei dünn vermaschten Graphen, z.B. wenn |E| = O (|V|), und nichtnegativen Gewichten ist diese Vorgehensweise zu empfehlen und der Dijkstra-Algorithmus einzusetzen. • In anderen Fällen gibt es spezielle Algorithmen für das n : n – KW - Problem Johnson‘s Algorithmus • Der KW – Algorithmus nach Johnson ist ein APSP-Algorithmus. Er benutzt Dijkstra und Bellman-Ford Algorithmen als Hilfsalgorithmen und führt eine Rekalibrierung durch. • Es wird zuerst mit Bellman-Ford die Existenz negativer Zyklen geprüft • Dann wird in zweitem Schritt, falls wir keinen negativen Zyklus haben, rekalibriert, so bekommen wir einen Graph, dessen Kanten positiv sind . • Weiter können wir Dijkstra den kürzesten Weg für alle Knoten berechnen Bestimmung der Rekalibrierung : Füge einen zusätzlichen Knoten s zum Graph hinzu und verbinde diesen Knoten s mit jedem anderen Knoten v V [G] durch eine Kante der Länge 0. Berechne alle h(v) und benutze dabei Bellman-Ford Algorithmus (h(v)=(s,v) ) . w:ER s 0 b 0 0 v u Graph Dann kann man w(u,v) –Werte nach der Formel w(u,v) = w(u,v) + h(u) - h(v) bekommen Rekalibrierung h : V R heißt eine Rekalibrierung einen gerichteten Graphen G = (V , E) mit Gewichtsfunktion w : V R, falls ( u,v) E : h(u) + w(u,v) h(v) gilt . Beobachtung. Ist h eine Rekalibrierung , so ist w : E R mit (u,v) E : w(u,v) : = w(u,v) + h(u) – h(v) eine nicht negative Gewichtsfunktion. Für w gilt zusätzlich : Ist p = (v0 ,..., vn) ein Weg, so ist w(p)= k w(vi, vi+1) = k (w(vi, vi+1) + h(vi) - h(vi+1) ) i=1 i=1 = n w(vi, vi+1) + h(v1) - h(vn) = w(p) + h(v1) - h(vn) qed. i=1 Falls p ein Kreis ist: n w(vi, vi+1) = n (w(vi, vi+1) ) i=1 i=1 JOHNSON(G) 1 compute G , where V [G ] = V [G] {s} and E[G'] = E[G] {(s , v) : v V [G]}, and w(s , v) = 0 for all v V [G] 2 if BELLMAN-FORD(G , w, s) = FALSE 3 then print “ Eingangsgraph hat einen negativen Zyklus” 4 else for each vertex v V [G] do set h(v) to (s, v) computed by BELLMAN-FORD 5 6 for each edge (u, v) E[G ] do w(u, v) w(u, v) + h(u) − h(v) 7 8 for each vertex u V [G] 9 do run DIJKSTRA(G, w , u) to compute (u, v) for all v V [G] for each vertex v V [G] 10 do duv (u, v) + h(v) − h(u) 11 12 return D Beispiel (Johnson -Algorithmus für negative Gewichte) B 2 3 4 8 A1 2 -4 1 7 5 E 6 4D 3C -5 Beispiel (Johnson -Algorithmus für negative Gewichte) B 2 0 3 0 s 0 A1 0 4 8 0 2 0 0 -1 -4 1 7 -4 E5 6 0 4D -5 3C -5 Beispiel (Johnson - Algorithmus für negative Gewichte) h( v ) = ( s, v ) w( u , v ) = w( u , v ) + h( u ) - h( v ) 2B 5 4 1 s A 0 1 0 0 4 0 -1 0 13 -5 0 0 2 10 0 -4 E5 2 0 4D 3C Beispiel (Johnson -Algorithmus für negative Gewichte) d(B) = (A, B) = 2 B -1 4 A d(C) = (A, C) = 2 0 10 13 0 2 -5 0 E 2 d(E) = (A, E) = 0 0 0 -4 C d(D) = (A, D) = 2 0 D d(B) = (A, B) + h(B) − h(A) = 1 d(C) = (A, C) + h(C) − h(A) = - 3 d(D) = (A, D) + h(D) − h(A) = 2 d(E) = (A, E) + h(E) − h(A) = - 4 Beispiel (Johnson -Algorithmus für negative Gewichte) d(A) = (B, A) = 2 B -1 4 A d(C) = (B, C) = 0 0 10 13 0 0 2 0 -4 E -5 2 0 C d(D) = (B,D) = 0 d(E) = (B, E) = 2 d(A) = (B, A) + h(A) − h(B) = 3 d(C) = (B, C) + h(C) − h(B)= -4 0 D d(D) = (B, D) + h(D)− h(B) = 1 d(E) = (B, E) + h(E) − h(B)= -1 Beispiel (Johnson -Algorithmus für negative Gewichte) d(A) = (C, A) = 2 B -1 4 A d(B) = (C, B) = 0 0 10 13 0 -5 0 2 0 0 -4 E 2 C d(D) = (C, D) = 0 d(E) = (C, E) = 2 d(A) = (C, A) + h(A) − h(C) = 7 d(B) = (C, B) + h(B) − h(C) = 4 0 D d(D) = (C,D) + h(D) − h(C) = 5 d(E) = (C, E) + h(E) − h(C) = 3 Beispiel (Johnson -Algorithmus für negative Gewichte) d(A) = (D, A) = 2 B -1 4 A d(B) = (D, B) = 0 0 10 13 0 2 -5 0 0 0 -4 E 2 C d(C) = (D, C) = 0 d(E) = (D, E) = 2 d(A) = (D, A) + h(A) − h(D) = 2 d(B) = (D, B) + h(B) − h(D) = -1 0 D d(C) = (D, C) + h(C) − h(D) = -5 d(E) = (D, E) + h(E) − h(D) = -2 Beispiel (Johnson -Algorithmus für negative Gewichte) d(A) = (E, A) = 4 B -1 4 A d(B) = (E, B) = 2 0 10 13 0 -5 0 2 E 2 d(D) = (E, D) = 2 0 0 -4 C d(C) = (E, C) = 2 d(A) = (E, A) + h(A) − h(E) = 8 0 D d(B) = (E, B) + h(B) − h(E) = 5 d(C) = (E, C) + h(C) − h(E) = 1 d(D) = (E, D) + h(D) − h(E) = 6 Komplexität des Johnson -Algorithmus • Die Initialisierung besteht aus Q (|V|) Schritten • Prüfung auf Existenz negativer Zyklen mit Bellman-Ford-Algorithmus O (|VE|) Schritten • Rekalibrierung Q (|E|) • Dijkstradurchlauf O (|VE|) • Distanzberechnung Q(|V|²) Die Gesamtkomplexität des Johnson-Algorithmus beträgt O (|V|²lg|V| + |V||E|lg|V|)