La matriz de predecesores de un grafo nos indica el camino más corto de un nodo a otro para todos los nodos de un grafo. Hay una variedad de métodos para construir el camino más corto de un grafo, en esta ocasión usaremos los algoritmos de Dijkstra y Floyd-Warshall para construir la matriz de distancia para formar la matriz de predecesores.
Matriz de predecesores
Tenemos un grafo G representado en la figura 1:
En este grafo la Matriz de predecesores sería:
Para leerlo tomemos como ejemplo el nodo 0. En la primera fila podemos leer el camino más corto desde el nodo 0 a todas las demás. En la segunda columna aparece el 4, nos indica que el predecesor del nodo 1 para llegar al nodo 0 es el nodo 4. Si nos vamos a la columna del nodo 4 (quinta columna) vemos que el predecesor del nodo 4 para llegar al nodo 0 es el mismo nodo 0. De esta manera se puede armar el camino. Los que están marcados con -1 nos dice que no hay predecesor para llegar a tal nodo.
Dijkstra
El algoritmo para generar los predecesores de un nodo es el siguiente:
import numpy as np def Dijkstra_PREV(source, graph): Q = list(graph.nodes) prev = np.full((graph.nodes.size), -1) dist = np.full((graph.nodes.size), np.inf) dist[source] = 0 while len(Q) > 0: min, u, index = np.inf, 0, 0 for i, q in enumerate(Q): if dist[q] < min: min, u, index = dist[q], q, i Q.pop(index) u_targets, u_weights = graph.get_targets_from_source(u, return_weight=True) for v, w_uv in zip(u_targets, u_weights): aux = dist[u] + w_uv if aux < dist[v]: dist[v] = aux prev[v] = u return dist, prev
Para dijkstra solo agregamos la linea 7 y la linea 26. De esta manera nos devuelve tanto las distancia (cuánto cuesta llegar al nodo) como los predecesores con la variable prev
.
Para correrlo, usaremos el mismo grafo anterior:
# Import modules... sources = [0, 0, 1, 1, 1, 2, 2, 2, 3, 4] targets = [3, 4, 2, 3, 4, 1, 3, 4, 4, 1] weights = [8, 6, 4, 5, 2, 9, 8, 5, 4, 2] graph = Graph(sources, targets, weights) graph.print_r() dist, prev = Dijkstra_PREV(0, graph) print(prev)
El resultado:
Dijkstra APSP
El APSP de dijkstra es ejecutar el código por cada nodo del grafo, la función sería:
def Dijkstra_apsp_PREV(graph): result = np.full((graph.nodes.size, graph.nodes.size), np.inf) result_prev = np.full((graph.nodes.size, graph.nodes.size), -1) total = len(graph.nodes) for i, v in enumerate(graph.nodes): result[i], result_prev[i] = Dijkstra_PREV(v, graph) return result, result_prev
El resultado sería:
Floyd-Warshall
Así como en Dijkstra, se agregan algunas nuevas lineas para conseguir la matriz de predecesores:
import numpy as np def Floyd_Warshall_PREV(graph): dist = np.full((graph.nodes.size, graph.nodes.size), np.inf) prev = np.full((graph.nodes.size, graph.nodes.size), -1) for idx in range(graph.source.size): dist[graph.source[idx], graph.target[idx]] = graph.weight[idx] prev[graph.source[idx], graph.target[idx]] = graph.source[idx] for k in graph.nodes: for i in graph.nodes: if k == i: dist[i, k] = 0 prev[i, k] = -1 for j in graph.nodes: if dist[i, j] > dist[i, k] + dist[k, j]: dist[i, j] = dist[i, k] + dist[k, j] prev[i, j] = prev[k, j] return dist, prev
Al correr tenemos el mismo resultado.
Código para test
import os import sys import numpy as np myPath = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, myPath + '/../') from graph.Graph import Graph from algorithms_prev.dijkstra_prev import * from algorithms_prev.floyd_warshall_prev import * sources = [0, 0, 1, 1, 1, 2, 2, 2, 3, 4] targets = [3, 4, 2, 3, 4, 1, 3, 4, 4, 1] weights = [8, 6, 4, 5, 2, 9, 8, 5, 4, 2] #Grafo con más de una misma solución """ sources = [0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5] targets = [1, 2, 5, 0, 3, 4, 5, 5, 3, 4, 0, 1, 0, 1, 4, 5, 0, 2, 3, 5, 1, 4] weights = [9, 6, 3, 6, 8, 7, 5, 4, 4, 4, 5, 1, 4, 4, 9, 2, 9, 7, 1, 4, 6, 1] """ graph = Graph(sources, targets, weights) #Grafos Aleatorios """ graph = Graph.creategraph(6, .8) """ graph.print_r() dist, prev = Dijkstra_apsp_PREV(graph) print(dist) print(prev) print("-----------------------------------------") dist, prev2 = Floyd_Warshall_PREV(graph) print(prev2)
Al descargar el repositorio se puede ejecutar el código anterior para usar usus propios grafos y realizar pruebas.
Código
Algoritmos: https://github.com/arturoverbel/graph_presentation/tree/master/algorithms_prev
Comentarios