VL-17: Kürzeste Pfade
(Datenstrukturen und Algorithmen, SS 2017) Walter Unger
SS 2017, RWTH
DSAL/SS 2017 VL-17: Kürzeste Pfade 1/44
Organisatorisches
• Vorlesung: Gerhard Woeginger (Zimmer 4024 im E1) Sprechstunde: Mittwoch 11:15–12:00
• Übungen: Tim Hartmann, David Korzeniewski, Björn Tauer Email:dsal-i1@algo.rwth-aachen.de
• Webseite:http://algo.rwth-aachen.de/Lehre/SS17/DSA.php
• Nächste Vorlesung:
Dienstag, Jul 4, 16:15–17:45 Uhr, Aula 1
DSAL/SS 2017 VL-17: Kürzeste Pfade 2/44
Kürzeste Pfade
• Single-Source Shortest Path
• Bellman-Ford
• Dijkstra
• All-Pairs Shortest Paths
• Transitive Hüllen
• Algorithmus von Warshall
• Algorithmus von Floyd
Das Rechenproblem: Kürzeste Pfade (1)
Das Rechenproblem: Kürzeste Pfade (2)
DSAL/SS 2017 VL-17: Kürzeste Pfade 5/44
Kürzeste Pfade: Das Problem
Beispiel (kürzester Weg)
Eingabe: 1. Eine Strassenkarte, auf der der Abstand zwischen jedem Paar von benachbarten Kreuzungen eingezeichnet ist,
2. eine Startkreuzungs, 3. eine Zielkreuzungt.
Ausgabe: Der kürzeste Weg vons nacht.
DSAL/SS 2017 VL-17: Kürzeste Pfade 6/44
Kürzeste Pfade: Notation
Gegeben ist ein kanten-gewichteter GraphG = (V,E,W).
• Gewicht eines Pfades =Summeder Gewichte der Kanten
• Ein kürzester Pfadvon Knotens∈V zu Knotenv ∈V ist ein Pfad vons nachv mitminimalem Gewicht.
Im Folgenden istδ: (V ×V)→(R∪ {+inf})eine Funktion, sodass:
I δ(s,v)ist das Gewicht des kürzesten Pfades vons nachv, und
I δ(s,v) = +inf, fallsv vons nicht erreichbar ist.
Kürzeste Pfade: Varianten
Es gibt viele verschiedene Problemvarianten:
I Kürzeste Pfade von einem Startknotens zu allen anderen Knoten:
Single-Source Shortest Paths(SSSP).
I Kürzeste Pfade von allen Knoten zu einem Zielknoten t.
Lässt sich auf SSSP zurückführen.
I Kürzester Pfad füreinfestes Knotenpaaru,v.
Es ist kein Algorithmus bekannt, der asymptotisch schneller ist als der beste SSSP-Algorithmus.
I Kürzeste Pfade für alleKnotenpaare.
All-Pairs Shortest Paths(APSP, zweiter Teil dieser Vorlesung).
Single-Source Shortest Path
DSAL/SS 2017 VL-17: Kürzeste Pfade 9/44
Single-Source Shortest Paths
Problem (Single-Source Shortest Path)
Für einen fixen gegebenen Knoten s ∈V (Quelle/source), bestimme für jeden anderen Knoten t∈V
einen kürzesten Pfad vons zut.
DSAL/SS 2017 VL-17: Kürzeste Pfade 10/44
Bellman-Ford
Der Bellman-Ford Algorithmus
I Berechnet kürzeste Pfade von einemeinzigen Startknoten aus
I ErlaubtnegativeKantengewichte.
I Zeigt an, ob es einen Kreis mit negativem Gewichtgibt, der vom Startknoten aus erreichbar ist.
I Falls ein solcher Kreis gefunden wird, gibt eskeineLösung
(da die Gewichte der kürzesten Pfade nicht mehr wohldefiniert sind).
I Sonst verbessert der Algorithmus iterativ für jeden Knoten v eine obere Grenze dist[v]fürδ(s,v), bis das Minimum gefunden wird.
I Kürzeste Pfade können nach Terminierung mittels der im Array prevgespeichertenVorgängerknotenentlang eines kürzesten Pfades rekonstruiert werden.
Bellman-Ford: Grundideen
I Initialisierung:dist[v]=+inf; dist[start]=0.
I Für alle Kanten(v,w)∈E:
Relaxierung: Ist das bisher bekannte Gewichtdist[w]grösser als dist[v]+W(v,w), soverbesseredist[w]auf diesen Wert.
I Wiederhole den vorigen Schritt bis sich nichts mehr ändert, oder brich, falls ein negativer Kreis gefunden wird.
Korrektheit von Bellman-Ford
Falls nach|V|−1 Wiederholungen noch Verbesserungen möglich sind, so gibt es einen negativen Kreis.
Andernfalls giltdist[v] =δ(s,v)für alle Knotenv ∈V. Beweisidee:
Pfad ohne Kreis in(V,E,W)enthält ≤ |V|−1 Zwischenknoten.
DSAL/SS 2017 VL-17: Kürzeste Pfade 13/44
Bellman-Ford in Pseudo-Code
1 bool bellmanFord(List adj[n], int n, int start, 2 int &dist[n], int &prev[n]) {
3
4 for (int v = 0; v < n; v++) // f u e r a l l e K n o t e n v 5 dist[v] = +inf;
6 prev[v] = -1; // v hat n o c h k e i n e n V o r g a e n g e r
7 }
8
9 d i s t [ s t a r t ] = 0; // E n t f e r n u n g von s t a r t zu s t a r t ist 0 10 for ( int i = 1; i < n ; i ++) // n -1 D u r c h l a e u f e
11 for ( int v = 0; v < n ; v ++) // f u e r a l l e K n o t e n v 12 f o r e a c h ( e d g e in adj [ v ])
13 if ( d i s t [ e d g e . t a r g e t ] > d i s t [ v ] + e d g e . w e i g h t ) { 14 d i s t [ e d g e . t a r g e t ] = d i s t [ v ] + e d g e . w e i g h t ; 15 p r e v [ e d g e . t a r g e t ] = v ;
16 } // R e l a x i e r u n g 17
18 for (int v = 0; v < n; v++) // f u e r a l l e K n o t e n v 19 foreach (edge in adj[v])
20 if (dist[edge.target] > dist[v] + edge.weight) 21 return false; // K r e i s mit n e g a t i v e m G e w i c h t 22 r e t u r n t r u e ;
23 }
• Zeitkomplexität:O(|V| · |E|)
DSAL/SS 2017 VL-17: Kürzeste Pfade 14/44
Bellman-Ford: Beispiel
0
14
18 ∞
6
24
∞ ∞
14
6
4
10
9
-12 3
3
8
2
15
Dijkstra
Der Dijkstra Algorithmus
Grundlegende Annahme
Alle Kantengewichte sindnicht-negativ:W(u,v)≥0für alle(u,v)∈E. Ergo: Kürzeste Pfade enthaltenkeine Kreise
Edsger Wybe Dijkstra (1930-2002), Turing Award 1972
DSAL/SS 2017 VL-17: Kürzeste Pfade 17/44
Dijkstra: Grundideen
Wie beim Algorithmus von Prim klassifizieren wir die Knoten in drei Kategorien:
Baumknoten: gehören zum bis jetzt konstruierten Baum
Randknoten: nicht im Baum, aber adjazent zu Knoten im Baum UngeseheneKnoten: alle anderen Knoten.
Grundkonzept:
I Kein Knoten ausserhalb des Baumes hat einen kürzeren Pfad als die Knoten im Baum.
I Jedem Knotenvist ein Wert dist[v]zugeordnet:
I Für Baumknotenv gilt: dist[v] =δ(s,v);
I Für Randknotenv gilt: dist[v] ist Minimum der Gewichte aller Pfade vom Startknoten zuv, wobei die letzte Kante im Schnitt liegt;
I Für ungesehenen Knotenv gilt: dist[v] =+inf.
DSAL/SS 2017 VL-17: Kürzeste Pfade 18/44
Dijkstra Algorithmus: Grundgerüst
1 // u n g e r i c h t e t e r G r a p h G mit n K n o t e n 2 v o i d d i j k s t r a S P ( G r a p h G , int n ) {
3 i n i t i a l i s i e r e a l l e K n o t e n als U N G E S E H E N ( W H I T E ) ; 4 markiere s als BAUM (BLACK) und setze d(s,s) =0;
5 r e k l a s s i f i z i e r e a l l e zu s adj K n o t e n als R A N D ( G R A Y ) ; 6 w h i l e ( es g i b t R a n d k n o t e n ) {
7 w a e h l e von a l l e n K a n t e n z w i s c h e n B a u m k n o t e n t und 8 R a n d k n o t e n v mit minimalem d(s,t) +W(t,v);
9 r e k l a s s i f i z i e r e v als B A U M ( B L A C K ) ; 10 f u e g e K a n t e (t,v) zum B a u m h i n z u ; 11 setze d(s,v) =d(s,t) +W(t,v);
12 r e k l a s s i f i z i e r e a l l e zu v adj U N G E S E H E N e n K n o t e n 13 als R A N D ( G R A Y ) ;
14 }
15 }
Die Unterschiede zu Prim’s Spannbaum Algorithmus sind rot gekennzeichnet
Dijkstra Algorithmus: Beispiel
0
14
6
10
7
5 14
∞
14 6
10 5 5
3 3
4 4
8 8
2
15
15
9
9
Dijkstra Algorithmus: Korrektheit (1)
Theorem
Wir betrachten einen Zeitpunkt während der Exekution des Dijkstra Algorithmus. Es seiB der zu diesem Zeitpunkt konstruierte (rote) Teilbaum. Dann gilt:
1. dist[v]≥δ(s,v) für alle Knotenv ∈V 2. dist[v] =δ(s,v) für alle Baumknotenv ∈B
3. dist[v] =min({∞} ∪ {δ(s,x) +W(x,v)|x ∈B und(x,v)∈E}) für alle Nicht-Baumknotenv ∈V \B;
4. δ(s,v)≤δ(s,u) für alle Baumknotenv ∈B und u∈V\B.
DSAL/SS 2017 VL-17: Kürzeste Pfade 21/44
Dijkstra Algorithmus: Korrektheit (2)
1. dist[v]≥δ(s,v) für alle Knotenv∈V 2. dist[v] =δ(s,v) für alle Baumknotenv∈B
3. dist[v] =min({∞} ∪ {δ(s,x) +W(x,v)|x∈B, (x,v)∈E})fürv∈V\B 4. δ(s,v)≤δ(s,u)für alle Baumknotenv∈Bundu∈V\B
Beweis.
1. und 3. und 4. sind (ziemlich) trivial.
Wir beweisen 2. durch Induktion über die Anzahl der Baumknoten:
I Basis:Bist leer undB={s}(nach erster Iteration): trivial.
I Ind. Schritt: Nimm an, Behauptung gilt für|B|=k>0. Seiv der Knoten der als nächster zum BaumBhinzugefügt wird. Der Falldist[v] = +infist trivial.
Betrachtedist[v]6= +inf. Dann gibt es eine Kante vom Baumknoten
x=prev[v]∈Bzuv6∈B. Aus 3. folgt, dassdist[v]das Gewicht des kürzesten Pfades vonsnachvüber Baumknoten ist. Aus 4. und der Wahl vonvfolgt, dass es keinen kürzeren Pfad gibt, d.h.,dist[v] =δ(s,v).
DSAL/SS 2017 VL-17: Kürzeste Pfade 22/44
Korrektheit
Theorem (Korrektheit)
Der Dijkstra Algorithmus berechnet die kürzesten Abstände
zwischen Knotens und jedem vons aus erreichbaren Knoten inG.
Dijkstra Algorithmus: Implementierung
1 // I n p u t : g e w i c h t e t e r G r a p h mit n Knoten , S t a r t k n o t e n 2 void dijkstra(int adj[n], int n, int start, int &dist[n],
3 int &prev[n]) {
4 for (int i = 0; i < n; i++) { 5 dist[i] = inf; prev[i] = -1;
6 }
7 dist[start] = 0; // s t a r t ist R a n d k n o t e n mit K o s t e n 0 8 Q = 0,...,n-1; // Q e n t h a e l t a l l e u n g e s e h e n e n K n o t e n
9 // und a l l e R a n d k n o t e n
10 w h i l e ( Q not e m p t y ) {
11 // v e r s c h i e b e b i l l i g s t e n R a n d k n o t e n v in B a u m :
12 // e x t r a c t M i n ( Q ) b e s t i m m t E l e m e n t e aus Q mit m i n i m a l e m 13 // d i s t [ e ] , e n t f e r n t e aus Q und g i b t e z u r u e c k 14 v = e x t r a c t M i n ( Q ) ;
15 for e a c h ( e d g e in adj [ v ]) // a k t u a l i s i e r e K o s t e n
16 // f u e r R a n d k n o t e n
17 if ( e d g e . t a r g e t in Q and
18 d i s t [ v ]+ e d g e . w e i g h t < d i s t [ e d g e . t a r g e t ]) { 19 d i s t [ e d g e . t a r g e t ] = d i s t [ v ] + e d g e . w e i g h t ; 20 p r e v [ e d g e . t a r g e t ] = v ;
21 }
22 }
23 }
Dijkstra Algorithmus: Anmerkungen
I Die kürzesten Wege werden mit zunehmendem Abstand zur Quelles gefunden.
I Implementierung: Ähnlich zum Spannbaum Algorithmus von Prim.
I Zeitkomplexität im Worst-Case:Θ(|V|2).
I Untere Schranke der Komplexität:Ω(|E|)
(Im schlimmsten Fall müssen alle Kanten überprüft werden.)
I Platzkomplexität:O(|V|).
I Dijkstra erlaubt keine negative Kosten. Warum?
DSAL/SS 2017 VL-17: Kürzeste Pfade 25/44
All-Pairs Shortest Paths
DSAL/SS 2017 VL-17: Kürzeste Pfade 26/44
All-Pairs Shortest Paths
Wir betrachten gewichtete gerichtete GraphenG= (V,E,W).
I Negative Gewichte sind erlaubt, aber keine Kreise mit negativem Gewicht.
I Nicht vorhandene Kanten haben GewichtW(·,·) = +inf.
Problem (All-Pairs Shortest Path)
Berechne für jedes Paari,j das GewichtD[i,j]des kürzesten Pfades.
Naive Lösung:
• Wende SSSP-Algorithmus (z.B. Bellman-Ford)|V|mal an.
• Dies führt zu Worst-Case ZeitkomplexitätO(|V|4).
• Effizientere Version:Floyd’s Algorithmus
Binäre Relationen
Binäre Relation
Eine(binäre) RelationR über einer MengeS ist eine TeilmengeR⊆S×S =S2.
Reflexivität, Transitivität
Eine RelationR ist
• reflexiv, wenn(u,u)∈R für alleu∈S
• transitiv, wenn aus(u,v)∈R und (v,w)∈R immer (u,w)∈R folgt
Transitive Hülle
Dietransitive Hülle R∗ einer RelationR ist die kleinste Erweiterung (Obermenge)R⊆R∗⊆S2, sodassR∗ reflexiv und transitiv ist.
Transitive Hülle eines Graphen
Für Graphen mit MengeS =V und RelationR=E gilt:
(u,v)∈R∗ gdw. es gibt Pfad vonunachv.
Transitive Hülle: Beispiel
R=
0 1 0 0 1
0 0 0 1 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
und transitive HülleR∗=
1 1 1 1 1
0 1 1 1 0
0 1 1 1 0
0 1 1 1 0
0 1 1 1 1
D
C B
A
E
Binäre RelationR
D
C B
A
E
Transitive HülleR∗
DSAL/SS 2017 VL-17: Kürzeste Pfade 29/44
Algorithmus von Warshall
DSAL/SS 2017 VL-17: Kürzeste Pfade 30/44
Algorithmus von Warshall: Idee (1)
AusR[i,k]und R[k,j]folgt ErreichbarkeitR[i,j].
k
i j
1 f o r e a c h (k∈V)
2 f o r e a c h ( e i n g e h e n d e K a n t e (i,k)∈E) 3 f o r e a c h ( a u s g e h e n d e K a n t e (k,j)∈E) 4 F u e g e (i,j) zu E h i n z u .
Algorithmus von Warshall: Idee (2)
Das reicht bereits aus, um längere Pfade zu berücksichtigen:
1 2
Die Reihenfolge spielt dabei keine Rolle:
2 1
Algorithmus von Warshall: Idee (3)
Allgemeiner Fall:
i j
k
⊆ {1, . . .,k−1} ⊆ {1, . .. ,k−1}
Das lässt sich als Rekursionsgleichung schreiben, wobeitij(k)=true
besagt, dass nach Berücksichtigung der Zwischenknoten{1, . . . ,k}der Knotenj voni aus erreichbar ist:
tij(k)=tij(k−1)∨
tik(k−1)∧tkj(k−1)
DSAL/SS 2017 VL-17: Kürzeste Pfade 33/44
Algorithmus von Warshall: Idee (4)
tij(k) =
false fürk =0, falls(i,j)6∈E
true fürk =0, falls(i,j)∈E
tij(k−1)∨
tik(k−1)∧tkj(k−1)
fürk >0
Da zur Berechnung vontij(k) nur die Wertetuv(k−1) gebraucht werden und da keine älteren Wertetuv(j) mitj <k−1 gebraucht werden, kann die Berechnungdirektim Ausgabearray (in-place) erfolgen.
DSAL/SS 2017 VL-17: Kürzeste Pfade 34/44
Algorithmus von Warshall: Beispiel (1)
Eine mögliche Nummerierung der Knoten:
0
1
2
3
5
4 6
7
x x x x xx· ·
·x·x·x· ·
· ·x·x· · ·
· · ·x·x· ·
· · · ·x·x·
· · · ·x x·x
· · · ·x·
· · · ·x
Algorithmus von Warshall: Beispiel (2)
Eine andere Nummerierung der Knoten:
0
1
4
7
6
5 2
3
x x x·x x·x
·x· · · · ·x
· ·x· · · · ·
· · ·x· · · ·
· ·x·x x· ·
· ·x· ·x· ·
· ·x x·x x·
· ·x x·xx x
Permutierung der Knoten liefert gleiche Matrixdarstellung vonR∗wie zuvor
1 f o r e a c h (k∈V)
2 f o r e a c h ( e i n g e h e n d e K a n t e (i,k)∈E) 3 f o r e a c h ( a u s g e h e n d e K a n t e (k,j)∈E) 4 F u e g e (i,j) zu E h i n z u .
1 v o i d t r a n s C l o s ( b o o l A [ n ][ n ] , int n , b o o l & R [ n ][ n ]) { 2 for ( int i = 0; i < n ; i ++)
3 for ( int j = 0; j < n ; j ++)
4 R [ i , j ] = A [ i , j ]; // K o p i e r e A n a c h R 5
6 for ( int i = 0; i < n ; i ++)
7 R [ i , i ] = t r u e ; // r e f l e x i v e H u e l l e 8
9 for ( int k = 0; k < n ; k ++) 10 for ( int i = 0; i < n ; i ++)
11 for ( int j = 0; j < n ; j ++)
12 R [ i , j ] = R [ i , j ] || ( R [ i , k ] && R [ k , j ]) ; 13 }
Zeitkomplexität:Θ(|V|3); Platzkomplexität:Θ(|V|2)
DSAL/SS 2017 VL-17: Kürzeste Pfade 37/44
Algorithmus von Floyd
DSAL/SS 2017 VL-17: Kürzeste Pfade 38/44
Der Algorithmus von Floyd
Zurück zu All-Pairs Shortest Paths:
• Algorithmus von Floyd löst APSP
• Grundidee: Erweiterung von Algorithmus von Warshall (Literatur nennt den Algorithmus oft“Floyd-Warshall”)
Der Algorithmus von Floyd: Idee
i j
k
⊆ {1
, . . .,k−1} ⊆ {1, . .. ,k−1}
25 10
36
Wir gehen wie bei Warshall vor, jedoch mit der folgenden angepassten Rekursionsgleichung:
dij(k)=
W(i,j) fürk =0
min
dij(k−1),dik(k−1)+dkj(k−1)
fürk >0
statt:tij(k)=tij(k−1)∨
tik(k−1)∧tkj(k−1)
Der Algorithmus von Floyd: Idee
i j
k
⊆ {1
, . . .,k−1} ⊆ {1, . .. ,k−1}
25 10
35
Wir gehen wie bei Warshall vor, jedoch mit der folgenden angepassten Rekursionsgleichung:
dij(k) =
W(i,j) fürk =0
min
dij(k−1),dik(k−1)+dkj(k−1)
fürk >0
statt:tij(k)=tij(k−1)∨
tik(k−1)∧tkj(k−1)
DSAL/SS 2017 VL-17: Kürzeste Pfade 40/44
Der Algorithmus von Floyd: Beispiel
https://www.cs.usfca.edu/~galles/visualization/Floyd.html
DSAL/SS 2017 VL-17: Kürzeste Pfade 41/44
Der Algorithmus von Floyd: Implementierung
1 v o i d f l o y d S P ( d o u b l e W [ n ][ n ] , int n , d o u b l e & D [ n ][ n ]) { 2 for ( int i = 0; i < n ; i ++)
3 for ( int j = 0; j < n ; j ++)
4 D [ i , j ] = W [ i , j ]; // K o p i e r e W n a c h D 5
6 for ( int i = 0; i < n ; i ++) // r e f l e x i v e H u e l l e 7 D [ i , i ] = 0;
8
9 for ( int k = 0; k < n ; k ++) 10 for ( int i = 0; i < n ; i ++)
11 for ( int j = 0; j < n ; j ++)
12 D [ i , j ] = min ( D [ i , j ] , D [ i , k ] + D [ k , j ]) ; 13 }
I Zeitkomplexität:Θ(|V|3); Platzkomplexität: Θ(|V|2).
I Hier nicht behandelt: Der Algorithmus kann auch mit negativen Kreisen umgehen.
Der Algorithmus von Floyd: Erweiterung
I Der angegebene Algorithmus berechnet nur die Länge der Pfade.
I Der Algorithmus lässt sich leicht auf die Berechung von Pfaden erweitern (z. B. Routingtabellen).
I Dazu speichern wir für jedes Paar (i,j)jeweils den letzten Zwischenknotenπij des kürzesten Pfades voni nachj (das heisst, πij ist der Vorgänger vonj auf diesem Pfad).
π(k)ij =
i fürk =0,i6=j, fallsW(i,j)6= +inf null fürk =0, sonst
π(k−1)kj fürk >0, fallsdij(k−1)>dik(k−1)+dkj(k−1) π(k−1)ij sonst
Organisatorisches
• Nächste Vorlesung:
Dienstag, Jul 4, 16:15–17:45 Uhr, Aula 1
• Webseite:http://algo.rwth-aachen.de/Lehre/SS17/DSA.php
DSAL/SS 2017 VL-17: Kürzeste Pfade 44/44