Tema 22: Algoritmos sobre grafos
Índice
1. El TAD de los grafos
1.1. Definiciones y terminología sobre grafos
- Un grafo G es un par (V,A) donde V es el conjunto de los vértices (o nodos) y A el de las aristas.
- Una arista del grafo es un par de vértices.
- Un arco es una arista dirigida.
- |V| es el número de vértices.
- |A| es el número de aristas.
- Un vértice v es adjacente a v' si vv' es una arista del grafo.
- Un grafo ponderado es un grafo cuyas aristas tienen un peso.
1.2. Signatura del TAD de los grafos
La signatura del TAD de los grafos es
creaGrafo :: (Ix v,Num p) => Orientacion -> (v,v) -> [(v,v,p)] -> Grafo v p dirigido :: (Ix v,Num p) => (Grafo v p) -> Bool adyacentes :: (Ix v,Num p) => (Grafo v p) -> v -> [v] nodos :: (Ix v,Num p) => (Grafo v p) -> [v] aristas :: (Ix v,Num p) => (Grafo v p) -> [(v,v,p)] aristaEn :: (Ix v,Num p) => (Grafo v p) -> (v,v) -> Bool peso :: (Ix v,Num p) => v -> v -> (Grafo v p) -> p
- Descripción de la signatura del TAD de grafos
(creaGrafo o cs as)es un grafo (dirigido o no, según el valor de o), con el par de cotas cs y listas de aristas as (cada arista es un trío formado por los dos vértices y su peso). Ver un ejemplo en el siguiente apartado.(dirigido g)se verifica siges dirigido.(nodos g)es la lista de todos los nodos del grafog.(aristas g)es la lista de las aristas del grafog.(adyacentes g v)es la lista de los vértices adyacentes al nodoven el grafog.(aristaEn g a)se verifica siaes una arista del grafog.(peso v1 v2 g)es el peso de la arista que une los vérticesv1yv2en el grafog.
Ejemplo de creación de grafos: El grafo
12 1 -------- 2 | \78 /| | \ 32/ | | \ / | 34| 5 |55 | / \ | | /44 \ | | / 93\| 3 -------- 4 61se define por
creaGrafo ND (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)]
1.3. Implementación de los grafos como vectores de adyacencia
Cabecera del módulo:
module Tema_22.GrafoConVectorDeAdyacencia (Orientacion (..), Grafo, creaGrafo, -- (Ix v,Num p) => Orientacion -> (v,v) -> [(v,v,p)] -> Grafo v p dirigido, -- (Ix v,Num p) => (Grafo v p) -> Bool adyacentes, -- (Ix v,Num p) => (Grafo v p) -> v -> [v] nodos, -- (Ix v,Num p) => (Grafo v p) -> [v] aristas, -- (Ix v,Num p) => (Grafo v p) -> [(v,v,p)] aristaEn, -- (Ix v,Num p) => (Grafo v p) -> (v,v) -> Bool peso -- (Ix v,Num p) => v -> v -> (Grafo v p) -> p ) where
Librerías auxiliares.
import Data.Array
OrientacionesD(dirigida) óND(no dirigida).data Orientacion = D | ND deriving (Eq, Show)
(Grafo v p)es un grafo con vértices de tipovy pesos de tipop.data Grafo v p = G Orientacion (Array v [(v,p)]) deriving (Eq, Show)
(creaGrafo o cs as)es un grafo (dirigido o no según el valor deo), con el par de cotascsy listas de aristasas(cada arista es un trío formado por los dos vértices y su peso). Los ejemplo se muestran después de la definicióncreaGrafo :: (Ix v, Num p) => Orientacion -> (v,v) -> [(v,v,p)] -> Grafo v p creaGrafo o cs vs = G o (accumArray (\xs x -> xs++[x]) [] cs ((if o == D then [] else [(x2,(x1,p))|(x1,x2,p) <- vs, x1 /= x2]) ++ [(x1,(x2,p)) | (x1,x2,p) <- vs]))
El grafo
12 1 -------- 2 | \78 /| | \ 32/ | | \ / | 34| 5 |55 | / \ | | /44 \ | | / 93\| 3 -------- 4 61se define por
ejGrafoND = creaGrafo ND (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)]
Su evaluación es
λ> ejGrafoND G ND 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)])])
ejGrafoDes el mismo grafo que ejGrafoND pero orientando las aristas de menor a mayor. Su definición esejGrafoD = creaGrafo D (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)]
y su evaluación es
λ> ejGrafoD G D array (1,5) [(1,[(2,12),(3,34),(5,78)]), (2,[(4,55),(5,32)]), (3,[(4,61),(5,44)]), (4,[(5,93)]), (5,[])])
(dirigido g)se verifica siges dirigido. Por ejemplo,dirigido ejGrafoD == True dirigido ejGrafoND == False
Su definición es
dirigido :: (Ix v,Num p) => (Grafo v p) -> Bool dirigido (G o _) = o == D
(adyacentes g v)es la lista de los vértices adyacentes al nodoven el grafog. Por ejemplo,adyacentes ejGrafoND 4 == [2,3,5] adyacentes ejGrafoD 4 == [5]
Su definición es
adyacentes :: (Ix v,Num p) => (Grafo v p) -> v -> [v] adyacentes (G _ g) v = map fst (g!v)
(nodos g)es la lista de todos los nodos del grafog. Por ejemplo,nodos ejGrafoND == [1,2,3,4,5] nodos ejGrafoD == [1,2,3,4,5]
Su definición es
nodos :: (Ix v,Num p) => (Grafo v p) -> [v] nodos (G _ g) = indices g
(peso v1 v2 g)es el peso de la arista que une los vérticesv1yv2en el grafog. Por ejemplo,peso 1 5 ejGrafoND == 78 peso 1 5 ejGrafoD == 78
Su definición es
peso :: (Ix v,Num p) => v -> v -> (Grafo v p) -> p peso x y (G _ g) = head [c | (a,c) <- g!x , a == y]
(aristaEn g a)se verifica siaes una arista del grafog. Por ejemplo,aristaEn ejGrafoND (5,1) == True aristaEn ejGrafoND (4,1) == False aristaEn ejGrafoD (5,1) == False aristaEn ejGrafoD (1,5) == True
Su definición es
aristaEn :: (Ix v,Num p) => (Grafo v p) -> (v,v) -> Bool aristaEn g (x,y) = y `elem` adyacentes g x
(aristas g)es la lista de las aristas del grafog. Por ejemplo,λ> aristas ejGrafoND [(1,2,12),(1,3,34),(1,5,78),(2,1,12),(2,4,55),(2,5,32), (3,1,34),(3,4,61),(3,5,44),(4,2,55),(4,3,61),(4,5,93), (5,1,78),(5,2,32),(5,3,44),(5,4,93)] λ> aristas ejGrafoD [(1,2,12),(1,3,34),(1,5,78),(2,4,55),(2,5,32),(3,4,61), (3,5,44),(4,5,93)]
Su definición es
aristas :: (Ix v,Num p) => (Grafo v p) -> [(v,v,p)] aristas (G o g) = [(v1,v2,w) | v1 <- nodos (G o g) , (v2,w) <- g!v1]
1.4. Implementación de los grafos como matrices de adyacencia
Cabecera del módulo.
module Tema_22.GrafoConMatrizDeAdyacencia (Orientacion (..), Grafo, creaGrafo, -- (Ix v,Num p) => Orientacion -> (v,v) -> [(v,v,p)] -> Grafo v p dirigido, -- (Ix v,Num p) => (Grafo v p) -> Bool adyacentes, -- (Ix v,Num p) => (Grafo v p) -> v -> [v] nodos, -- (Ix v,Num p) => (Grafo v p) -> [v] aristas, -- (Ix v,Num p) => (Grafo v p) -> [(v,v,p)] aristaEn, -- (Ix v,Num p) => (Grafo v p) -> (v,v) -> Bool peso -- (Ix v,Num p) => v -> v -> (Grafo v p) -> p ) where
Librerías auxiliares
import Data.Array
OrientacionesD(dirigida) óND(no dirigida).data Orientacion = D | ND deriving (Eq, Show)
(Grafo v p)es un grafo con vértices de tipovy pesos de tipop.data Grafo v p = G Orientacion (Array (v,v) (Maybe p)) deriving (Eq, Show)
(creaGrafo o cs as)es un grafo (dirigido o no, según el valor deo), con el par de cotascsy listas de aristasas(cada arista es un trío formado por los dos vértices y su peso). Ver un ejemplo a continuación.creaGrafo :: (Ix v, Num p) => Bool -> (v,v) -> [(v,v,p)] -> Grafo v p creaGrafo o cs@(l,u) as = G o (matrizVacia // ([((x1,x2),Just w) | (x1,x2,w) <- as] ++ if o == D then [] else [((x2,x1),Just w) | (x1,x2,w) <- as, x1 /= x2])) where matrizVacia = array ((l,l),(u,u)) [((x1,x2),Nothing) | x1 <- range cs, x2 <- range cs]
ejGrafoNDes el grafo12 1 -------- 2 | \78 /| | \ 32/ | | \ / | 34| 5 |55 | / \ | | /44 \ | | / 93\| 3 -------- 4 61Su definición es
ejGrafoND = creaGrafo ND (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)]
y su evaluación es
λ> ejGrafoND G ND array ((1,1),(5,5)) [((1,1),Nothing),((1,2),Just 12),((1,3),Just 34), ((1,4),Nothing),((1,5),Just 78),((2,1),Just 12), ((2,2),Nothing),((2,3),Nothing),((2,4),Just 55), ((2,5),Just 32),((3,1),Just 34),((3,2),Nothing), ((3,3),Nothing),((3,4),Just 61),((3,5),Just 44), ((4,1),Nothing),((4,2),Just 55),((4,3),Just 61), ((4,4),Nothing),((4,5),Just 93),((5,1),Just 78), ((5,2),Just 32),((5,3),Just 44),((5,4),Just 93), ((5,5),Nothing)]
ejGrafoDes el mismo grafo que ejGrafoND pero orientando las aristas de menor a mayor. Su definición esejGrafoD = creaGrafo D (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)]
y su evaluación es
λ> ejGrafoD G D (array ((1,1),(5,5)) [((1,1),Nothing),((1,2),Just 12),((1,3),Just 34), ((1,4),Nothing),((1,5),Just 78),((2,1),Nothing), ((2,2),Nothing),((2,3),Nothing),((2,4),Just 55), ((2,5),Just 32),((3,1),Nothing),((3,2),Nothing), ((3,3),Nothing),((3,4),Just 61),((3,5),Just 44), ((4,1),Nothing),((4,2),Nothing),((4,3),Nothing), ((4,4),Nothing),((4,5),Just 93),((5,1),Nothing), ((5,2),Nothing),((5,3),Nothing),((5,4),Nothing), ((5,5),Nothing)])(dirigido g)se verifica siges dirigido. Por ejemplo,dirigido ejGrafoD == True dirigido ejGrafoND == False
Su definición es
dirigido :: (Ix v,Num p) => (Grafo v p) -> Bool dirigido (G o _) = o == D
(adyacentes g v)es la lista de los vértices adyacentes al nodoven el grafog. Por ejemplo,adyacentes ejGrafoND 4 == [2,3,5] adyacentes ejGrafoD 4 == [5]
Su definición es
adyacentes :: (Ix v,Num p) => (Grafo v p) -> v -> [v] adyacentes (G o g) v = [v' | v' <- nodos (G o g), (g!(v,v')) /= Nothing]
(nodos g)es la lista de todos los nodos del grafog. Por ejemplo,nodos ejGrafoND == [1,2,3,4,5] nodos ejGrafoD == [1,2,3,4,5]
Su definición es
nodos :: (Ix v,Num p) => (Grafo v p) -> [v] nodos (G _ g) = range (l,u) where ((l,_),(u,_)) = bounds g
(peso v1 v2 g)es el peso de la arista que une los vérticesv1yv2en el grafog. Por ejemplo,peso 1 5 ejGrafoND == 78 peso 1 5 ejGrafoD == 78
Su definición es
peso :: (Ix v,Num p) => v -> v -> (Grafo v p) -> p peso x y (G _ g) = w where (Just w) = g!(x,y)
(aristaEn g a)se verifica siaes una arista del grafog. Por ejemplo,aristaEn ejGrafoND (5,1) == True aristaEn ejGrafoND (4,1) == False
Su definición es
aristaEn :: (Ix v,Num p) => (Grafo v p) -> (v,v) -> Bool aristaEn (G _o g) (x,y)= (g!(x,y)) /= Nothing
(aristas g)es la lista de las aristas del grafog. Por ejemplo,λ> aristas ejGrafoD [(1,2,12),(1,3,34),(1,5,78),(2,4,55),(2,5,32),(3,4,61), (3,5,44),(4,5,93)] λ> aristas ejGrafoND [(1,2,12),(1,3,34),(1,5,78),(2,1,12),(2,4,55),(2,5,32), (3,1,34),(3,4,61),(3,5,44),(4,2,55),(4,3,61),(4,5,93), (5,1,78),(5,2,32),(5,3,44),(5,4,93)]
Su definición es
aristas :: (Ix v,Num p) => (Grafo v p) -> [(v,v,p)] aristas g@(G o e) = [(v1,v2,extrae(e!(v1,v2))) | v1 <- nodos g, v2 <- nodos g, aristaEn g (v1,v2)] where extrae (Just w) = w
2. Recorridos en profundidad y en anchura
2.1. Recorrido en profundidad
Importaciones de librerías auxiliares.
-- Nota: Elegir una implementación de los grafos. import Tema_22.GrafoConVectorDeAdyacencia -- import Tema_22.GrafoConMatrizDeAdyacencia -- import I1M.Grafo
En los ejemplos se usará el grafo
g+---> 2 <---+ | | | | 1 --> 3 --> 6 --> 5 | | | | +---> 4 <---------+
cuya definición es
g = creaGrafo D (1,6) [(1,2,0),(1,3,0),(1,4,0),(3,6,0), (5,4,0),(6,2,0),(6,5,0)]
(recorridoEnProfundidad i g)es el recorrido en profundidad del grafogdesde el vérticei. Por ejemplo,recorridoEnProfundidad 1 g == [1,2,3,6,5,4]
Su definición es
recorridoEnProfundidad :: (Num p, Eq p, Ix v) => v -> Grafo v p -> [v] recorridoEnProfundidad i g = rp [i] [] where rp [] vis = vis rp (c:cs) vis | c `elem` vis = rp cs vis | otherwise = rp ((adyacentes g c)++cs) (vis++[c])
Traza del cálculo de
(recorridoEnProfundidad 1 g)recorridoEnProfundidad 1 g = rp [1] [] = rp [2,3,4] [1] = rp [3,4] [1,2] = rp [6,4] [1,2,3] = rp [2,5,4] [1,2,3,6] = rp [5,4] [1,2,3,6] = rp [4,4] [1,2,3,6,5] = rp [4] [1,2,3,6,5,4] = rp [] [1,2,3,6,5,4] = [1,2,3,6,5,4]
(recorridoEnProfundidad' i g)es el recorrido en profundidad del grafo, usando la lista de los visitados como acumulador. Por ejemplo,recorridoEnProfundidad' 1 g == [1,2,3,6,5,4]
Su definición es
recorridoEnProfundidad' :: (Num p, Eq p, Ix v) => v -> Grafo v p -> [v] recorridoEnProfundidad' i g = reverse (rp [i] []) where rp [] vis = vis rp (c:cs) vis | c `elem` vis = rp cs vis | otherwise = rp ((adyacentes g c)++cs) (c:vis)
Traza del cálculo de
(recorridoEnProfundidad' 1 g)recorridoEnProfundidad' 1 g = reverse (rp [1] []) = reverse (rp [2,3,4] [1]) = reverse (rp [3,4] [2,1]) = reverse (rp [6,4] [3,2,1]) = reverse (rp [2,5,4] [6,3,2,1]) = reverse (rp [5,4] [6,3,2,1]) = reverse (rp [4,4] [5,6,3,2,1]) = reverse (rp [4] [4,5,6,3,2,1]) = reverse (rp [] [4,5,6,3,2,1]) = reverse [4,5,6,3,2,1] = [1,2,3,6,5,4]
2.2. Recorrido en anchura
Importaciones de librerías auxiliares.
-- Nota: Elegir una implementación de los grafos. import Tema_22.GrafoConVectorDeAdyacencia -- import Tema_22.GrafoConMatrizDeAdyacencia -- import I1M.Grafo
(recorridoEnAnchura i g)es el recorrido en anchura del grafogdesde el vérticei. Por ejemplo,recorridoEnAnchura 1 g == [1,2,3,4,6,5]
Su definición es
recorridoEnAnchura :: (Num p, Eq p, Ix v) => v -> Grafo v p -> [v] recorridoEnAnchura i g = reverse (ra [i] []) where ra [] vis = vis ra (c:cs) vis | c `elem` vis = ra cs vis | otherwise = ra (cs ++ adyacentes g c) (c:vis)
Traza del cálculo de
(recorridoEnAnchura 1 g)RecorridoEnAnchura 1 g = ra [1] [] = ra [2,3,4] [1] = ra [3,4] [2,1] = ra [4,6] [3,2,1] = ra [6] [4,3,2,1] = ra [2,5] [6,4,3,2,1] = ra [5] [6,4,3,2,1] = ra [4] [5,6,4,3,2,1] = ra [] [5,6,4,3,2,1] = [1,2,3,4,6,5]
3. Árboles de expansión mínimos
- Sea G = (V,A) un grafo conexo no orientado en el que cada arista tiene un peso no negativo. Un árbol de expansión mínimo de G es un subgrafo G' = (V,A') que conecta todos los vértices de G y tal que la suma de sus pesos es mínima.
- Aplicación: Si los vértices representan ciudades y el coste de una arista {a,b} es el construir una carretera de a a b, entonces un árbol de expansión mínimo representa el modo de enlazar todas las ciudades mediante una red de carreteras de coste mínimo.
- Terminología de algoritmos voraces: Sea G = (V,A) un grafo y T un
conjunto de aristas de G.
- T es una solución si es un grafo de expansión.
- T es completable si no tiene ciclos.
- T es prometedor si es completable y puede ser completado hasta llegar a una solución óptima.
- Una arista toca un conjunto de vértices B si exactamente uno de sus extremos pertenece a B.
- Teorema: Sea G = (V,A) un grafo conexo no orientado cuyas aristas tienen un peso asociado. Sea B un subjconjunto propio del conjunto de vértices V y T un conjunto prometedor de aristas tal que ninguna arista de T toca a B. Sea e una arista de peso mínimo de entre todas las que tocan a B. Entonces, (T ∪ {e}) es prometedor.
3.1. El algoritmo de Kruskal
Para los ejemplos se considera el siguiente grafo:
1 2 1 ----- 2 ----- 3 | /| /| | / | / | | / | / | 4| /6 |4 /5 |6 | / | / | | / | / | |/ |/ | 4 ----- 5 ----- 6 \ 3 | 8 / \ | / \ | / 4\ |7 /3 \ | / \ | / \|/ 7Aplicación del algoritmo de Kruskal al grafo anterior:
Etapa Arista Componentes conexas 0 {1} {2} {3} {4} {5} {6} {7} 1 {1,2} {1,2} {3} {4} {5} {6} {7} 2 {2,3} {1,2,3} {4} {5} {6} {7} 3 {4,5} {1,2,3} {4,5} {6} {7} 4 {6,7} {1,2,3} {4,5} {6,7} 5 {1,4} {1,2,3,4,5} {6,7} 6 {2,5} arista rechazada 7 {4,7} {1,2,3,4,5,6,7}El árbol de expansión mínimo contiene las aristas no rechazadas:
{1,2}, {2,3}, {4,5}, {6,7}, {1,4} y {4,7}.Librerías auxiliares.
import Tema_22.GrafoConVectorDeAdyacencia -- import Tema_22.GrafoConMatrizDeAdyacencia -- import I1M.Grafo -- Elegir la 1ª si se ha elegido una del Tema 22 y actualizarla para -- usar la misma implementación. import Tema_22.RecorridoEnAnchura -- import I1M.RecorridoEnAnchura import qualified Data.Map as M import Data.List import Data.Ix import Test.QuickCheck
Grafos usados en los ejemplos.
g1, g2, g3, g4 :: Grafo Int Int g1 = creaGrafo ND (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)] g2 = creaGrafo ND (1,5) [(1,2,13),(1,3,11),(1,5,78), (2,4,12),(2,5,32), (3,4,14),(3,5,44), (4,5,93)] g3 = creaGrafo ND (1,7) [(1,2,5),(1,3,9),(1,5,15),(1,6,6), (2,3,7), (3,4,8),(3,5,7), (4,5,5), (5,6,3),(5,7,9), (6,7,11)] g4 = creaGrafo ND (1,7) [(1,2,5),(1,3,9),(1,5,15),(1,6,6), (2,3,7), (3,4,8),(3,5,1), (4,5,5), (5,6,3),(5,7,9), (6,7,11)]
(kruskal g)es el árbol de expansión mínimo del grafogcalculado mediante el algoritmo de Kruskal. Por ejemplo,kruskal g1 == [(55,2,4),(34,1,3),(32,2,5),(12,1,2)] kruskal g2 == [(32,2,5),(13,1,2),(12,2,4),(11,1,3)] kruskal g3 == [(9,5,7),(7,2,3),(6,1,6),(5,4,5),(5,1,2),(3,5,6)] kruskal g4 == [(9,5,7),(6,1,6),(5,4,5),(5,1,2),(3,5,6),(1,3,5)]
kruskal :: (Ix v, Num p, Ord p) => Grafo v p -> [(p,v,v)] kruskal g = aux (sort [(p,x,y) | (x,y,p) <- aristas g]) (M.fromList [(x,x) | x <- nodos g]) [] (length (nodos g) - 1) where aux _ _ ae 0 = ae aux [] _ _ _ = error "Imposible" aux ((p,x,y):as) d ae n | actualizado = aux as d' ((p,x,y):ae) (n-1) | otherwise = aux as d ae n where (actualizado,d') = buscaActualiza (x,y) d
(raiz t n)es la raíz denen el diccionariod. Por ejemplo,raiz (M.fromList [(1,1),(3,1),(4,3),(5,4),(2,6),(6,6)]) 5 == 1 raiz (M.fromList [(1,1),(3,1),(4,3),(5,4),(2,6),(6,6)]) 2 == 6
Su definición es
raiz :: (Eq n, Ord n) => M.Map n n -> n -> n raiz d x | v == x = v | otherwise = raiz d v where v = d M.! x
(buscaActualiza a d)es el par formado porFalsey el diccionariod, si los dos vértices de la arista a tienen la misma raíz endy el par formado porTruey el diccionario obtenido añadiéndole adla arista formada por el vértice deade mayor raíz y la raíz del vértice deade menor raíz. Y actualizando las raices de todos los elementos afectados por la raíz añadida. Por ejemplo,λ> d = M.fromList [(1,1),(2,1),(3,3),(4,4),(5,5),(6,5),(7,7)] λ> buscaActualiza (5,4) d (True,fromList [(1,1),(2,1),(3,3),(4,4),(5,4),(6,4),(7,7)]) λ> d' = snd it λ> buscaActualiza (6,1) d' (True,fromList [(1,1),(2,1),(3,3),(4,1),(5,1),(6,1),(7,7)])
Su definición es
buscaActualiza :: (Eq n, Ord n) => (n,n) -> M.Map n n -> (Bool,M.Map n n) buscaActualiza (x,y) d | x' == y' = (False, d) | y' < x' = (True, modificaR x (d M.! x) y' d) | otherwise = (True, modificaR y (d M.! y) x' d) where x' = raiz d x y' = raiz d y
(modificaR x y y' d)actualizadcomo sigue:- el valor de todas las claves
zcon valoryesy' - el valor de todas las claves
zcon (z>x) con valorxesy'
Su definición es
modificaR :: (Eq n, Ord n) => n -> n -> n -> M.Map n n -> M.Map n n modificaR x y y' d = aux2 ds (aux1 cs d) where cs = M.keys d ds = filter (>x) cs aux1 [] tb = tb aux1 (a:as) tb | tb M.! a == y = aux1 as (M.update (\_ -> Just y') a tb) | otherwise = aux1 as tb aux2 [] tb = tb aux2 (b:bs) tb | tb M.! b == x = aux2 bs (M.update (\_ -> Just y') b tb) | otherwise = aux2 bs tb
- el valor de todas las claves
- Traza del diccionario correspondiente al grafo
g3Lista de aristas, ordenadas según su peso:
[(3,5,6),(5,1,2),(5,4,5),(6,1,6),(7,2,3),(7,3,5), (8,3,4),(9,1,3),(9,5,7),(11,6,7),(15,1,5)]
Inicial
fromList [(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7)]
Después de añadir la arista (5,6) de peso 3
fromList [(1,1),(2,2),(3,3),(4,4),(5,5),(6,5),(7,7)]
Después de añadir la arista (1,2) de peso 5
fromList [(1,1),(2,1),(3,3),(4,4),(5,5),(6,5),(7,7)]
Después de añadir la arista (4,5) de peso 5
fromList [(1,1),(2,1),(3,3),(4,4),(5,4),(6,4),(7,7)]
Después de añadir la arista (1,6) de peso 6
fromList [(1,1),(2,1),(3,3),(4,1),(5,1),(6,1),(7,7)]
Después de añadir la arista (2,3) de peso 7
fromList [(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,7)]
- Las posibles aristas a añadir son:
- la (3,5) con peso 7, que no es posible pues la raíz de 3 coincide con la raíz de 5, por lo que formaría un ciclo
- la (3,4) con peso 8, que no es posible pues la raíz de 3 coincide con la raíz de 4, por lo que formaría un ciclo
- la (1,3) con peso 9, que no es posible pues la raíz de 3 coincide con la raíz de 1, por lo que formaría un ciclo
- la (5,7) con peso 9, que no forma ciclo
Después de añadir la arista (5,7) con peso 9
fromList [(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1)]
- No es posible añadir más aristas, pues formarían ciclos.
3.2. El algoritmo de Prim
(prim g)es el árbol de expansión mínimo del grafogcalculado mediante el algoritmo de Prim. Por ejemplo,prim g1 == [(55,2,4),(34,1,3),(32,2,5),(12,1,2)] prim g2 == [(32,2,5),(12,2,4),(13,1,2),(11,1,3)] prim g3 == [(9,5,7),(7,2,3),(5,5,4),(3,6,5),(6,1,6),(5,1,2)]
Su definición es
prim :: (Ix v, Num p, Ord p) => Grafo v p -> [(p,v,v)] prim g = prim' [n] -- Nodos colocados ns -- Nodos por colocar [] -- Árbol de expansión (aristas g) -- Aristas del grafo where (n:ns) = nodos g prim' _ _ _ [] = [] prim' _ [] ae _ = ae prim' t r ae as = prim' (v':t) (delete v' r) (e:ae) as where e@(_,_, v') = minimum [(c,u,v)| (u,v,c) <- as, u `elem` t, v `elem` r]
3.3. Comprobación de propiedades de los algoritmos de Kruskal y Prim
(generaGND n ps)es el grafo completo de ordenntal que los pesos están determinados porps. Por ejemplo,λ> generaGND 3 [4,2,5] (ND,array (1,3) [(1,[(2,4),(3,2)]), (2,[(1,4),(3,5)]), 3,[(1,2),(2,5)])]) λ> generaGND 3 [4,-2,5] (ND,array (1,3) [(1,[(2,4)]),(2,[(1,4),(3,5)]),(3,[(2,5)])])Su definición es
generaGND :: Int -> [Int] -> Grafo Int Int generaGND n ps = creaGrafo ND (1,n) l3 where l1 = [(x,y) | x <- [1..n], y <- [1..n], x < y] l2 = zip l1 ps l3 = [(x,y,z) | ((x,y),z) <- l2, z > 0]
genGNDes un generador de grafos no dirigidos. Por ejemplo,λ> sample genGND (ND,array (1,1) [(1,[])]) (ND,array (1,3) [(1,[(2,3),(3,13)]),(2,[(1,3)]),(3,[(1,13)])]) ...
Su definición es
genGND :: Gen (Grafo Int Int) genGND = do n <- choose (1,50) xs <- vectorOf (n*n) arbitrary return (generaGND n xs)
Los grafos está contenido en la clase de los objetos generables aleatoriamente.
instance Arbitrary (Grafo Int Int) where arbitrary = genGND
(conexo g)se verifica si el grafo no dirigidoges conexo. Por ejemplo,conexo (creaGrafo False (1,3) [(1,2,0),(3,2,0)]) == True conexo (creaGrafo False (1,4) [(1,2,0),(3,4,0)]) == False
Su definición es
conexo :: (Ix a, Num p, Eq p) => Grafo a p -> Bool conexo g = length (recorridoEnAnchura i g) == n where xs = nodos g i = head xs n = length xs
Propiedad: Si
ges un grafo no dirigido conexo y con aristas, entonces(kruskal g)tiene el mismo conjunto de nodos queg,(prim g)tiene el mismo conjunto de nodos quegy- los pesos de
(kruskal g)y(prim g)son iguales.
prop_AE :: Grafo Int Int -> Property prop_AE g = not (null (aristas g)) && conexo g ==> nodosAE (kruskal g) == ns && nodosAE (prim g) == ns && pesoAE (kruskal g) == pesoAE (prim g) where ns = nodos g nodosAE ae = sort (nub (concat [[x,y] | (_,x,y) <- ae])) pesoAE xs = sum [p | (p,_,_) <- xs]
La comprobación es
λ> quickCheck prop_AE +++ OK, passed 100 tests.
4. Material complementario
El código del tema se encuentra en los siguientes enlaces
- Implementación de los grafos mediante vectores de adyacencia.
- Implementación de los grafos mediante matrices de adyacencia.
- Recorrido en profundidad.
- Recorrido en anchura.
- Algoritmos de Kruskal y de Prim.
Este tema también se encuentra en los siguientes formatos:
- Como transparencias en PDF.
- Como libro interactivo en IHaskell sobre Jupyter.
- Como vídeos de clase: vídeo 1 y vídeo 2.
5. Bibliografía
- F. Rabhi y G. Lapalme. Algorithms: A functional programming approach.
- Cap. 7: Graph algorithms.
- Graph algorithms en Wikipedia
- The Stony Brook algorithm repository: