Algorithmen und Datenstrukturen II

Werbung
Graphen in Haskell
Algorithmen und Datenstrukturen II
Graphen in Haskell
D. Rösner
Institut für Wissens- und Sprachverarbeitung
Fakultät für Informatik
Otto-von-Guericke Universität Magdeburg
c
Sommer 2009, 17. April 2009, 2009
D.Rösner
D. Rösner AuD II 2009 . . .
1
Graphen in Haskell
Gliederung
1
Graphen in Haskell
ADT Graph
Implementierung
Adjazenzlisten
Adjazenzmatrix
Suche
Tiefensuche
Breitensuche
Topologisches Sortieren
D. Rösner AuD II 2009 . . .
2
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell
im folgenden wird ein abstrakter Datentyp (ADT) für
gewichtete Graphen definiert
Anpassung an ungewichtete Graphen durch Ignorieren der
Gewichte
Anforderungen an den Typ der Elemente eines Graphen:
Typ der Knoten n: damit Knoten als Indizes von Arrays
genutzt werden können, sollten sie zur Klasse Ix gehören
Typ der Gewichte w: Gewichte sind Zahlen ( class Num)
m.a.W. Typdefinition für Graph als type Graph n w
s. [RL99], Ch. 7.2
D. Rösner AuD II 2009 . . .
4
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell
im ADT Graph sollen Funktionen für die folgenden
Aufgaben realisiert werden:
eine Funktion zum Erstellen eines Graphen aus der Liste
seiner (gewichteten) Kanten (mkGraph)
zu einem beliebigen Knoten wird die Liste adjazenter
Knoten geliefert (adjacent)
alle Knoten des Graphen werden geliefert (nodes)
alle Kanten des Graphen werden geliefert (edgesD, falls
gerichtet, edgesU, falls ungerichtet)
Test, ob eine bestimmte Kante im Graph existiert (edgeIn)
das Gewicht der Kante zwischen zwei Knoten wird geliefert
(weight)
s. [RL99], Ch. 7.2
D. Rösner AuD II 2009 . . .
5
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell
Moduldefinition:
module Graph(Graph,mkGraph,adjacent,nodes,edgesU,edgesD,edgeIn,
weight) where
import Array
...
die Signaturen des ADT Graph ergeben sich wie folgt:
mkGraph :: (Ix n,Num w)
=> Bool -> (n,n) -> [(n,n,w)] -> (Graph n w)
adjacent :: (Ix n,Num w) => (Graph n w) -> n -> [n]
nodes :: (Ix n,Num w) => (Graph n w) -> [n]
edgesU :: (Ix n,Num w) => (Graph n w) -> [(n,n,w)]
edgesD :: (Ix n,Num w) => (Graph n w) -> [(n,n,w)]
edgeIn :: (Ix n,Num w) => (Graph n w) -> (n,n) -> Bool
weight :: (Ix n,Num w) => n -> n -> (Graph n w) -> w
s. [RL99], Ch. 7.2
D. Rösner AuD II 2009 . . .
6
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell
Bemerkungen:
mkGraph :: (Ix n,Num w)
=> Bool -> (n,n) -> [(n,n,w)] -> (Graph n w)
das erste Argument gibt an, ob der Graph gerichtet ist; ist
er ungerichtet, dann werden angegebene Kanten in beiden
Richtungen verwendet, sonst nur in der angegebenen
Form
das zweite Argument gibt die Grenzen des Bereichs der
Knoten an
das dritte Argument enthält die Kanteninformation als
Tripel aus den beiden Knoten und dem Gewicht
s. [RL99], Ch. 7.2
D. Rösner AuD II 2009 . . .
7
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell
Graphen
12
1
2
78
34
32
5
44
55
93
3
4
61
Abbildung: Beispiele für Graphen: gewichteter ungerichteter Graph
(vgl. [RL99], Fig. 7.1 (c))
D. Rösner AuD II 2009 . . .
8
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell
der gewichtete Graph aus [RL99], Fig. 7.1 (c) (s.o.) lässt
sich also wie folgt kreieren:
mkGraph False (1,5) [(1,2,12),(1,3,34),(1,5,78),
(2,4,55),(2,5,32),
(3,4,61),(3,5,44),
(4,5,93)]
s. [RL99], Ch. 7.2
D. Rösner AuD II 2009 . . .
9
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell
Implementierung durch Adjazenzlisten
falls Liste verwendet wird, um die Knoteninfo zu verwalten:
type Graph n w =
[(n, [(n,w)])]
der Aufwand für den Zugriff auf die Adjazenzliste eines
Knotens n ist dann O(|V|)
für konstanten Aufwand für den Zugriff auf die
Adjazenzliste eines Knotens empfiehlt sich als Alternative
ein Array, um die Knoteninfo zu verwalten:
type Graph n w =
Array n [(n,w)]
s. [RL99], Ch. 7.2.2
D. Rösner AuD II 2009 . . .
11
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell
Implementierung durch Adjazenzlisten
der Graph aus [RL99], Fig. 7.1 (c) (s.o.) lässt sich mit
Array-Funktionen direkt wie folgt kreieren:
graphAL = array (1,5) [(1,[(2,12),(3,34),(5,78)]),
(2,[(1,12),(4,55),(5,32)]),
(3,[(1,34),(4,61),(5,44)]),
(4,[(2,55),(3,61),(5,93)]),
(5,[(1,78),(2,32),(3,44),(4,93)])]
weniger aufwändig ist die (ohnehin empfohlene) Nutzung
von mkGraph aus dem ADT (s.o.)
s. [RL99], Ch. 7.2.2
D. Rösner AuD II 2009 . . .
12
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell
Implementierung der Funktionen des ADT Graph durch
Adjazenzlisten
mkGraph:
mkGraph dir bnds es =
accumArray (\xs x -> x:xs) [] bnds
([(x1,(x2,w)) | (x1,x2,w) <- es] ++
if dir then []
else [(x2,(x1,w))|(x1,x2,w)<-es,x1/=x2])
s. [RL99], Ch. 7.2.2
D. Rösner AuD II 2009 . . .
13
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell
Implementierung der Funktionen des ADT Graph durch
Adjazenzlisten
adjacent g v
= map fst (g!v)
nodes g
= indices g
edgeIn g (x,y) = elem y (adjacent g x)
weight x y g
= head [ c | (a,c)<-g!x , a==y]
edgesD g = [(v1,v2,w)| v1<- nodes g, (v2,w) <-g!v1]
edgesU g = [(v1,v2,w)| v1<- nodes g, (v2,w) <-g!v1, v1 < v2]
s. [RL99], Ch. 7.2.2
D. Rösner AuD II 2009 . . .
14
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell
Implementierung der Funktionen des ADT Graph mit einer
Adjazenzmatrix
Erinnerung:
ob zwischen zwei Knoten eines Graphen eine Kante
vorliegt, wird durch einen Eintrag in einer
zweidimensionalen quadratischen Matrix repräsentiert
die Koordinaten repräsentieren die Knoten und es gilt, dass
im Matrixelement (i, j) genau dann ein Eintrag vorliegt,
wenn zwischen den Knoten vi und vj eine Kante existiert
möglicher Typ:
type Graph n w = Array (n,n) w
s. [RL99], Ch. 7.2.3
D. Rösner AuD II 2009 . . .
15
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell
Implementierung der Funktionen des ADT Graph mit einer
Adjazenzmatrix
möglicher Typ:
type Graph n w = Array (n,n) w
zu klären ist, mit welchem Eintrag ausgedrückt wird, dass
keine Kante zwischen zwei Knoten vorliegt
um die möglichen Werte für Gewichte nicht zu sehr
einzuschränken, empfiehlt sich hier die Verwendung des
algebraischen Typs Maybe
s. [RL99], Ch. 7.2.3
D. Rösner AuD II 2009 . . .
16
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell: Adjazenzmatrix
Einschub:
der Typ Maybe a besteht aus den möglichen Werten
Nothing und
Just a
in unserem Fall repräsentiert Nothing dann, dass keine
Kante
vorhandene Gewichte sind in der Form Just a in der
Matrix eingetragen vorliegt
damit modifizierter Typ:
type Graph n w = Array (n,n) (Maybe w)
s. [RL99], Ch. 7.2.3
D. Rösner AuD II 2009 . . .
17
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell: Adjazenzmatrix
damit Implementierung von mkGraph
zunächst wird die Matrix mit Nothing vorbelegt
(emptyArray)
dann werden – mit Unterscheidung zwischen gerichtetem
und ungerichtetem Fall – Kantengewichte als Werte der
Form Just w eingefügt
der Code:
mkGraph dir bnds@(l,u) es
= emptyArray // ([((x1,x2),Just w) |(x1,x2,w)<-es] ++
if dir then []
else
[((x2,x1),Just w) |(x1,x2,w)<-es,x1/=x2])
where
emptyArray
= array ((l,l),(u,u))
[((x1,x2),Nothing) | x1 <- range bnds,
x2 <- range bnds]
s. [RL99], Ch. 7.2.3
D. Rösner AuD II 2009 . . .
18
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell: Adjazenzmatrix
Implementierung weiterer Funktionen des ADT Graph
adjacent g v1 = [ v2 | v2 <-nodes g,(g!(v1,v2))/= Nothing]
nodes g
= range (l,u) where ((l,_),(u,_)) = bounds g
edgeIn g (x,y)= (g!(x,y)) /= Nothing
weight x y g
= w where (Just w) = g!(x,y)
s. [RL99], Ch. 7.2.3
D. Rösner AuD II 2009 . . .
19
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell: Adjazenzmatrix
Implementierung weiterer Funktionen des ADT Graph
edgesD g
= [(v1,v2,unwrap(g!(v1,v2)))
| v1 <-nodes g, v2 <- nodes g,
edgeIn g (v1,v2)]
where unwrap (Just w) = w
edgesU g
= [(v1,v2,unwrap(g!(v1,v2)))
| v1 <-nodes g, v2 <- range (v1,u),
edgeIn g (v1,v2)]
where (_,(u,_)) = bounds g
unwrap (Just w) = w
s. [RL99], Ch. 7.2.3
D. Rösner AuD II 2009 . . .
20
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell: Suche
die Funktion depthFirstSearch nimmt einen
Startknoten und einen Graphen und liefert die Liste der bei
einer Tiefensuche erreichten Knoten zurück
Variante 1:
depthFirstSearch start g = dfs [start] []
where
dfs [] vis
= vis
dfs (c:cs) vis
| elem c vis = dfs cs vis
| otherwise = dfs ((adjacent g c)++cs) (vis++[c])
s. [RL99], Ch. 7.3
D. Rösner AuD II 2009 . . .
22
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell: Suche
Variante: statt in jedem Schritt die Liste besuchter Knoten
zu durchlaufen, um den aktuellen Knoten anzuhängen
(also: vis++[c]), wird stattdessen einmal an Ende die
Liste besuchter Knoten umgedreht
zugehöriger Code:
depthFirstSearch’ start g = reverse (dfs [start] [])
where
dfs [] vis
= vis
dfs (c:cs) vis
| elem c vis = dfs cs vis
| otherwise = dfs ((adjacent g c)++cs) (c:vis)
s. [RL99], Ch. 7.3
D. Rösner AuD II 2009 . . .
23
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell: Suche
Variante: die Verwaltung der Kandidatenknoten erfolgt mit
einem Stack
zugehöriger Code:
depthFirstSearch’’ start g
= reverse (dfs (push start emptyStack) [])
where
dfs s vis
| (stackEmpty s) = vis
| elem (top s) vis = dfs (pop s) vis
| otherwise = let c = top s
in dfs (foldr push (pop s) (adjacent g c))
(c:vis)
s. [RL99], Ch. 7.3
D. Rösner AuD II 2009 . . .
24
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell: Suche
bei Breitensuche erfolgt die Verwaltung der
Kandidatenknoten mit einer Queue (nach dem
FIFO-Prinzip)
zugehöriger Code:
breadthFirstSearch start g
= reverse (bfs (enqueue start emptyQueue) [])
where
bfs q vis
| (queueEmpty q) = vis
| elem (front q) vis = bfs (dequeue q) vis
| otherwise = let c = front q
in bfs (foldr enqueue (dequeue q)
(adjacent g c))
(c:vis)
s. [RL99], Ch. 7.3
D. Rösner AuD II 2009 . . .
25
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell: Topologisches Sortieren
Vorgehen:
der Algorithmus zum topologischen Sortieren arbeitet – wie
Tiefensuche und Breitensuche – mit den beiden Listen der
Kandidatenknoten und der besuchten Knoten
die Aktualisierung geschieht wie folgt:
Kandidaten: (c:cs) → cs
besucht: vis → c:(tsort (adjacent g c) vis)
m.a.W.: wenn ein Knoten als besucht eingereiht wird,
geschieht dies vor seinen (rekursiv) topologisch sortierten
adjazenten Knoten
die initiale Liste von Kandidaten ergibt sich aus den Knoten
ohne vorausgehende Knoten
s. [RL99], Ch. 7.4
D. Rösner AuD II 2009 . . .
27
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell: Topologisches Sortieren
das komplette Programm
inDegree g n
= length [t | v<-nodes g, t<-adjacent g v, (n==t)]
topologicalSort g
= tsort [n | n<-nodes g , (inDegree g n == 0)] []
where
tsort [] r
= r
tsort (c:cs) vis
| elem c vis = tsort cs vis
| otherwise = tsort cs (c:(tsort (adjacent g c) vis))
s. [RL99], Ch. 7.4
D. Rösner AuD II 2009 . . .
28
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell: Topologisches Sortieren
Anwendung auf einen Graph mit Information zu
Lehrveranstaltungen
data Courses = Maths | Theory | Languages | Programming
| Concurrency| Architecture | Parallelism
deriving (Eq,Ord,Enum,Ix,Show)
cg = mkGraph True (Maths,Parallelism)
[(Maths,Theory,1), (Languages,Theory,1),
(Programming,Languages,1),
(Programming,Concurrency,1),
(Concurrency,Parallelism,1),
(Architecture,Parallelism,1)]
s. [RL99], Ch. 7.4
D. Rösner AuD II 2009 . . .
29
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Graphen in Haskell: Topologisches Sortieren
Anwendung auf einen Graph mit Information zu
Lehrveranstaltungen
? topologicalSort cg
[Architecture, Programming, Concurrency,
Parallelism, Languages, Maths, Theory]
s. [RL99], Ch. 7.4
D. Rösner AuD II 2009 . . .
30
Graphen in Haskell
ADT Graph
Implementierung
Suche
Topologisches Sortieren
Literatur: I
Fethi Rabhi and Guy Lapalme.
Algorithms – A Functional Programming Approach.
Pearson Education Ltd., Essex, 1999.
2nd edition, ISBN 0-201-59604-0.
D. Rösner AuD II 2009 . . .
31
Herunterladen