zu Breitensuche, Tiefensuche, Dijkstras Algorithmus

Werbung
Kap. 9: Graphtraversierung
Ausgangspunkt oder Baustein fast jedes nichttrivialen Graphenalgorithmus
326
Graphtraversierung als Kantenklassifizierung
forward
s
tree
backward
cross
327
Graphtraversierung als Kantenklassifizierung
I
Baumkanten: Elemente des Waldes, der bei der Suche gebaut wird
I
Vorwärtskanten: verlaufen parallel zu Wegen aus Baumkanten
I
Rückwärtskanten: verlaufen antiparallel zu Wegen aus Baumkanten
I
Querkanten: alle übrigen
forward
s
tree
backward
cross
328
Breitensuche
Baue Baum von Startknoten s,
der alle von s erreichbaren Knoten
mit möglichst kurzen Pfaden erreicht.
Berechne Abstände:
b
s
0
e
g
c
d
f
1
2
tree
backward
cross
forward
3
329
Breitensuche
I
Einfachste Form des Kürzeste-Wege-Problems
I
Umgebung eines Knotens definieren
(ggf. begrenzte Suchtiefe)
I
Einfache, effiziente Graphtraversierung
(auch wenn Reihenfolge egal)
b
s
0
e
g
c
d
f
1
2
tree
backward
cross
forward
3
330
Breitensuche
Algorithmenidee: Baum Schicht für Schicht aufbauen
b
s
0
e
g
c
d
f
1
2
tree
backward
cross
forward
3
331
Function bfs(s) :
Q:= hsi
// aktuelle Schicht
while Q 6= hi do
exploriere Knoten in Q
merke dir Knoten der nächsten Schicht in Q 0
Q:= Q 0
b
s
0
e
g
c
d
f
1
2
tree
backward
cross
forward
3
332
Repräsentation des Baums
Feld parent speichert Vorgänger.
I
I
noch nicht erreicht: parent[v ] = ?
Startknoten/Wurzel: parent[s] = s
b
s
e
c
d
g
tree
parent
f
333
Function bfs(s : NodeId) : (NodeArray of NodeId)
⇥(NodeArray of N0 [ {•})
d=h•, . . . , •i : NodeArray of N0 [ {•};
d[s]:= 0
parent=h?, . . . , ?i : NodeArray of NodeId;
parent[s]:= s
Q = hsi, Q 0 = hi : Set of NodeId
// current, next layer
for (`:= 0; Q 6= hi; `++ )
invariant Q contains all nodes with distance ` from s
foreach u 2 Q do
foreach (u, v ) 2 E do
// scan u
if parent(v ) = ? then
// unexplored
Q 0 := Q 0 [ {v }
d[v ]:= ` + 1;
parent(v ):= u
(Q, Q 0 ):= (Q 0 , hi)
// next layer
return (parent, d)
// BFS = {(v , w ) : w 2 V , v = parent(w )}
334
Repräsentation von Q und Q 0 mittels FIFO
Q, Q 0 ! einzelne FIFO-Queue
I
Standardimplementierung in anderen Büchern
+ „Oberflächlich“ einfach
Korrektheit mglw. weniger evident
= Effizient (?)
Übung!
Übung: ausprobieren!
335
Alternative Repräsentation von Q und Q 0
I
Zwei Stapel
I
Schleife 1⇥ ausrollen
loop Q ! Q 0 ; Q 0 ! Q
I
Beide Stapel in ein Feld der Größe n
Q
!
Q0
336
Tiefensuche
tree
backward s
cross
forward
b
d
e
g
f
c
337
Tiefensuchschema für G = (V , E )
unmark all nodes;
init
foreach s 2 V do
if s is not marked then
mark s
root(s)
DFS(s, s)
// make s a root and grow
// a new DFS tree rooted at s
Procedure DFS(u, v : NodeId)
// Explore v coming from u
foreach (v , w ) 2 E do
if w is marked then traverseNonTreeEdge(v , w )
else
traverseTreeEdge(v , w )
mark w
DFS(v , w )
backtrack(u, v )
// return from v along the incoming edge
338
DFS-Baum
init:
root(s):
traverseTreeEdge(v , w ):
parent=h?, . . . , ?i : NodeArray of NodeId
parent[s]:= s
parent[w ]:= v
tree
s
b
parent
mark s
d
root(s)
dfs(s,s)
traverseTreeEdge(s,b)
mark b
dfs(s,b)
s
b
d
e
g
f
c
e
g
f
c
339
dfs(s,b)
traverseTreeEdge(b,e)
mark(e)
dfs(b,e)
traverseTreeEdge(e,g)
mark(g)
dfs(e,g)
traverseNonTreeEdge(g,b)
traverseTreeEdge(g,f)
mark(f)
dfs(g,f)
backtrack(g,f)
backtrack(e,g)
traverseNonTreeEdge(e,f)
traverseTreeEdge(e,c)
mark(c)
dfs(e,c)
backtrack(e,c)
backtrack(b,e)
backtrack(s,b)
s
b
e
d
s
b
b
e
b
d
g
f
c
e
d
s
f
c
d
s
g
g
f
c
e
g
f
c
340
traverseTreeEdge(s,d)
mark(d)
dfs(s,d)
traverseNonTreeEdge(d,e)
traverseNonTreeEdge(d,f)
backtrack(s,d)
backtrack(s,s)
s
b
e
d
s
b
d
g
f
c
e
g
f
c
341
DFS-Nummerierung
init:
root(s):
traverseTreeEdge(v , w ):
dfsPos=1 : 1..n
dfsNum[s]:= dfsPos++
dfsNum[w ]:= dfsPos++
u v :, dfsNum[u] < dfsNum[v ] .
Beobachtung:
Knoten auf dem Rekursionsstapel sind bzgl.
1
tree
backward s
cross
forward
2
b
3
e
sortiert
4
g
d
c
7
6
5
f
342
Fertigstellungszeit
init:
backtrack(u, v ):
finishingTime=1 : 1..n
finishTime[v ]:= finishingTime++
7
tree
backward s
cross
forward
5
b
4
e
2
g
d
c
6
3
1
f
343
Kantenklassifizierung bei DFS
type
(v , w )
tree
forward
backward
cross
dfsNum[v ] <
dfsNum[w ]
yes
yes
no
no
finishTime[w ] <
finishTime[v ]
yes
yes
no
yes
w is
marked
no
yes
yes
yes
forward
s
tree
backward
cross
344
Topologische Sortierung
Definition 5
Eine lineare Anordnung t der Knoten eines DAGs G = (V , E ),
in der alle Kanten von “kleineren” zu “größeren” Knoten verlaufen,
heißt topologische Sortierung, d. h.
8(u, v ) 2 E : t(u) < t(v ).
Beispiel:
topologisch sortierter Kleidergraph, Quelle: Wikipedia
Kleidergraph, Quelle: Wikipedia
345
Topologisches Sortieren mittels DFS
Theorem 6
G ist kreisfrei (DAG) , DFS findet keine Rückwärtskante.
In diesem Fall liefert
t(v ):= n
finishTime[v ]
eine topologische Sortierung.
346
Topologisches Sortieren mittels DFS
Theorem 6
G ist kreisfrei (DAG) , DFS findet keine Rückwärtskante.
In diesem Fall liefert
t(v ):= n
finishTime[v ]
eine topologische Sortierung.
Beweis “)”: Annahme: 9 Rückwärtskante.
Zusammen mit Baumkanten ergibt sich ein Kreis.
Widerspruch.
forward
s
tree
backward
cross
346
Topologisches Sortieren mittels DFS
Satz: G kreisfrei (DAG) , DFS findet keine Rückwärtskante.
In diesem Fall liefert t(v ):= n finishTime[v ] eine topologische
Sortierung, d. h. 8(u, v ) 2 E : t(u) < t(v ).
Beweis “(”:
Keine Rückwärtskante
Kantenklassifizierung
z}|{
)
8(v , w ) 2 E : finishTime[v ] > finishTime[w ]
) G ist kreisfrei und
finishTime definiert umgekehrte topologische Sortierung.
347
Starke Zusammenhangskomponenten
⇤
Betrachte die Relation $ mit
⇤
u $ v falls 9 Pfad hu, . . . , v i und 9 Pfad hv , . . . , ui.
⇤
Beobachtung: $ ist Äquivalenzrelation
⇤
Die Äquivalenzklassen von $ bezeichnet man als starke
Zusammenhangskomponenten.
Übung
DFS-basierter Linearzeitalgorithmus ! Algorithmen II
348
Mehr DFS-basierte Linearzeitalgorithmen
I
2-zusammenhängende Komponenten: bei Entfernen eines Knotens
aus einer Komponente bleibt diese zusammenhängend
(ungerichtet)
I
3-zusammenhängende Komponenten
I
Planaritätstest (lässt sich der Graph kreuzungsfrei zeichnen?)
I
Einbettung planarer Graphen
349
BFS
! DFS
pro BFS:
I
nichtrekursiv
I
keine Vorwärtskanten
I
kürzeste Wege, „Umgebung“
pro DFS:
I
keine explizite Datenstruktur
(Rekursionsstapel) für ToDos,
daher mglw. einfacher
I
Grundlage vieler Algorithmen
forward
s
tree
backward
cross
350
Kap. 10: Kürzeste Wege
Eingabe:
I
Graph G = (V , E ) mit
I
I
Kostenfunktion/Kantengewicht
c : E ! R sowie
Startknoten s.
3.0 km
Ausgabe: für alle v 2 V :
I
Länge µ(v ) des kürzesten Pfades von s nach v ,
I
µ(v ) := min {c(p) : p ist Pfad von s nach v }
mit c(he1 , . . . , ek i) := Âki=1 c(ei ).
351
Kap. 10: Kürzeste Wege
Eingabe:
I
Graph G = (V , E ) mit
I
I
Kostenfunktion/Kantengewicht
c : E ! R sowie
Startknoten s.
3.0 km
Ausgabe: für alle v 2 V :
I
Länge µ(v ) des kürzesten Pfades von s nach v ,
I
µ(v ) := min {c(p) : p ist Pfad von s nach v }
mit c(he1 , . . . , ek i) := Âki=1 c(ei ).
Oft wollen wir auch „geeignete“ Repräsentation der kürzesten Pfade.
351
Anwendungen
I
Routenplanung
I
I
I
I
Straßennetze
Spiele
Kommunikationsnetze
Unterprogramm
I
I
Flüsse in Netzwerken
...
I
Tippfehlerkorrektur
I
Disk Scheduling
I
...
3.0 km
352
Grundlagen
Gibt es immer einen kürzesten Pfad?
Es kann negative Kreise geben!
q v s p
q v ...
s p
(2)
u C
uC
weitere Grundlagen just in time
353
Azyklische Graphen
später
354
Kantengewichte
0
Alle Gewichte gleich: Breitensuche (BFS)!
b
s
0
e
g
c
d
f
1
2
tree
backward
cross
forward
3
355
Dijkstras Algorithmus
Nun: Beliebige nichtnegative Kantengewichte
M
0
Distance to M
R
5
Lösung ohne Rechner:
I
I
I
I
Kanten ! Fäden
L
O
Kantengewicht !
Fadenlänge
Knoten ! Knoten
Dann: Am Startknoten
anheben.
11
13
15
Q
H
G
N
F
K P
E
C
17
17
18
19
20
S
V
J
W
356
Korrektheit der Bindfäden
M
0
Distance to M
R
Betrachte beliebigen Knoten v
mit Hängetiefe d[v ].
L
9 Pfad mit Hängetiefe:
verfolge straffe Fäden
¬9 kürzerer Pfad:
falls es einen solchen Pfad
gäbe, wäre einer seiner Fäden
zerrissen!
5
11
13
15
O
Q
H
G
N
F
K P
E
C
17
17
18
19
20
S
V
J
W
357
Edsger Wybe Dijkstra
1930–2002
I
1972 ACM Turing Award
I
THE: das erste Mulitasking-OS
I
Semaphor
I
Selbst-stabilisierende Systeme
I
GOTO Statement Considered Harmful
Bildquelle: Wikipedia
358
Allgemeine Definitionen
Wie bei BFS benutzen wir zwei
Knotenarrays:
Initialisierung:
d[s] = 0, parent[s] = s
d[v ] = •, parent[v ] = ?
parent parent
parent[v ] = Vorgänger von v
auf dem (vorläufigen) kürzesten
Pfad von s nach v
Invariante:
dieser Pfad bezeugt d[v ]
d[v]
parent
I
Kante
d[v ] = aktuelle (vorläufige)
Distanz von s nach v
Invariante: d[v ] µ(v )
Kante Kante
I
s
v
359
Kante (u, v ) relaxieren
Falls d[u] + c(u, v ) < d[v ]
(vielleicht d[v ] = •),
setze
I
d[v ] := d[u] + c(u, v )
und
I
parent[v ] := u
Invarianten bleiben erhalten!
Beobachtung:
d[v ] kann sich mehrmals
ändern!
360
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
Herunterladen