Vorlesung Informatik B, 10. 07. 2006 THEMA: Ku rzeste Wege in Graphen: Die Algorithmen von Dijkstra und Floyd{ Warshall; Transitive Hu lle Bestimmung ku rzester Wege in Graphen Sei G = (V; E ) ein gerichteter Graph mit einer Gewichtsfunktion w, die den Kanten nichtnegative Gewichte (`Lange' der Kante) zuordnet. Wir denieren das Gewicht eines Weges als die Summe der Gewichte seiner Kanten. Ziel ist es fur einen Startknoten s leichteste (`kurzeste') Wege zu allen anderen erreichbaren Knoten zu berechnen. Voru berlegungen: Die Eigenschaft `kurzester Weg' vererbt sich auf Teilwege. Ist also p ein kurzester Weg von u nach v u ber die Zwischenknoten u0 und v0 , dann ist der induzierte Teilweg von u0 nach v0 auch ein kurzester. Damit kann man kurzeste Wege von s zu allen erreichbaren Knoten so auswahlen, dass sie einen Baum bilden, den man kompakt mittels der {Zeiger verwaltet. Breitensuche ist ein Spezialfall eines Kurzesten-Wege-Problems, alle Kanten haben Gewicht 1. Der Algorithmus von Dijkstra Idee des Algorithmus In Analogie zum MST{Algorithmus von Prim vergroern wir in jedem Schritt eine Menge S von Knoten, fur die die Abstande von s und kurzeste Wege bereits bestimmt sind. Der Algorithmus ist `greedy'. Fur jeden Knoten v in V n S halten wir einen Schlussel d[v] aufrecht, der sagt, wie lang der bisher bekannte kurzeste Weg von s nach v ist und wir merken uns auch, wer der Vorganger [v] auf einem solchen Weg ist. Die Schlussel d[v] werden in einer Prioritats{Warteschlange verwaltet. Wir nehmen den Kopf u der Warteschlange in S auf und mussen uberprufen, ob man dessen Nachbarn v von s aus uber u besser (d.h. auf kurzerem Weg als bisher bekannt) erreichen kann. Dies nennt man Relaxieren und formal ist es folgendes: Falls d[v] > d[u] + w(u; v) dann ersetze d[v] durch d[u] + w(u; v) und [v] zeigt jetzt auf u. u v Adj[u] s Für Knoten in S sind kürzeste wege schon gefunden (dick) Knoten u hat kleinsten Schlüssel in V\S. .Er wird in S aufgenommen und seine Nachbarn überprüft (relaxieren) Hier ist der formale Pseudocode. (G; w; s) 01 for jede Ecke v 2 V(G) 02 do d[v] 1 Dijkstra 03 [v] 04 d[s] 0 05 S 06 Q ; Nil V(G) 07 while Q 6= ; 08 do u 09 S 10 for jede Ecke v 11 Extract-Min(Q) S [ fug do if d[v] 12 2 Adj[u] > d[u] + w(u,v) then d[v] d[u] + w(u,v) [v] = u 13 Was ist die Komplexitat dieses Algorithmus? Dies hangt von der konkreten Realisierung der Prioritatsschlange ab: Verwendet man unsortierte Arrays, so kostet Extract-Min im worst case Lange des Arrays, wahrend die Update-Operation in konstanter Zeit geht, also insgesamt O(jV j ). 2 Verwendet man binare Heaps, so kostet Extract-Min O(log jV j) und jeder Update O(log jV j), insgesamt O(jE j log jV j). Korrektheitsbeweis fu r den Algorithmus: Bezeichne d (s; v) die Lange eines kurzesten Weges von s nach v. Wir mussen zeigen, dass in dem Moment, wenn v in S aufgenommen wird, d (s; v) = d[v] gilt (danach wird d[v] ja nicht mehr verandert!) Zunachst ist klar, dass im Algorithmus zu jedem Zeitpunkt und fur jeden Knoten v gilt d[v] d (s; v). G G G Indirekter Beweis: u S s x y Ein kürzester Weg von s nach u, y der erste Knoten außerhalb von S, x sein Vorgänger in S 1. Sei u der zeitlich erste (!!) Knoten, fur den bei Aufnahme in S gilt d[u] > d (s; u), also der Algorithmus einen Fehler macht. 2. Sei y erster Knoten auf einem tatsachlich kurzestem Weg von s nach u, der in V n S liegt. Fur y gilt aber d[y] = d (s; y). Weil: d[x] ist korrekt fur Vorganger x 2 S von y. Nach Annahme war u ja erster Fehler. Bei Aufnahme von x in S wurde Kante (x; y) relaxiert, also ist auch d[y] korrekt. Wenn y = u, sind wir hier schon fertig. 3. Ansonsten haben wir: d[u] > d (s; u) = d (s; y) + d (y; u) = d[y] + d (y; u): Aber damit ist d[u] > d[y] bei der Aufnahme von u in S , weil d (y; u) nichtnegativ ist. 4. Das ist aber der gesuchte Widerspruch, der Algorithmus hat u genommen, hatte aber tatsachlich y mit dem kleineren Schlussel nehmen mussen. G G G G G G G Der Algorithmus von Floyd-Warshall Ziel ist es, fur einen gerichteten Graphen G = (V; E ) und eine Kantengewichtsfunktion w alle Abstande zwischen Knoten und realisierende kurzeste Wege zu nden. Das heit dann All-PairShortest-Path-Problem. Wir setzen wieder voraus, dass es keine Kreise mit negativem Gesamtgewicht gibt. Naturlich lat sich dieses Problem durch Anwendung des Dijkstra-Algorithmus fur jeden moglichen Startknoten s losen. Wir werden aber sehen, dass es konzeptionell viel einfacher geht und insbesondere nur elementare Datenstrukturen notig sind. Das dahinter stehende algorithmische Prinzip ist das des sogenannten Dynamischen Programmierens. Die Idee: Wir benennen die Knoten mit 1; ::::; n. Ziel ist, eine n n{Matrix D zu berechnen, deren Eintrage d die Lange eines kurzesten gerichteten Weges von i nach j angibt. Zusatzlich werden wir mit -Verweisen wie bei Dijkstra tatsachlich kurzeste Wege implizit verwalten. Sei also die n n{Matrix, deren Eintrag der Vorganger des Knotens j auf einem kurzesten i;j i;j Weg von i nach j ist. Wir werden zunachst starke Einschrankungen auf das Problem legen, die eingeschrankten Problem losen und dann die Einschrankungen wieder Schritt fur Schritt wegnehmen, bis wir wieder beim Ausgangsproblem sind. Wie sehen die Einschrankungen aus? Wir betrachten fur die Knoten i; j nicht mehr alle Wege von i nach j und suchen darunter einen kurzesten, sondern wir schranken die Menge der moglichen Zwischenknoten (die diese Wege benutzen konnen, aber nicht mussen) ein. Wir denieren: Sei d die Lange eines kurzesten Weges von i nach j , der nur Zwischenknoten aus der Knotenmenge f1; :::; kg benutzt. Nichtdenierte Werte werden auf 1 gesetzt. Fur k = 0 erlauben wir gar keine Zwischenknoten! (k ) i;j Die sind analog deniert, NIL falls der Knoten nicht existiert. (k ) i;j Was hilft uns dies? Die Matrizen D der d {Werte und die Matrizen der Knoten lassen sich bottom{up sehr einfach berechnen. Initialisierung: Die Matrizen D und kann man direkt aus der Adjazenzmatrix A = (a ) des Graphen (dies ist hier die geeignete Datenstruktur, a die Lange der Kante) ablesen. i;j (0) i;j k) gewinnt man aus D (k 1) i;j 8 <0 =: a 1 8 <i =: i NIL i;j i;j i=j 9i ! j sonst i=j 9i ! j sonst mittels der folgenden rekursiven Beziehung: = minfd ; d +d g Denn wenn der Knoten k als Zwischenknoten vorkommt dann nur ein Mal!! Und analog: d( k) (k i;j ( k) i;j = ( k i;j k 1) 1) k;j falls d sonst Es ist oensichtlich D = D und = . (n) 1) i;j ( ( (n) (k ) i;j (0) d(0) D( (k ) i;j (0) (k ) (k ) Zentrale Beobachtung: (k 1) (k i;k (k i;j 1) 1) k;j < d( k i;k 1) +d (k k;j 1) Alle Zwischenknoten stammen aus der Knotenmenge {1,....,k} i j k Nur Zwischenknoten aus der Knotenmenge {1,.......,k−1} Π (k) i,j Nur Zwischenknoten aus der Knotenmenge {1,.......,k−1} (k−1) Π (k) = Π i,j i,j Alle Zwischenknoten aus {1,....,k}, ABER der Knoten k tritt nicht auf! j i Dies ergibt sofort den folgenden Algorithmus fur die Bestimmung von D : (n) (W ) (Ecken 1,2,...,n; W ist n n{Matrix der Kantengewichte, 1 falls (i,j) keine Kante ist und =Kantengewicht sonst) Floyd{Warshall 0 f ur i=j , = 01 n rows[W] 02 D(0) 03 for k 04 d.h. wi;j = W 1 to n do for i 05 1 to n do for j 1 to n (k ) 06 (n) di;j (k min( di;j 1) (k , di;k 1) (k + dk;j 1) ) 07 return D Dieser Algorithmus hat eine Komplexitat von O(n ). 3 : Die transitive Hulle eines gerichteten Graphen G = (V; E ) ist der Graph G = (V; E ), wobei es in G eine Kante von i nach j gibt, falls in G ein gerichteter Weg von i nach j existiert. Eine Variante Man kann eine einfache Abwandlung des Floyd{Warshall dazu benutzen, die transitive Hulle zu bestimmen. Dazu sei t eine Boolesche Variable, die 1 ist, falls ein gerichteter Weg mit Zwischenknoten aus f1; :::; kg von i nach j existiert und 0 sonst. Oensichtlich gilt: t =t _ (t ^ t ) mit der Initialisierung 8 1 i=j < t = 1 9i ! j : 0 sonst (k ) i;j (k ) (k i;j i;j (0) i;j 1) (k i;k 1) (k k;j 1)