Dijkstras Algorithmus: Pseudocode initialize d, parent all nodes are non-scanned while 9 non-scanned node u with d[u] < • u := non-scanned node v with minimal d[v ] relax all edges (u, v ) out of u u is scanned now Behauptung: Am Ende definiert d die optimalen Entfernungen und parent die zugehörigen Wege 361 Beispiel 2 3 2 b c a 2 9 5 s 10 8 1 0 e f 4 d 0 7 10 2 3 5 7 2 a b c 2 9 5 s 10 8 1 0 e f 4 d 0 7 6 6 2 3 5 2 a b c 2 9 5 s 10 8 1 0 e f 4 d 0 7 10 7 2 3 5 2 a c b 2 9 5 s 10 8 1 0 f e 4 d 0 7 6 6 2 3 5 7 2 a c b 2 9 5 s 10 8 1 0 e f 4 d 0 10 6 7 2 3 5 7 2 a b c 2 9 5 s 10 8 1 0 f e 4 d 0 6 6 7 362 Korrektheit Annahme: alle Kosten nicht-negativ! Wir zeigen: 8v 2 V : I v erreichbar =) v wird irgendwann gescannt I v gescannt =) µ(v ) = d[v ] 363 v erreichbar =) v wird irgendwann gescannt Annahme: v ist erreichbar, aber wird nicht gescannt z s = v1 | gescannt ungescannt ungescannt }| { z}|{ z }| { ! v1 ! · · · ! vi 1 ! vi ! · · · ! vk = v {z } ein kürzester s–v Pfad =) vi 1 wird gescannt =) Kante vi 1 ! vi wird relaxiert =) d[vi ] < • Widerspruch – nur Knoten x mit d[x] = • werden nie gescannt ? 364 v erreichbar =) v wird irgendwann gescannt Annahme: v ist erreichbar, aber wird nicht gescannt z s = v1 | gescannt ungescannt ungescannt }| { z}|{ z }| { ! v1 ! · · · ! vi 1 ! vi ! · · · ! vk = v {z } ein kürzester s–v Pfad =) vi 1 wird gescannt =) Kante vi 1 ! vi wird relaxiert =) d[vi ] < • Widerspruch – nur Knoten x mit d[x] = • werden nie gescannt Ups: Spezialfall i = 1? Kann auch nicht sein. v1 = s wird nach Initialisierung gescannt. 364 v gescannt =) µ(v ) = d[v ] Annahme: v gescannt und µ(v ) < d[v ] OBdA: v ist der erste gescannte Knoten mit µ(v ) < d[v ]. t := Scan-Zeit von v z s = v1 | Scan-Zeit t Scan-Zeit < t Scan-Zeit = t }| { z}|{ z }| { ! v1 ! · · · ! vi 1 ! vi ! · · · ! vk = v {z } ein kürzester s–v Pfad Also gilt zur Zeit t: µ(vi 1) = d[vi 1] vi 1 ! vi wurde relaxiert z}|{ =) d[vi ] d[vi 1 ] + c(vi 1 , vi ) = µ(vi ) µ(v )< d[v ] =) vi wird vor v gescannt. Widerspruch! Wieder: Spezialfall i = 1 unmöglich. 365 Implementierung? initialize d, parent all nodes are non-scanned while 9 non-scanned node u with d[u] < • u := non-scanned node v with minimal d[v ] relax all edges (u, v ) out of u u is scanned now Wichtigste Operation: finde u 366 Prioritätsliste Wir speichern ungescannte erreichte Knoten in addressierbarer Prioritätsliste Q. Schlüssel ist d[v ]. Knoten speichern handles. oder gleich items 367 Implementierung ⇡ BFS mit PQ statt FIFO Function Dijkstra(s : NodeId) : NodeArray⇥NodeArray // returns (d, parent) Initialisierung: d=h•, . . . , •i : NodeArray of R [ {•} // tentative distance from root parent=h?, . . . , ?i : NodeArray of NodeId parent[s]:= s // self-loop signals root Q : NodePQ // unscanned reached nodes d[s] := 0; Q.insert(s) 368 Function Dijkstra(s : NodeId) : NodeArray⇥NodeArray d = h•, . . . , •i; parent[s]:= s; d[s] := 0; Q.insert(s) while Q 6= 0/ do u := Q.deleteMin // scan u foreach edge e = (u, v ) 2 E do if d[u] + c(e) < d[v ] then d[v ]:= d[u] + c(e) parent[v ] := u // if v 2 Q then Q.decreaseKey(v ) else Q.insert(v ) return (d, parent) s u scanned // relax update tree u v reached 369 Beispiel 2 3 2 b c a 2 9 5 s 10 8 1 0 e f 4 d 0 7 10 2 3 5 7 2 a b c 2 9 5 s 10 8 1 0 e f 4 d 0 7 6 6 2 3 5 2 a b c 2 9 5 s 10 8 1 0 e f 4 d 0 7 10 7 2 3 5 2 a c b 2 9 5 s 10 8 1 0 f e 4 d 0 7 6 6 2 3 5 7 2 a c b 2 9 5 s 10 8 1 0 e f 4 d 0 7 10 6 2 3 5 7 2 a b c 2 9 5 s 10 8 1 0 f e 4 d 0 7 6 6 Operation insert(s) deleteMin 2 relax s ! a 10 relax s ! d deleteMin 3 relax a ! b deleteMin 2 relax b ! c 1 relax b ! e deleteMin 9 relax e ! b 8 relax e ! c 0 relax e ! d deleteMin 4 relax d ! s 5 relax d ! b deleteMin (s, 0) (a, 2) (b, 5) (e, 6) (d, 6) (c, 7) Queue h(s, 0)i hi h(a, 2)i h(a, 2), (d, 10)i h(d, 10)i h(b, 5), (d, 10)i h(d, 10)i h(c, 7), (d, 10)i h(e, 6), (c, 7), (d, 10)i h(c, 7), (d, 10)i h(c, 7), (d, 10)i h(c, 7), (d, 10)i h(d, 6), (c, 7)i h(c, 7)i h(c, 7)i h(c, 7)i hi 370 Dijkstra: Laufzeit Function Dijkstra(s : NodeId) : NodeArray⇥NodeArray Initialisierung: d=h•, . . . , •i : NodeArray of R [ {•} // O(n) parent=h?, . . . , ?i : NodeArray of NodeId // O(n) parent[s]:= s Q : NodePQ // unscanned reached nodes, O(n) d[s] := 0; Q.insert(s) 371 Dijkstra: Laufzeit Function Dijkstra(s : NodeId) : NodeArray⇥NodeArray d = {•, . . . , •}; parent[s]:= s; d[s] := 0; Q.insert(s) while Q 6= 0/ do u := Q.deleteMin foreach edge e = (u, v ) 2 E do if d[u] + c(e) < d[v ] then d[v ]:= d[u] + c(e) parent[v ] := u if v 2 Q then Q.decreaseKey(v ) else Q.insert(v ) return (d, parent) // O(n) // n⇥ // m⇥ // m⇥ // m⇥ // m⇥ // m⇥ // n⇥ 372 Dijkstra: Laufzeit Function Dijkstra(s : NodeId) : NodeArray⇥NodeArray d = {•, . . . , •}; parent[s]:= s; d[s] := 0; Q.insert(s) while Q 6= 0/ do u := Q.deleteMin foreach edge e = (u, v ) 2 E do if d[u] + c(e) < d[v ] then d[v ]:= d[u] + c(e) parent[v ] := u if v 2 Q then Q.decreaseKey(v ) else Q.insert(v ) return (d, parent) // O(n) // n⇥ // m⇥ // m⇥ // m⇥ // m⇥ // m⇥ // n⇥ Insgesamt: TDijkstra = O m · TdecreaseKey (n) + n · (TdeleteMin (n) + Tinsert (n)) 372 Laufzeit Dijkstras ursprüngliche Implementierung: „naiv“ I insert: O(1) d[v ]:= d[u] + c(u, v ) I decreaseKey: O(1) d[v ]:= d[u] + c(u, v ) I deleteMin: O(n) d komplett durchsuchen TDijkstra = O m · TdecreaseKey (n) + n · (TdeleteMin (n) + Tinsert (n)) TDijkstra59 = O(m · 1 + n · (n + 1)) = O m + n2 373 Laufzeit Bessere Implementierung mit Binary-Heap-Prioritätslisten: I insert: O(log n) I decreaseKey: O(log n) I deleteMin: O(log n) TDijkstra = O m · TdecreaseKey (n) + n · (TdeleteMin (n) + Tinsert (n)) TDijkstraBHp = O(m · log n + n · (log n + log n)) = O((m + n) log n) 374 Laufzeit (Noch) besser mit Fibonacci-Heapprioritätslisten: I insert: O(1) I decreaseKey: O(1) (amortisiert) I deleteMin: O(log n) (amortisiert) TDijkstra = O m · TdecreaseKey (n) + n · (TdeleteMin (n) + Tinsert (n)) TDijkstraFib = O(m · 1 + n · (log n + 1)) = O(m + n log n) Aber: konstante Faktoren in O(·) sind hier größer! 375 Analyse im Mittel Modell: Kantengewichte sind „zufällig“ auf die Kanten verteilt Dann gilt: ⇣ m⌘ E[TDijkstraBH(ea)p ] = O m + n log n log n Beweis: In Algorithmen II 376 Monotone ganzzahlige Prioritätslisten Beobachtung: In Dijkstras Algorithmus steigt das Minimum in der Prioritätsliste monoton. Das kann man ausnutzen. schnellere Algorithmen u.U. bis herunter zu O(m + n). Details: in Algorithmen II 377 Negative Kosten Was machen wir, wenn es Kanten mit negativen Kosten gibt? Es kann Knoten geben mit d[v ] = • s p u C q v s p uC (2) q v ... Wie finden wir heraus, welche das sind? Endlosschleifen vermeiden! a b d 42 0 −00 −00 −00 0 +00−2 −00 j k 0 −1 0 s f 2 2 g 5 −3 −2 −1 −3 −1 i −2 h 378 Zurück zu Basiskonzepten (Abschnitt 10.1 im Buch) Lemma: 9 kürzester s–v -Pfad P =) P ist OBdA einfach(eng. simple) Beweisidee: (Kontraposition) Fall: v über negativen Kreis erreichbar ) ¬9 kürzester s–v -Pfad (sondern beliebig viele immer kürzere) q v s p q v ... s p (2) u C uC Sonst: betrachte beliebigen nicht-einfachen s–v -Pfad. Alle Kreise streichen einfacher, nicht längerer Pfad. a b d 42 0 −00 −00 −00 0 +00−2 −00 j k 0 −1 0 s f 2 2 g 5 −3 −2 −1 −3 −1 i −2 h 379 Mehr Basiskonzepte Übung, zeige: Teilpfade kürzester Pfade sind selbst kürzeste Pfade a b c d a b, b c, c d, a b c, b c d Übung: Kürzeste-Wege-Baum Alle kürzeste Pfade von s aus zusammen bilden einen Baum, falls es keine negativen Kreise gibt. 2 3 5 7 2 a b c 2 9 5 s 10 8 1 0 f e 4 d 6 0 6 7 380