Graphen in Haskell Graphen in Haskell Gliederung Algorithmen und Datenstrukturen II 1 Graphen in Haskell Graphen in Haskell ADT Graph Implementierung Adjazenzlisten Adjazenzmatrix D. Rösner Suche Institut für Wissens- und Sprachverarbeitung Fakultät für Informatik Otto-von-Guericke Universität Magdeburg Tiefensuche Breitensuche Topologisches Sortieren c Sommer 2009, 17. April 2009, 2009 D.Rösner D. Rösner AuD II 2009 . . . Graphen in Haskell 1 ADT Graph Implementierung Suche Topologisches Sortieren D. Rösner AuD II 2009 . . . Graphen in Haskell Graphen in Haskell 2 ADT Graph Implementierung Suche Topologisches Sortieren Graphen in Haskell im ADT Graph sollen Funktionen für die folgenden Aufgaben realisiert werden: im folgenden wird ein abstrakter Datentyp (ADT) für gewichtete Graphen definiert 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) 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 s. [RL99], Ch. 7.2 D. Rösner AuD II 2009 . . . 4 D. Rösner AuD II 2009 . . . 5 ADT Graph Implementierung Suche Topologisches Sortieren Graphen in Haskell Graphen in Haskell Graphen in Haskell ADT Graph Implementierung Suche Topologisches Sortieren Graphen in Haskell Moduldefinition: Bemerkungen: module Graph(Graph,mkGraph,adjacent,nodes,edgesU,edgesD,edgeIn, weight) where import Array ... 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 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 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 s. [RL99], Ch. 7.2 D. Rösner AuD II 2009 . . . 6 ADT Graph Implementierung Suche Topologisches Sortieren Graphen in Haskell D. Rösner AuD II 2009 . . . Graphen in Haskell Graphen 7 ADT Graph Implementierung Suche Topologisches Sortieren Graphen in Haskell 12 1 2 78 34 der gewichtete Graph aus [RL99], Fig. 7.1 (c) (s.o.) lässt sich also wie folgt kreieren: 32 5 44 55 93 3 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)] 4 61 s. [RL99], Ch. 7.2 Abbildung: Beispiele für Graphen: gewichteter ungerichteter Graph (vgl. [RL99], Fig. 7.1 (c)) D. Rösner AuD II 2009 . . . 8 D. Rösner AuD II 2009 . . . 9 Graphen in Haskell ADT Graph Implementierung Suche Topologisches Sortieren Graphen in Haskell Graphen in Haskell Graphen in Haskell Implementierung durch Adjazenzlisten falls Liste verwendet wird, um die Knoteninfo zu verwalten: type Graph n w = Implementierung durch Adjazenzlisten der Graph aus [RL99], Fig. 7.1 (c) (s.o.) lässt sich mit Array-Funktionen direkt wie folgt kreieren: [(n, [(n,w)])] 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)])] 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 = ADT Graph Implementierung Suche Topologisches Sortieren weniger aufwändig ist die (ohnehin empfohlene) Nutzung von mkGraph aus dem ADT (s.o.) Array n [(n,w)] s. [RL99], Ch. 7.2.2 s. [RL99], Ch. 7.2.2 D. Rösner AuD II 2009 . . . Graphen in Haskell 11 D. Rösner AuD II 2009 . . . ADT Graph Implementierung Suche Topologisches Sortieren Graphen in Haskell Graphen in Haskell 12 ADT Graph Implementierung Suche Topologisches Sortieren Graphen in Haskell Implementierung der Funktionen des ADT Graph durch Adjazenzlisten 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]) 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] s. [RL99], Ch. 7.2.2 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 . . . 13 D. Rösner AuD II 2009 . . . 14 Graphen in Haskell ADT Graph Implementierung Suche Topologisches Sortieren Graphen in Haskell Graphen in Haskell ADT Graph Implementierung Suche Topologisches Sortieren Graphen in Haskell Implementierung der Funktionen des ADT Graph mit einer Adjazenzmatrix Erinnerung: Implementierung der Funktionen des ADT Graph mit einer Adjazenzmatrix möglicher Typ: 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 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 möglicher Typ: type Graph n w = Array (n,n) w s. [RL99], Ch. 7.2.3 s. [RL99], Ch. 7.2.3 D. Rösner AuD II 2009 . . . Graphen in Haskell 15 D. Rösner AuD II 2009 . . . ADT Graph Implementierung Suche Topologisches Sortieren Graphen in Haskell Graphen in Haskell: Adjazenzmatrix 16 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 Einschub: der Typ Maybe a besteht aus den möglichen Werten Nothing und Just a der Code: in unserem Fall repräsentiert Nothing dann, dass keine Kante vorhandene Gewichte sind in der Form Just a in der Matrix eingetragen vorliegt 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] damit modifizierter Typ: type Graph n w = Array (n,n) (Maybe w) s. [RL99], Ch. 7.2.3 s. [RL99], Ch. 7.2.3 D. Rösner AuD II 2009 . . . 17 D. Rösner AuD II 2009 . . . 18 Graphen in Haskell ADT Graph Implementierung Suche Topologisches Sortieren Graphen in Haskell Graphen in Haskell: Adjazenzmatrix Graphen in Haskell: Adjazenzmatrix Implementierung weiterer Funktionen des ADT Graph 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 adjacent g v1 = [ v2 | v2 <-nodes g,(g!(v1,v2))/= Nothing] nodes g ADT Graph Implementierung Suche Topologisches Sortieren = range (l,u) where ((l,_),(u,_)) = bounds g 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 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 s. [RL99], Ch. 7.2.3 D. Rösner AuD II 2009 . . . Graphen in Haskell 19 ADT Graph Implementierung Suche Topologisches Sortieren D. Rösner AuD II 2009 . . . Graphen in Haskell Graphen in Haskell: Suche 20 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: 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]) 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 s. [RL99], Ch. 7.3 D. Rösner AuD II 2009 . . . 22 D. Rösner AuD II 2009 . . . 23 Graphen in Haskell ADT Graph Implementierung Suche Topologisches Sortieren Graphen in Haskell Graphen in Haskell: Suche 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: 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) 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 s. [RL99], Ch. 7.3 D. Rösner AuD II 2009 . . . Graphen in Haskell 24 ADT Graph Implementierung Suche Topologisches Sortieren D. Rösner AuD II 2009 . . . Graphen in Haskell Graphen in Haskell: Topologisches Sortieren 25 ADT Graph Implementierung Suche Topologisches Sortieren Graphen in Haskell: Topologisches Sortieren Vorgehen: das komplette Programm 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: 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)) 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 s. [RL99], Ch. 7.4 D. Rösner AuD II 2009 . . . 27 D. Rösner AuD II 2009 . . . 28 Graphen in Haskell ADT Graph Implementierung Suche Topologisches Sortieren Graphen in Haskell Graphen in Haskell: Topologisches Sortieren ADT Graph Implementierung Suche Topologisches Sortieren Graphen in Haskell: Topologisches Sortieren Anwendung auf einen Graph mit Information zu Lehrveranstaltungen Anwendung auf einen Graph mit Information zu Lehrveranstaltungen data Courses = Maths | Theory | Languages | Programming | Concurrency| Architecture | Parallelism deriving (Eq,Ord,Enum,Ix,Show) ? topologicalSort cg [Architecture, Programming, Concurrency, Parallelism, Languages, Maths, Theory] 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 s. [RL99], Ch. 7.4 D. Rösner AuD II 2009 . . . Graphen in Haskell 29 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 D. Rösner AuD II 2009 . . . 30