You are on page 1of 5

Algoritmo de Johnson

El algoritmo de Johnson es una forma de encontrar el camino ms corto entre todos los
pares de vrtices de un grafo dirigido disperso. Permite que las aristas tengan pesos
negativos, si bien no permite ciclos de peso negativo. Funciona utilizando el algoritmo de
Bellman-Ford para hacer una transformacin en el grafo inicial que elimina todas las aristas de
peso negativo, permitiendo por tanto usar el algoritmo de Dijkstra en el grafo transformado. Su
nombre viene de Donald B. Johnson, quien fuera el primero en publicar la tcnica en 1977.

Descripcin del algoritmo[editar]


El algoritmo de Johnson consiste en los siguientes pasos:
1. Primero se aade un nuevo nodo q al grafo, conectado a cada uno de los nodos del
grafo por una arista de peso cero.
2. En segundo lugar, se utiliza el algoritmo de Bellman-Ford, empezando por el nuevo
vrtice q, para determinara para cada vrtice v el peso mnimo h(v) del camino
de q a v. Si en este paso se detecta un ciclo negativo, el algoritmo concluye.
3. Seguidamente, a las aristas del grafo original se les cambia el peso usando los valores
calculados por el algoritmo de Bellman-Ford: una arista de u a v con tamao w(u, v),
da el nuevo tamao w(u, v) + h(u) h(v)
4. Por ltimo, para cada nodo s se usa el algoritmo de Dijkstra para determinar el camino
ms corto entre s y los otros nodos, usando el grafo con pesos modificados.
En el grafo con pesos modificados, todos los caminos entre un par de nodos s y t tienen la
misma cantidad h(s) h(t) aadida a cada uno de ellos, as que un camino que sea el ms
corto en el grafo original tambin es el camino ms corto en el grafo modificado y viceversa.
Sin embargo, debido al modo en el que los valores h(v) son computados, todos los pesos
modificados de las aristas son no negativos, asegurando entonces la optimalidad de los
caminos encontrados por el algoritmo de Dijkstra. Las distancias en el grafo original pueden
ser calculadas a partir de las distancias calculadas por el algoritmo de Dijkstra en el grafo
modificado invirtiendo la transformacin realizada en el grafo.

Anlisis de la complejidad[editar]
La complejidad temporal de este algoritmo, usando montculos de Fibonacci en la
implementacin del algoritmo de Dijkstra, es de O(V2log V + VE): el algoritmo usa un tiempo
de O(VE) para la fase Bellman-Ford del algoritmo, y O(V log V + E) para cada una de
las V instancias realizadas del algoritmo de Dijkstra. Entonces, cuando el grafo es disperso el
tiempo total del algoritmo puede ser menor que el algoritmo de Floyd-Warshall, que resuelve el
mismo problema en un tiempo de O(V3).

Ejemplo[editar]
Las etapas del algoritmo de Johnson estn descritas en la siguiente
ilustracin:

En la imagen de la izquierda tiene dos aristas negativas y no contiene ciclos negativos. En la


segunda imagen se muestra el nuevo vrtice q con peso 0 hacia todos los nodos. En la
tercera imagen, se muestra el rbol de caminos mnimos calculado con el algoritmo de
Bellman-Ford con q como vrtice inicial, y los valores h(v) calculados para cada otro nodo
como la longitud del camino ms corto entre q y ese nodo. A modo de ilustracin, en dicha
imagen solo aparecen los caminos que se tomaran para determinar cada valor h(v). Ntese
que todos estos valores h(v) no son positivos, porque q tiene una arista de unin con cada
nodo de peso 0, y por tanto el camino ms corto no puede ser mayor que ese valor. En la
parte derecha se muestra el grafo modificado, hecho reemplazando el peso de cada
arista w(u, v) por w(u, v) + h(u) h(v). En este grafo modificado, todos los pesos de las aristas
son positivos y el camino ms corto entre dos nodos cualesquiera usa la misma secuencia de
aristas que en el camino ms corto entre los mismos dos nodos en el grafo original. El
algoritmo concluye aplicando el algoritmo de Dijkstra para cada uno de los cuatro nodos
originales en el grafo modificado (cuarta imagen).

