Algoritmo de recorridos minimos de grafos. Sebastián Estrada Mejía. 910015 ¿Que problema resuelve? En la Teoría de grafos, el problema de los caminos más cortos es el problema que consiste en encontrar un camino entre dos vértices (o nodos) de tal manera que la suma de los pesos de las aristas que lo constituyen es mínima. Un ejemplo es encontrar el camino más rápido para ir de una ciudad a otra en un mapa. En este caso, los vértices representan las ciudades, y las aristas las carreteras que las unen, cuya ponderación viene dada por el tiempo que se emplea en atravesarlas. Algoritmo de Dijkstra Logo ¿Como procesada de datos? Teniendo un grafo dirigido ponderado de N nodos no aislados, sea x el nodo inicial, un vector D de tamaño N guardará al final del algoritmo las distancias desde x al resto de los nodos. Algoritmo: 1. Inicializar todas las distancias en D con un valor infinito relativo ya que son desconocidas al principio, exceptuando la de x que se debe colocar en 0 debido a que la distancia de x a x sería 0. 2. Sea a = x (tomamos a como nodo actual). 3. Recorremos todos los nodos adyacentes de a, excepto los nodos marcados, llamaremos a estos vi. 4. Si la distancia desde x hasta vi guardada en D es mayor que la distancia desde x hasta a, sumada a la distancia desde a hasta vi ; esta se sustituye con la segunda nombrada, esto es: si (Di > Da + d(a, vi)) entonces Di = Da + d(a, vi) 5. Marcamos como completo el nodo a. 6. Tomamos como próximo nodo actual el de menor valor en D (puede hacerse almacenando los valores en una cola de prioridad) y volvemos al paso 3 mientras existan nodos no marcados. Una vez terminado al algoritmo, D estará completamente lleno DIJKSTRA (Grafo G, nodo_fuente s) para u ∈ V[G] hacer distancia[u] = INFINITO padre[u] = NULL distancia[s] = 0 adicionar (cola, (s,distance[s])) mientras que cola no es vacía hacer u = extraer_minimo(cola) para v ∈ adyacencia[u] hacer si distancia[v] > distancia[u] + peso (u, v) hacer distancia[v] = distancia[u] + peso (u, v) padre[v] = u adicionar(cola,(v,distance[v])) Complejidad. Orden de complejidad del algoritmo: O(|V|2+|E|) = O (|V|2) sin utilizar cola de prioridad, O((|E|+|V|) log |V|) utilizando cola de prioridad (por ejemplo un montículo). Algoritmo de Bellman-Ford Logo Algoritmo: El Algoritmo de Bellman-Ford es, en su estructura básica, muy parecido al algoritmo de Dijkstra, pero en vez de seleccionar vorazmente el nodo de peso mínimo aun sin procesar para relajarlo, simplemente relaja todas las aristas, y lo hace |V|-1 veces, siendo |V| el número de vértices en el grafo. Las repeticiones permiten a las distancias mínimas recorrer el árbol, ya que en la ausencia de ciclos negativos, el camino más corto solo visita cada vértice una vez. A diferencia de la solución voraz, la cual depende de la suposición de que los pesos sean positivos, esta solución se aproxima más al caso general. K=0 y n=4 vi(0) =Ci,4 i=0 v0(0) =C0,4=INF i=1 v1(0) =C1,4=5 i=2 v2(0) =C2,4=4 i=3 v3(0) =C3,4=1 i=4 v4(0) =C4,4=0 K=1 vi (1) = min vj j i (0) + Ci,j i=0,1,2,3 j=0,1,2,3,4 Logo BellmanFord(Grafo G, nodo_origen s) // inicializamos el grafo. Ponemos distancias a INFINITO menos el nodo origen que // tiene distancia 0 for v ∈ V[G] do distancia[v]=INFINITO predecesor[v]=NIL distancia[s]=0 // relajamos cada arista del grafo tantas veces como número de nodos -1 haya en el grafo for i=1 to |V[G]-1| do for (u, v) ∈ E[G] do if distancia[v]>distancia[u] + peso(u, v) then distancia[v] = distancia[u] + peso (u, v) predecesor[v] = u // comprobamos si hay ciclos negativo for (u, v) ∈ E[G] do if distancia[v] > distancia[u] + peso(u, v) then print ("Hay ciclo negativo") return FALSE return TRUE Complejidad del algoritmo. - Tiene una complejidad del orden de N3 por cada nodo que realiza el algoritmo, es decir, un orden mayor que Dijkstra. El interés del algoritmo radica en que se asemeja al de una red en la que se parte de la ignorancia total de un nodo, en la siguiente iteración conoce a sus nodos vecinos y así va progresivamente conociendo nodos más lejanos. - La complejidad computacional de este problema es complejidad NPCompleto. Complejidad NP-completo. En teoría de la complejidad computacional, la clase de complejidad NPcompleto es el subconjunto de los problemas de decisión en NP tal que todo problema en NP se puede reducir en cada uno de los problemas de NPcompleto. Se puede decir que los problemas de NP-completo son los problemas más difíciles de NP y muy probablemente no formen parte de la clase de complejidad P. La razón es que de tenerse una solución polinómica para un problema NP-completo, todos los problemas de NP tendrían también una solución en tiempo polinómico. Si se demostrase que un problema NPcompleto, llamémoslo A, no se pudiese resolver en tiempo polinómico, el resto de los problemas NP-completos tampoco se podrían resolver en tiempo polinómico. Esto se debe a que si uno de los problemas NP-completos distintos de A, digamos X, se pudiese resolver en tiempo polinómico, entonces A se podría resolver en tiempo polinómico, por definición de NP-completo. Ahora, pueden existir problemas en NP y que no sean NP-completos para los cuales exista solución polinómica aún no existiendo solución para A. Algoritmo de Floyd-Warshall El algoritmo de Floyd-Warshall compara todos los posibles caminos a través del grafo entre cada par de vértices. El algoritmo es capaz de hacer esto con sólo V3 comparaciones (esto es notable considerando que puede haber hasta V2 aristas en el grafo, y que cada combinación de aristas se prueba). Lo hace mejorando paulatinamente una estimación del camino más corto entre dos vértices, hasta que se sabe que la estimación es óptima. Sea un grafo G con conjunto de vértices V, numerados de 1 a N. Sea además una función caminoMinimo(i,j,k) que devuelve el camino mínimo de i a j usando únicamente los vértices de 1 a k como puntos intermedios en el camino. Ahora, dada esta función, nuestro objetivo es encontrar el camino mínimo desde cada i a cada j usando únicamente los vértices de 1 hasta k + 1. Hay dos candidatos para este camino: un camino mínimo, que utiliza únicamente los vértices del conjunto (1...k); o bien existe un camino que va desde i hasta k + 1, y de k + 1 hasta j, que es mejor. Sabemos que el camino óptimo de i a j que únicamente utiliza los vértices de 1 hasta k está definido por caminoMinimo(i,j,k), y está claro que si hubiera un camino mejor de i a k + 1 a j, la longitud de este camino sería la concatenación del camino mínimo de i a k + 1 (utilizando vértices de (1...k)) y el camino mínimo de k + 1 a j (que también utiliza los vértices en (1... k)). Do 1 2 3 4 1 2 3 4 0 INF 4 INF 8 0 INF 2 INF 1 0 9 1 INF INF 0 Do 1 2 3 4 1 2 3 4 0 5 4 7 3 0 7 2 4 1 0 3 1 6 5 0 S4 1 2 3 4 1 2 3 4 0 1 1 1 2 0 2 2 3 3 0 3 4 4 4 0 S4 1 2 3 4 1 2 3 4 0 3 1 3 4 0 4 2 4 3 0 2 4 3 1 0 Floyd-Warshall (G) n=|V [G]| for (int i=1; i<=numeroNodos; i++) for (int j=1; j<=numeroNodos; j++) si Hay conexión MatrizdePeso[i][j]=peso; else MatrizdePeso[i][j]=infinito; MatrizNodoIntermedio[i][j]=j; Si i=j MatrizNodoIntermedio[i][j]=0; MatrizdePeso[i][j]=0; for(int k=1;k<=numeroNodos;k++) for(int i=1;i<=numeroNodos;i++) for(int j=1;j<=numeroNodos;j++) a=MatrizdePeso[i][k]+MatrizdePeso[k][j]; i f(a<MatrizdePeso[i][j]) { MatrizdePeso[i][j]=a; MatrizNodoIntermedio[i][j]=k; } return MatrizdePeso, MatrizNodoIntermedio;