vnext

Werbung
FB Informatik
Prof. Dr. R.Nitsch
Programmieren 2
Future Car Projekt
Praktikum 6
- Speichern von Graphen
- Suchen in Graphen
- Kürzeste Wege
Reiner Nitsch
 [email protected]
Darstellung von Graphen als Array von Listen
• Grundlagen zu Graphen (siehe
Vorlesung Mathematik 1)
Array von
Adjazenzlisten
1
4
2
5
3
6
Ungerichteter Graph G(V,E)
V: Knotenmenge
E: Kantenmenge
1
2
4 
2
5
4
3
5
6 
4
1
2
5 
5
4
3
2 
6
3
6
1
2
2
5 
3
5
6
4
2 
Gerichteter Graph
5
4 
6
6 
1
4
2
5
09.10.2008
3
FB Informatik
Prof. Dr. R.Nitsch
4 
6 
Projekt FutureCar
Adjazenzlisten
1 
Die mit Knoten 5 verbunden
Nachbarknoten
(= Kantenmenge E5 )
Bietet Antwort auf Fragen zu Graphen
G wie z.B.
• Speicherbedarf?  proportional zur
Anzahl Knoten plus Anzahl Kanten 
O(|V|+|E|)
• Wieviele Kanten enden an vi?
• Welche Nachbarn vj hat vi?
• Existiert Kante E=(vi,vj)?  O(|Ei|)
Gewichteter Graph: Gewicht als zusätzliche Info der Listenelemente
2
Darstellung von Graphen mit Adjazenzmatrix
Adjazenzmatrix a mit Elementen aij
aij = 1 wenn Kante E=(vi,vj) in G
enthalten, sonst aij=0
1
2
3
4
5
6
Ungerichteter Graph G(V,E)
1
2
3
4
5
6
Gerichteter Graph G(V,E)
09.10.2008
1
2
3
i 4
5
6
1
0
1
0
1
0
0
2
1
0
0
1
1
0
j

3 4
0 1
0 1
0 0
0 0
1 1
1 0
5
0
1
1
1
0
0
6
0
0
1
0
0
1
 aij = aji (symmetrisch)
0
0
0
0
0
0
1
0
0
1
0
0
0
0
0
0
0
0
1
0
0
0
1
0
0
1
1
0
0
0
0
0
1
0
0
1
FB Informatik
Prof. Dr. R.Nitsch
Bietet Antwort auf Fragen zu
Graphen G wie z.B.
• Welche Kanten enden an vi?
• Welche Nachbarn vj hat vi?
• Existiert Kante E=(vi,vj)?
 O(1)
Speicherbedarf? O(|V|2)
 ungünstig wenn G wenige
Kanten hat
Gewichteter Graph: Gewicht an
Stelle von '1' in Matrix
eintragen
 aij ≠ aji (unsymmetrisch)
