El algoritmo de Johnson es una forma de encontrar los caminos más cortos entre todos los pares de vértices en un grafo dirigido ponderado por aristas . Permite que algunos de los pesos de las aristas sean números negativos , pero no pueden existir ciclos de peso negativo . Funciona utilizando el algoritmo Bellman-Ford para calcular una transformación del grafo de entrada que elimina todos los pesos negativos, lo que permite utilizar el algoritmo de Dijkstra en el grafo transformado. [1] [2] Lleva el nombre de Donald B. Johnson , quien publicó la técnica por primera vez en 1977. [3]
Una técnica de reponderación similar también se utiliza en el algoritmo de Suurballe para encontrar dos caminos disjuntos de longitud total mínima entre los mismos dos vértices en un gráfico con pesos de arista no negativos. [4]
El algoritmo de Johnson consta de los siguientes pasos: [1] [2]
Las primeras tres etapas del algoritmo de Johnson se representan en la siguiente ilustración.
El gráfico de la izquierda de la ilustración tiene dos aristas negativas, pero ningún ciclo negativo. El gráfico del centro muestra el nuevo vértice q , un árbol de ruta más corta calculado por el algoritmo Bellman-Ford con q como vértice inicial, y los valores h ( v ) calculados en cada nodo como la longitud de la ruta más corta desde q hasta ese nodo. Nótese que estos valores no son todos positivos, porque q tiene una arista de longitud cero hacia cada vértice y la ruta más corta no puede ser más larga que esa arista. A la derecha se muestra el gráfico reponderado, formado al reemplazar cada peso de arista por w ( u , v ) + h ( u ) − h ( v ) . En este gráfico reponderado, todos los pesos de las aristas no son negativos, pero la ruta más corta entre dos nodos cualesquiera usa la misma secuencia de aristas que la ruta más corta entre los mismos dos nodos en el gráfico original. El algoritmo concluye aplicando el algoritmo de Dijkstra a cada uno de los cuatro nodos iniciales en el gráfico reponderado.
En el grafo reponderado, a todos los caminos entre un par s y t de nodos se les suma la misma cantidad h ( s ) − h ( t ) . La afirmación anterior se puede demostrar de la siguiente manera: Sea p un camino . Su peso W en el grafo reponderado viene dado por la siguiente expresión :
Cada se cancela en la expresión entre corchetes anterior; por lo tanto, nos queda la siguiente expresión para W :
La expresión entre corchetes es el peso de p en la ponderación original.
Dado que la reponderación añade la misma cantidad al peso de cada ruta , una ruta es una ruta más corta en la ponderación original si y solo si es una ruta más corta después de la reponderación. El peso de las aristas que pertenecen a una ruta más corta desde q a cualquier nodo es cero y, por lo tanto, las longitudes de las rutas más cortas desde q a cada nodo se vuelven cero en el grafo reponderado; sin embargo, siguen siendo rutas más cortas. Por lo tanto, no puede haber aristas negativas: si la arista uv tuviera un peso negativo después de la reponderación, entonces la ruta de longitud cero desde q a u junto con esta arista formaría una ruta de longitud negativa desde q a v , contradiciendo el hecho de que todos los vértices tienen distancia cero desde q . La no existencia de aristas negativas asegura la optimalidad de las rutas encontradas por el algoritmo de Dijkstra. Las distancias en el grafo original se pueden calcular a partir de las distancias calculadas por el algoritmo de Dijkstra en el grafo reponderado invirtiendo la transformación de reponderación. [1]
La complejidad temporal de este algoritmo, que utiliza montones de Fibonacci en la implementación del algoritmo de Dijkstra, es : el algoritmo utiliza tiempo para la etapa Bellman–Ford del algoritmo y para cada una de las instancias del algoritmo de Dijkstra. Por lo tanto, cuando el grafo es escaso , el tiempo total puede ser más rápido que el algoritmo Floyd–Warshall , que resuelve el mismo problema en tiempo . [1]