1 Wiederholung 2 Der Algorithmus von Dijkstra

Werbung
1
Wiederholung
Sei G ein einfacher (ungerichteter), zusammenhängender Graph mit den
Knoten V (Vertices) und den Kanten E (Edges).
1.1
Gewichte
Das Gewicht zwischen zwei benachbarten Knoten j und k wird durch die
Gewichtsfunktion g(j, k) : V × V → R bestimmt. Diese wird oft auch als
Distanz- oder Kostenfunktion interpretiert.
1.2
Weg
Ein Weg w von x1 nach x2 ist eine Folge von Knoten (x1 , x2 , ..., xn ), wobei
je zwei aufeinanderfolgende Knoten Nachbarn sind.
Der Weg heisst geschlossen falls x1 = xn und einfach falls jeder Eckpunkt höchstens einmal vorkommt. Ein Graph heisst zusammenhängend
wenn zwischen zwei beliebigen Knoten ein Weg existiert.
P
Die Länge eines Weges ist definiert als L(w) := 1n−1 g(xi , xi + 1). Die
Länge des kürzesten Weges von j zu k ist eine Graphenmetrik d(j, k) :=
inf {L(w) : w = (j, v1 , v2 , ..., k)}
2
Der Algorithmus von Dijkstra
2.1
Einleitung
Der Algorithmus von Dijkstra wurde 1959 von Edsger Dijkstra entdeckt und
findet den kürzesten Weg zwischen einem Startknoten und allen anderen
Knoten in einem Graphen. 1 .
Damit der Algorithmus angewendet werden darf müssen alle Gewichte
positiv und der Graph zusammenhängend sein.
2.2
Der Algorithmus
Der Algorithmus von Dijkstra unterscheidet zwischen permanenten und temporären Knoten. Anfangs sind alle Knoten bis auf den Startknoten temporär mit einer Distanz von +∞. In den einzelnen Schritten werden die
Knoten zu denen der kürzeste Weg gefunden wurde als permanente Knoten
1
Mit einer kleinen Änderung der Abbruchbedingung könnte der Algorithmus auch den
kürzesten Weg zwischen einem Startknoten und nur einem einzigen Endknoten finden.
1
mit der entsprechenden Distanz markiert. Sobald alle Knoten als permanent
markiert sind endet der Algorithmus.
Für jeden Knoten muss die aktuelle Distanz, der Vorgänger und der Zustand (temporär oder permanent) gespeichert werden. Ausserdem ist immer
genau ein Knoten als aktiv markiert.
1. Markiere den Startknoten als permanent mit Distanz 0, alle anderen
Knoten als temporär mit Distanz +∞.
2. Wähle den Startknoten als aktiven Knoten a.
3. Sei j ein Nachbar des aktiven Knotens und temporär. Falls Distanz(a)+
g(a, j) < Distanz(j) setze Distanz(j) = Distanz(a) + g(a, j) und
V orgaenger(j) = a. Dieser Schritt heisst Update.
4. Wähle einen temporären Knoten mit minimaler Distanz und markiere
ihn als permanent und aktiv.
5. Gehe zu Schritt 3 falls noch temporäre Knoten vorhanden sind. Ansonsten endet der Algorithmus.
Das Ergebnis ist eine Baumstruktur die den kürzesten Weg zu jedem
Knoten im Graphen enthält.
2
2.3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Pseudocode
f u n c t i o n D i j k s t r a ( Graph , s o u r c e ) :
// S c h r i t t 1 : I n i t i a l i s i e r e n
for each v e r t e x v i n Graph :
d i s t [ v ] := i n f i n i t y
v o r g a e n g e r [ v ] := u n d e f i n e d
d i s t [ s o u r c e ] := 0
Q := th e s e t o f a l l nodes i n Graph
// S c h r i t t 5 : Solang noch Knoten vorhanden s i n d :
while Q i s not empty :
// S c h r i t t 4 : Waehle Knoten mit min . D i s t a n z
// Beim e r s t e n D u r c h l a u f i s t das S c h r i t t 2 !
a := v e r t e x i n Q with s m a l l e s t d i s t [ ]
remove a from Q
// S c h r i t t 3 : Update
for each n e i g h b o r j o f a :
a l t := d i s t [ a ] + g ( a , j )
if alt < dist [ j ]
d i s t [ j ] := a l t
v o r g a e n g e r [ j ] := a
return v o r g a e n g e r [ ]
2.4
Laufzeitanalyse
Die Initialisierung (Zeilen 1-8) wirkt sich auf jeden Knoten aus, also O(|V |).
Wird eine Liste als Datenstruktur für vorgaenger verwendet dauert das
Finden und Entfernen des Knotens mit der kürzesten Entfernung (Zeilen
14,15) je O(|V |). Weil dieser Schritt auf jeden Knoten angewendet wird
ergibt das O(|V |2 )
Das Update (Zeilen 18-22) wird zwar |V |-mal durchgeführt, insgesamt
muss die Schleife aber höchstens |E| mal durchlaufen werden, da jede Kante
nur genau einmal verwendet wird. Die Anweisungen in der Schleife sind je
mit konstanter Zeit O(1) beschränkt. Das ergibt eine Laufzeit von O(|E|)
Insgesamt ergibt das O(|V |2 +|E|). Bei Verwendung einer besseren Datenstruktur2 für die temporären Knoten können minimale Knoten in O(log(|E|)
2
Fibonacci Heap, http://de.wikipedia.org/wiki/Fibonacci-Heap
3
statt O(|E|) gefunden und entfernt werden (Zeilen 14,15). Das verbessert
die Laufzeit auf O(|E| log |E| + |V |).
4
5
6
3
A*
3.1
Einleitung
Der A* Algorithmus (1968 von Peter Hart, Nils Nilson und Bertram Raphael)
findet den kürzesten Weg zwischen zwei Knoten in einem Graphen. Er ist
unter bestimmten Umständen eine Verallgemeinerung des Algorithmus’ von
Dijkstra 3 .
Während Dijkstra immer blind den Knoten mit der kürzesten Distanz
vom Startknoten aus behandelt, verwendet A* eine heuristische Funktion um
den verbleibenden Weg zum Ziel abzuschätzen und so den nächsten Knoten
des Pfades auf "intelligente" Art zu finden. Suchalgorithmen die eine solche
heuristische Funktion verwenden nennt man informierte Algorithmen.
Für die Anwendung des Algorithmus gelten die selben Voraussetzungen
wie für Dijkstra: zusammenhängender Graph mit positiven Gewichten.
3.2
Informeller Algorithmus
A* versucht immer den Pfad weiterzugehen, für den die bekannte Länge
gemeinsam mit der heuristischen Restlänge minimal ist.
Für jeden Knoten wird die Distanz, Gesamtdistanz (geschätzt bis zum
Ziel), der Vorgänger und der Zustand (temporär oder permanent) gespeichert. Ausserdem gibts es immer genau einen akiven Knoten a.
1. Markiere alle Knoten als temporär mit Distanz und Gesamtdistanz +∞
und setze alle Vorgänger als undefiniert. Nur der Startknoten wird als
permanent mit Distanz 0 markiert.
2. Markiere den Startknoten als aktiven Knoten.
3. Sei j ein Nachbar des aktiven Knotens und temporär. Falls Distanz(a)+
g(a, j) < Distanz(j) setze
V orgaenger(j) = a.
Distanz(j) = Distanz(a) + g(a, j)
GesamtDistanz(j) = Distanz(j) + h(j)
4. Wähle einen neuen temporären Knoten mit minimaler Gesamtdistanz
als aktiv und markiere ihn als permanent.
3
Und zwar nur dann wenn der Algorithmus von Dijkstra ebenfalls verwendet wird um
den kürzesten Weg von einem Startpunkt zu einem Endpunkt zu berechnen und gestoppt
wird sobald das Ziel als permanent markiert ist.
7
5. Falls der aktive Knoten gleich dem Zielknoten ist: beende den Algorithmus
6. Beende den Algorithmus falls der aktive Knoten a gleich dem Zielknoten ist.
3.3
Ein Beispiel
Selber ausdenken, lösen und prüfen. Danke!
3.4
Die heuristische Funktion
Von der heuritischen Funktion wird gefordert dass h(x) < d(x, ziel). Durch
diese Forderung kann der Algorithmus sicherstellen dass gewisse Wege garantiert
küzer oder länger sind als andere und diese dann zuallererst, bzw. gar nicht
behandelt. Ein Beispiel für eine zulässige heuristische Abschätzung bei einem
Routenplaner wäre die verbleibende Entfernung als Luftlinie.
Gibt es zwei heuristische Funktionen so kann h(x) := min(h1 (x), h2 (x))
als kombinierte Variante verwendet werden.
8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
f u n c t i o n AStar (G, s t a r t , g o a l )
temporary := V
permanent := empty
d i s t [ V−s t a r t ] := +i n f
d i s t [ s t a r t ] := 0
t o t a l _ d i s t [ V−s t a r t | := +i n f
t o t a l _ d i s t [ s t a r t ] := h_score [ s t a r t ]
p re [V] = u n d e f i n e d
while temporary i s not empty
a := v : v i n temporary where t o t a l _ d i s t [ v ] i s minimal
i f a == g o a l
return path
remove a from temporary
add a t o permanent
f o r e a c h y i n neighbor_nodes ( a ) and y not i n permanent :
new_dist := d i s t [ a ] + g ( a , y )
i f new_dist < d i s t [ y ] :
p re [ y ] := a
d i s t [ y ] := new_dist
t o t a l _ d i s t [ y ] := d i s t [ y ] + h ( y )
return no_path_found
3.5
Laufzeitanalyse
Die Laufzeitanalyse ergibt, genau wie bei Dijkstra, O(|V | log V ). Die tatsächliche Laufzeit hängt jedoch kaum mehr mit der berechenbaren O-Laufzeit
zusammen, sondern vielmehr mit der Qualität der heuristischen Funktion.
Eine perfekte Heruistik würde immer den richtigen Nachfolgeknoten auf
einem gesuchten Pfad erkennen 4 .
4
In diesem Fall würde es sich aber auch nicht mehr um ein Suchproblem handeln, da
der beste Pfad offensichtlich direkt berechenbar wäre.
9
3.6
Beweis: A* ist optimal
Sei w1 der optimale Pfad mit Länge L1 und w2 mit Länge L2 die gefundene
suboptimale Lösung.
Für die suboptimale Lösung gilt sicher dass L2 > L1 (sonst wäre die
Lösung nicht suboptimal).
Weil h eine zulässige Heuristik ist gilt für jeden Knoten auf dem optimalen
Pfad: d(x) + h(x) <= L1
Damit dist_total(x) = d(x) + h(x) <= L1 < L2
Wegen Schritt (4) des Algorithmus wird also jeder Punkt x der auf dem
optimalen Pfad liegt als aktiver Knoten ausgewählt werden bevor der Zielknoten über einen suboptimalen Pfad als Zielknoten ausgewählt würde.
Setz man x = zielknoten kann man sehen dass der Zielknoten nur über
den optimalen Pfad ausgewählt werden kann.
4
Quellen
E.W. Dijkstra, A note on two problems in connexion with Graphs (http://wwwm3.ma.tum.de/twiki/pub/MN0506/WebHome/dijkstra.pdf)
M. Schmuckenschlaeger, Graphentheorie (http://www.quixquax.at/bleichling/anwendungen/h
Wikipedia: Dijkstra Algorithm, A* Algorithm
10
Herunterladen