Projekt FutureCar
3
7,7
Adjazenzliste des Graphen der FutureCar World
1
#### # ## # #
#
#
# ## ##
# ## ##
# ## ##
#
#
# ## ##
# ## ##
Rasterkarte (Grid) von FC-City
3,4 4,4 5,4 6,4 7,4 8,4
3,5 4,5 5,5 6,5 7,5 8,5
2
4
Rasterkarte von FC-City
mit XY-Koordinaten
3,6 4,6 5,6 6,6 7,6 8,6
FB Informatik
Prof. Dr. R.Nitsch
4,7
5,8
6,5
7,7 
5,5
4,6
7,7
5,8 
7,6
6,5
5,8
4,6 
6,8
7,7
4,6
6,5 
3,7
4,7 
4,6
3,6 
3,7 4,7 5,7 6,7 7,7 8,7
Adjazenzlisten
3,8 4,8 5,8 6,8 7,8 8,8
3,9 4,´9 5,9 6,9 7,9 8,9
5
Graph zur Modellierung der
Erreichbarkeitsbeziehungen
zwischen den Zellen
der Rasterkarte
3
09.10.2008
Projekt FutureCar
47
55
76
68
37
46
58
46
65
77
47
36
65
77
58
46
77
58
46
65
Adjazenzlisteninfo
als Textsequenz 4
Implementierungsempfehlungen
1,1
FB Informatik
Prof. Dr. R.Nitsch
Node
1,2 
+ loc
+ adjList
2,1
1,1 
3,1
2,1
3,2 
1,2
2,2
1,3 
2,2
3,2
2,1 
3,2
3,1
4,2 
3,1
2,1 3,2
+ Konstruktor:
1..N
1
Graph
- nodes:Node[ ]
+ Graph(filename:string)
+…
Adjazenzlisten
Empfehlungen:
map<Location, Node> nodes
• Container zum Verwalten der Knoten?
• Knoten in Container einfügen?
nodes[ loc ] = Node(…)
• Was sollte in den Adjazenzlisten gespeichert werden? Location oder Adressen der Nachbarknoten.
• Welcher Datentyp eignet sich für Route?
Verwendbarkeit im Navi sicherstellen (Siehe Praktikum 5)
09.10.2008
Projekt FutureCar
5
Traversieren von Graphen
FB Informatik
Prof. Dr. R.Nitsch
• Als Traversieren bezeichnet man das systematische Besuchen aller Knoten und das
Durchlaufen jeder Kante eines Graphen.
• Algorithmen zum Traversieren eines Graphen dienen als Basis für viele andere
grundlegende Algorithmen zur Verarbeitung von Graphen
• Man unterscheidet zwischen
– Breitentraversierung (breadth-first search, BFS): Die Knoten werden geordnet
nach der "Entfernung" von einem Startknoten durchlaufen
• zuerst alle Knoten mit 1 Kantenlänge Abstand vom Startknoten
• danach alle diejenigen Knoten mit Abstand 2,
• danach die mit Abstand 3, usw.
– Tiefentraversierung (depth-first search, DFS): Dieser Algorithmus erhöht
immer zuerst die Distanz vom Startknoten, bevor er in die Breite geht und
Nachbarknoten mit gleicher Distanz besucht (meist rekursiv implementiert)
• Bereits besuchte Knoten müssen markiert werden,
Node
weil sich die Algorithmen sonst in den Kreisen des
+ loc:Location
Graphen verlieren.
Markierung
+ adjList
+ visited:bool
+ Konstruktor:
09.10.2008
Projekt FutureCar
6
Tiefentraversierung (Rekursiv)
FB Informatik
Prof. Dr. R.Nitsch
• Rekursiver Algorithmus (in Pseudocode), der ausgehend von einer unmarkierten Ecke vi, alle
anderen Knoten vj, j!=i eines Graphen G (genauer: einer Komponente desselben) besucht
Funktion: traverse-dfs(v)
Zweck: Tiefensuche in einem Graphen
Parameter v: Ecke bei dem die Suche beginnt
PRE: --POST: Alle Ecken, die von v erreichbar sind,
sind gefunden.
Node
+ loc:Location
+ adjList
+ visited:bool
+ Konstruktor:
procedure traverse-dfs(v)
visited(v) := true
Markiere v als besucht
vnext := adjList[v]
Bestimme einen Nachbarknoten von v und nenne
diesen vnext
WHILE(exist(vnext)
WHILE(vnext existiert
AND NOT visited(vnext))
UND noch nicht besucht ist)
traverse-dfs(vnext)
beginne weitere Tiefensuche bei vnext
Wieder zurück,
vnext := succ(v_next)
bestimme weiteren Nachbarknoten von v
und nenne diesen wieder vnext
END WHILE
END WHILE
09.10.2008
Projekt FutureCar
7
Beispiel zur Tiefentraversierung
FB Informatik
Prof. Dr. R.Nitsch
procedure traverse-dfs(v)
visited(v) := true
vnext := adjList[v]
WHILE( exist(vnext)
UND NOT visited(vnext)
traverse-dfs(vnext)
vnext := succ(v_next)
END WHILE
Adjazenzlisten
von Seite 2
Startknoten
2
3
1
2
3
1
2
3
4
5
6
4
5
6
4
5
6
1
2
3
4
09.10.2008
2
5
2
4 
2
5
4
3
5
6 
4
1
2
5 
5
4
3
2 
6
3
6
1 
Komplexität: O(|V|+|E|)
1
1
1
3
6
4
5
6
Projekt FutureCar
Alle Knoten und
Kanten besucht!
8
Tiefentraversierung (Iterativ)
FB Informatik
Prof. Dr. R.Nitsch
• Iterativer Algorithmus mit einem Stack, der ausgehend von einer unmarkierten Ecke
vi, alle anderen Knoten vj, j!=i eines Graphen G (genauer: einer Komponente
desselben) besucht
PRE: ---
POST: Alle Ecken, die von v erreichbar sind, sind markiert.
procedure traverse-dfs(v)
t := empty-stack
visited(v) := true
push(t,v)
WHILE NOT empty(t) DO {
v := top(t)
vnext := adjList[v]
// t ist ein lokaler Stack
// markiere v als besucht
// lege v auf den Stack
// hole oberstes Element aus Stack
// hole ersten Nachbarknoten
WHILE exist(vnext) AND visited(vnext) DO // schon besucht?
vnext := succ(vnext)
// Ja! Dann eben den Nächsten
END WHILE
IF exist(vnext) THEN
// Noch einen Unbesuchten gefunden?
visit(vnext)
// diesen besuchen (und bearbeiten),
visited(vnext) := true
// als "besucht" markieren und
push(t,vnext)
// Erst mal auf den Stack damit ...
ELSE DO
pop(t)
END IF
END WHILE
// Erledigt! Alle Nachbarn von v besucht
}09.10.2008
Projekt FutureCar
… und hier
schon wieder
runter!
9
Breitentraversierung
FB Informatik
Prof. Dr. R.Nitsch
• Iterativer Algorithmus, der alle Knoten eines zusammenhängenden Graphen geordnet nach der
Entfernung vom Startknoten v durchläuft.
– Zuerst werden alle vom Startknoten über 1 Kante erreichbaren Knoten besucht
– Danach alle über mindestens 2 Kanten erreichbaren Knoten, usw.
– Entsteht formal aus Tiefentraversierung, wenn man den Stack durch eine Queue ersetzt.
PRE:
--Post:
Alle Knoten, die von v erreichbar sind, sind markiert, also besucht worden
procedure bfs_node(v)
t := empty-queue
// Definition einer leeren lokalen Queue
visited(v) := true
// Starknoten v als "besucht" markieren
enqueue(t,v)
Die Änderungen gegenüber
WHILE NOT empty(t) DO
der Tiefensuche sind ROT
v := front(t)
// vordersten Knoten in t lesen
markiert!
vnext := adjList[v]
// hole ersten Nachbarknoten
WHILE exist(vnext) AND visited(vnext) DO
// schon besucht?
vnext := succ(vnext)
// Ja! Dann eben den Nächsten
END WHILE
IF exist(vnext) THEN
// Noch einen Unbesuchten gefunden?
visit(vnext)
// diesen besuchen (und bearbeiten),
visited(vnext) := true
// als "besucht" markieren und
enqueue(t,v_next)
// erst mal in queue einreihen, wo sie bis zur
// Bearbeitung ihrer Nachbarknoten warten
ELSE DO
dequeue(t)
// Erledigt! Alle Nachbarn von v wurden besucht
END IF
10
Projekt FutureCar
END09.10.2008
WHLE
Beispiel zur Breitentraversierung
FB Informatik
Prof. Dr. R.Nitsch
procedure bfs_node(v)
t := empty-queue
visited(v) := true
enqueue(t,v)
WHILE NOT empty(t) DO
v := front(t)
vnext := adj[v]
WHILE exist(vnext) AND visited(vnext) DO
vnext := succ(vnext) END WHILE
IF exist(vnext) THEN
visit(vnext)
visited(vnext) := true
enqueue(t,vnext)
2
t:
ELSE
dequeue(t) END IF END WHILE
Startknoten
Adjazenzlisten
von Seite 2
5
4
1
3
3
1
2
3
1
2
3
4
5
6
4
5
6
4
5
6
1
2
3
1
2
3
09.10.2008
6
4
5
4 
2
5
4
3
5
6 
4
1
2
5 
5
4
3
2 
6
3
6
1 
6
2
5
2
Queue
1
4
1
6
Projekt FutureCar
Jetzt sind alle
Knoten mit Distanz
"1Kante" zum
Startknoten besucht
Alle Knoten und
Kanten besucht!
11
Kürzeste Wege mittels Breitensuche
FB Informatik
Prof. Dr. R.Nitsch
• Gesucht ist eine Verbindung (Pfad) zwischen 2 Knoten:
– Tiefensuche liefert eine entsprechende Kantenfolge, wenn es eine gibt (aber nicht
unbedingt die Kürzeste).
– Breitensuche liefert garantiert die Kürzeste.
• Aufgabe
Mit Hilfe eines Breitensuchverfahrens soll der kürzeste Weg in einem ungewichteten
Graphen G vom Startpunkt src zum Zielknoten dest gefunden werden, der über die
geringste Anzahl von Kanten verläuft. Dabei wird der Weg so codiert, dass man ihn
hinterher rekonstruieren kann.
• Lösung
– Die Breitentraversierung durchläuft alle Knoten geordnet nach der Kantendistanz
zu src.
– Der Vorgängerknoten, von dem ausgehend der Knoten v betreten wird, verbindet
somit v auf dem kürzesten Wege mit src (keine Kantengewichte!).
– Im Bearbeitungsschritt merkt sich Knoten v daher seinen Vorgängerknoten
– Nachdem Knoten dest betreten wurde und dieser sich seinen Vorgängerknoten
gemerkt hat, ist die Suche beendet.
– Der kürzeste Weg, der dest mit src verbindet, ergibt sich nun, indem man,
beginnend bei dest, die Folge der Vorgängerknoten rekonstruiert.
– Rückwärts gelesen (std::reverse) ergibt diese Folge den gesuchten kürzesten
Weg.
12
09.10.2008
Projekt FutureCar
Breitensuche des Knotens d ausgehend vom Startknoten s
PRE:
POST:
FB Informatik
Prof. Dr. R.Nitsch
exist(s), exist(d)
Alle Knoten, die von v erreichbar sind, sind markiert, also besucht worden
FUNCTION bf_search(s,d)
t := empty-queue
visited(s) := true
pred(s) := nil
enqueue(t,s)
Ergänzungen zum vorherigen Algorithmus sind ROT markiert
// Definition einer leeren lokalen Queue
// Starknoten v als "besucht" markieren
// s kennt seinen Vorgänger noch nicht
Node
+ loc:Location
+ adjList
Verweis auf Vorgängerknoten
+ pred
WHILE NOT empty(t) AND front(t)!=d DO // Abbruch der Suche wenn d besucht
+ visited:bool
v := front(t)
// vordersten Knoten in t lesen
+ Konstruktor:
vnext := adj[v]
// hole ersten Nachbarknoten
WHILE exist(vnext) AND visited(vnext) DO
v_next := succ(v_next)
// Bereits besuchte Knoten überspringen
END WHILE
IF v_next != nil THEN
// Solange unbesuchte Nachbarknoten zu v existieren
visit(v_next)
// diese besuchen (und bearbeiten),
pred(v_next) := v
// Vorgänger merken (besuchen & bearbeiten)
visited(v_next) := true
// als solche markieren und
enqueue(t,v_next)
// in queue einfügen, wo sie bis zur Bearbeitung
// ihrer Nachbarknoten warten
ELSE DO
dequeue(t)
// entferne vorderstes Element aus t
END IF
// Alle Nachbarn dieses Knotens sind besucht
IF empty(t) THEN
{ kein Pfad von s nach d }
09.10.2008
Projekt FutureCar
13
Herunterladen