Ir al contenido

Algoritmo de Floyd-Warshall

De Wikipedia, la enciclopedia libre
Diagrama mostrando las ecuaciones del Algoritmo de Floyd-Warshall para calcular los caminos más cortos entre todos los pares de vértices en un grafo ponderado.

En informática, el algoritmo de Floyd-Warshall, descrito en 1959 por Bernard Roy, es un algoritmo de análisis sobre grafos para encontrar el camino mínimo en grafos dirigidos ponderados. El algoritmo encuentra el camino entre todos los pares de vértices en una única ejecución. El algoritmo de Floyd-Warshall es un ejemplo de programación dinámica.

El algoritmo de Warshall

[editar]

El algoritmo de Warshall es un ejemplo de algoritmo booleano. A partir de una tabla inicial compuesta de 0`s (no hay correspondencia inicial en el grafo) y 1`s (hay una correspondencia, llamase “flecha”, entre nodos), obtiene una nueva matriz denominada “Matriz de Clausura Transitiva” en la que se muestran todas las posibles uniones entre nodos, directa o indirectamente. Es decir, si de “A” a “B” no hay una “flecha”, es posible que si haya de “A” a “C” y luego de “C” a “B”. Luego, este resultado se verá volcado en la matriz final.

El algoritmo de Floyd

[editar]

El algoritmo de Floyd es muy similar, pero trabaja con grafos ponderados. Es decir, el valor de la “flecha” que representamos en la matriz puede ser cualquier número real o infinito. Infinito marca que no existe unión entre los nodos. Esta vez, el resultado será una matriz donde estarán representadas las distancias mínimas entre nodos, seleccionando los caminos más convenientes según su ponderación (“peso”). Por ejemplo, si de “A” a “B” hay 36 (km), pero de “A” a “C” hay 2(km) y de “C” a “B” hay 10 (km), el algoritmo nos devolverá finalmente que de “A” a “B” hay 12 (km).

Los pasos a dar en la aplicación del algoritmo de Floyd son los siguientes:

* Formar las matrices iniciales C y D, donde C es la matriz de adyacencia, y D es una matriz del mismo tamaño cargada con valores iniciales Dij = i.

* Se toma k=1.

* Se selecciona la fila y la columna k de la matriz C y entonces, para i y j, con i≠k, j≠k e i≠j, hacemos:

Si (Cik Ckj) < Cij → Dij = Dkj y Cij = Cik Ckj

En caso contrario, dejamos las matrices como están.

* Si k ≤ n, aumentamos k en una unidad y repetimos el paso anterior, en caso contrario paramos las iteraciones.

* La matriz final C contiene los costes óptimos para ir de un vértice a otro, mientras que la matriz D contiene los penúltimos vértices de los caminos óptimos que unen dos vértices, lo cual permite reconstruir cualquier camino óptimo para ir de un vértice a otro.

Algoritmo