Implementacin[editar]
La estructura de datos para almacenar el grafo consiste en una representacin de cada vrtice
como una lista de las aristas que parten del mismo. Estas aristas constan de un origen, un
destino y un peso. El conjunto de vrtices se representa como un array de vrtices y un ndice
que nos indica el ltimo vrtice empleado del array.
La implementacin del algoritmo devuelve una matriz de elementos precedentes y otra de
distancias, mediante la primera se puede seguir el camino de menor coste desde un nodo
dado a cualquier otro nodo del grafo, y si paralelamente vamos sumando las distancias de la
otra matriz, obtenemos los costes totales de dichos caminos mnimos.

Pseudocdigo[editar]

Tipos
Arista = REGISTRO
o : NATURAL
d : NATURAL
peso : INT
sig : NATURAL
FIN
LAristas = PUNTERO A Arista
TGrafo = ARRAY [1..N] DE LAristas
THv = ARRAY [1..N] DE ENTERO
TVector = ARRAY [1..N] DE ENTERO
TMatriz = ARRAY [1..N] DE TVector
//suponemos ig>1
PROC Johnson (grafo: TGrafo; ig: NATURAL; distancias: TMatriz ;
previos: TMatriz)
VARIABLES
i : NATURAL
p : LAristas
min_caminos : THv
aux_dist, aux_prev : TVector
INICIO
grafo[ig] nueva_arista(ig,1,0,NULO)
inc(ig)
p grafo[ig]
PARA i 2 HASTA ig-2 HACER
p^.sig nueva_arista(ig,i,0,NULO)
p p^.sig
FIN
BellmanFord(grafo,ig, min_caminos)
PARA i 1 HASTA ig-1 HACER
p grafo[i]
MIENTRAS (p != NULO) HACER
p^.peso p^.peso + min_caminos[p^.o] - min_caminos[p^.d]
p p^.sig
FIN
FIN
PARA i 1 HASTA ig-2 HACER

Dijkstra(grafo,i, aux_dist,aux_prev)

// devuelve los

caminos mnimos desde el ltimo nodo


// a todos los dems
previos[i] aux_prev;
CalcularDistancias(grafo, previos, aux_dist,distancias); //
este algoritmo realiza la transformacin inversa a la
//
que habamos hecho antes sobre los pesos, para obtener
//
las distancias reales
FIN
FIN

C++[editar]

#include <cstdlib>
using namespace std;
struct Arista{
unsigned o;
unsigned d;
int peso;
LAristas sig;
};
typedef LAristas Arista*;
typedef TGrafo LArista[N];
typedef TVector int[N];
typedef TMatriz TVector[N];
//suponemos ig>1
void johnson(const TGrafo &grafo, int ig, TMatriz &distancias, TMatriz
&previos){
Arista* p;
TVector min_caminos;
TVector aux_dist;

TVector aux_prev;
grafo[ig] = nueva_arista(ig,1,0,NULL);
ig++;
p = grafo[ig];
for(unsigned i=2; i<=ig-2; i++){
p->sig = nueva_arista(ig,i,0,NULL);
p = p->sig;
}
BellmanFord(grafo,ig, min_caminos);
for(unsigned i=1; i<=ig-1; i++){
p = grafo[i];
while(p != NULL){
p->peso = p->peso + min_caminos[p->o] - min_caminos[p->d];
p = p->sig;
}
}
for(unsigned i=1; i<=ig-2; i++){
Dijkstra(grafo,i-1, aux_dist,aux_prev) // devuelve los
caminos mnimos desde el ltimo nodo
// a todos los dems.
previos[i-1] = aux_prev;
CalcularDistancias(grafo, previos, aux_dist,distancias); //
este algoritmo realiza la transformacin inversa a la
//
que habamos hecho antes sobre los pesos, para obtener
//
las distancias reales
}
}

You might also like