[editar]

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 comparaciones (esto es notable considerando que puede haber hasta 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 con conjunto de vértices , numerados de 1 a N. Sea además una función que devuelve el camino mínimo de a usando únicamente los vértices de 1 a como puntos intermedios en el camino. Ahora, dada esta función, nuestro objetivo es encontrar el camino mínimo desde cada a cada usando únicamente los vértices de 1 hasta .

Hay dos candidatos para este camino: un camino mínimo, que utiliza únicamente los vértices del conjunto ; o bien existe un camino que va desde hasta , y de hasta , que es mejor. Sabemos que el camino óptimo de a que únicamente utiliza los vértices de 1 hasta está definido por , y está claro que si hubiera un camino mejor de a a , la longitud de este camino sería la concatenación del camino mínimo de a (utilizando vértices de ) y el camino mínimo de a (que también utiliza los vértices en ).

Por lo tanto, podemos definir de forma recursiva:

Esta fórmula es la base del algoritmo Floyd-Warshall. Funciona ejecutando primero para todos los pares , usándolos para después hallar para todos los pares ... Este proceso continúa hasta que , y habremos encontrado el camino más corto para todos los pares de vértices usando algún vértice intermedio.

Pseudocodigo

[editar]

Convenientemente, cuando calculamos el k-esimo caso, se puede sobreescribir la información salvada en la computación k -1. Esto significa que el algoritmo usa memoria cuadrática. Hay que cuidar la inicialización de las condiciones:

1 /* Suponemos que la función pesoArista devuelve el coste del camino que va de i a j
2    (infinito si no existe).
3 También suponemos que  es el número de vértices y pesoArista(i,i) = 0
4 */
5
6 int camino[][];
7 /* Una matriz bidimensional. En cada paso del algoritmo, camino[i][j] es el camino mínimo 
8 de i hasta j usando valores intermedios de (1..k-1). Cada camino[i][j] es inicializado a 
9
10 */
11
12 procedimiento FloydWarshall ()
13    para k: = 0 hasta n − 1
14
15          camino[i][j] = mín ( camino[i][j], camino[i][k] camino[k][j])
16
17    fin para

Código en C

[editar]
// Declaraciones en el archivo .h
int cn; //cantidad de nodos
vector< vector<int> > ady;

// Devuelve una matriz con las distancias mínimas de cada nodo al resto de los vértices.
vector< vector<int> > Grafo :: floydWarshall(){
    vector< vector<int> > path = this->ady;


    /* No tendría porqué ser necesario si ya hay ceros en las diagonales de ady */
    for(int i = 0; i < cn; i  )
        path[i][i] = 0;
    
    for(int k = 0; k < cn; k  )
        for(int i = 0; i < cn; i  )
            for(int j = 0; j < cn; j  ){
                int dt = path[i][k]   path[k][j];
                if(path[i][j] > dt)
                    path[i][j] = dt;
            }
    
    return path;
}

Código en Java

[editar]
/** Numero de nodos del grafo */
	private int numNodos;
/** Matriz de adyacencia, para almacenar los arcos del grafo */
	private int[][] arcos = new int[TAM][TAM];
/** Matriz de Camino (Warshall - Path) */
	private boolean[][] warshallC = new boolean[TAM][TAM];
	
public void warshall() {
		int i, j, k;

		// Obtener la matriz de adyacencia en P
		for (i = 0; i < numNodos; i  )
			for (j = 0; j < numNodos; j  )
				warshallC[i][j] = (arcos[i][j] != INFINITO);

		// Iterar
		for (k = 0; k < numNodos; k  )
			for (i = 0; i < numNodos; i  )
				for (j = 0; j < numNodos; j  )
					warshallC[i][j] = (warshallC[i][j] || (warshallC[i][k] && warshallC[k][j]));
	}

Comportamiento con ciclos negativos

[editar]

Para que haya coherencia numérica, Floyd-Warshall supone que no hay ciclos negativos (de hecho, entre cualquier pareja de vértices que forme parte de un ciclo negativo, el camino mínimo no está bien definido porque el camino puede ser infinitamente pequeño). No obstante, si hay ciclos negativos, Floyd-Warshall puede ser usado para detectarlos. Si ejecutamos el algoritmo una vez más, algunos caminos pueden decrementarse pero no garantiza que, entre todos los vértices, caminos entre los cuales puedan ser infinitamente pequeños, el camino se reduzca. Si los números de la diagonal de la matriz de caminos son negativos, es condición necesaria y suficiente para que este vértice pertenezca a un ciclo negativo.

Ejemplo

[editar]

Hallar el camino mínimo desde el vértice 3 hasta 4 en el grafo con la siguiente matriz de distancias:

Aplicamos el algoritmo de Floyd-Warshall, y para ello en cada iteración fijamos un vértice intermedio.

1ª Iteración: nodo intermedio = 1

La matriz es simétrica, por lo que solamente hará falta calcular el triángulo superior de las distancias.

d23 = min(d23, d21 d13) = 8

d24 = min(d24, d21 d14) = 4

d25 = min(d25, d21 d15) = 9

d26 = min(d26, d21 d16) =

d32 = min(d32, d31 d12) = 8

d34 = min(d34, d31 d14) = 6

d35 = min(d35, d31 d15) = 7

d36 = min(d36, d31 d16) = 1

d45 = min(d45, d41 d15) =

d46 = min(d46, d41 d16) = 4

d56 = min(d56, d51 d16) =

La matriz de distancia después de esta iteración es:

2ª Iteración: nodo intermedio = 2

d13 = min(d13, d12 d23) = 5

d14 = min(d14, d12 d24) = 1

d15 = min(d15, d12 d25) = 12

d16 = min(d16, d12 d26) =

d34 = min(d34, d32 d24) = 6

d35 = min(d35, d32 d25) = 7

d36 = min(d36, d32 d26) = 1

d45 = min(d45, d42 d25) = 13

d46 = min(d46, d42 d26) = 4

d56 = min(d56, d52 d26) =

La matriz de distancia después de esta iteración es:

3ª Iteración: nodo intermedio = 3

d12 = min(d12, d13 d32) = 3

d14 = min(d14, d13 d34) = 1

d15 = min(d15, d13 d35) = 12

d16 = min(d16, d13 d36) = 6

d24 = min(d24, d23 d34) = 4

d25 = min(d25, d23 d35) = 9

d26 = min(d26, d23 d36) = 9

d45 = min(d45, d43 d35) = 13

d46 = min(d46, d43 d36) = 4

d56 = min(d56, d53 d36) = 8

La matriz de distancia después de esta iteración es:

4ª Iteración: nodo intermedio = 4

d12 = min(d12, d14 d42) = 3

d13 = min(d13, d14 d43) = 5

d15 = min(d15, d14 d45) = 12

d16 = min(d16, d14 d46) = 5

d23 = min(d23, d24 d43) = 8

d25 = min(d25, d24 d45) = 9

d26 = min(d26, d24 d46) = 8

d35 = min(d35, d34 d45) = 7

d36 = min(d36, d34 d46) = 1

d56 = min(d56, d54 d46) = 8

La matriz de distancia después de esta iteración es:

5ª Iteración: nodo intermedio = 5

d12 = min(d12, d15 d52) = 3

d13 = min(d13, d15 d53) = 5

d14 = min(d14, d15 d54) = 1

d16 = min(d16, d15 d56) = 5

d23 = min(d23, d25 d53) = 8

d24 = min(d24, d25 d54) = 4

d26 = min(d26, d25 d56) = 8

d34 = min(d34, d35 d54) = 6

d36 = min(d36, d35 d56) = 1

d46 = min(d46, d45 d56) = 4

La matriz de distancia después de esta iteración es:

6ª Iteración: nodo intermedio = 6

d12 = min(d12, d16 d62) = 3

d13 = min(d13, d16 d63) = 5

d14 = min(d14, d16 d64) = 1

d15 = min(d15, d16 d65) = 12

d23 = min(d23, d26 d63) = 8

d24 = min(d24, d26 d64) = 4

d25 = min(d25, d26 d65) = 9

d34 = min(d34, d36 d64) = 5

d35 = min(d35, d36 d65) = 7

d45 = min(d45, d46 d65) = 12

La matriz de distancia después de esta iteración es:

Ya se han hecho todas las iteraciones posibles. Por tanto, el camino mínimo entre 2 vértices cualesquiera del grafo será el obtenido en la matriz final. En este caso, el camino mínimo entre 3 y 4 vale 5.

Análisis

[editar]

Si utilizamos matrices booleanas, para encontrar todos los de desde se necesita hacer operaciones binarias. Debido a que empezamos con y computamos la secuencia de matrices booleanas , , , , el número total de operaciones binarias es de . Por lo tanto, la complejidad del algoritmo es y puede ser resuelto por una máquina determinista de Turing en tiempo polinómico.

Aplicaciones y generalizaciones

[editar]

El algoritmo de Floyd-Warshall puede ser utilizado para resolver los siguientes problemas, entre otros:

  • Camino mínimo en grafos dirigidos (algoritmo de Floyd).
  • Cierre transitivo en grafos dirigidos (algoritmo de Warshall). Es la formulación original del algoritmo de Warshall. El grafo es un grafo no ponderado y representado por una matriz booleana de adyacencia. Entonces la operación de adición es reemplazada por la conjunción lógica(AND) y la operación menor por la disyunción lógica (OR).
  • Encontrar una expresión regular dada por un lenguaje regular aceptado por un autómata finito (algoritmo de Kleene).
  • Inversión de matrices de números reales (algoritmo de Gauss-Jordan).
  • Ruta óptima. En esta aplicación es interesante encontrar el camino del flujo máximo entre 2 vértices. Esto significa que en lugar de tomar los mínimos con el pseudocodigo anterior, se coge el máximo. Los pesos de las aristas representan las limitaciones del flujo. Los pesos de los caminos representan cuellos de botella; por ello, la operación de adición anterior es reemplazada por la operación mínimo.
  • Comprobar si un grafo no dirigido es bipartito.

Implementación del algoritmo de Floyd-Warshall

[editar]

Referencias

[editar]

Véase también

[editar]

Enlaces externos

[editar]