Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Algoritmo de Dijkstra
Tipo
Algoritmo de bsqueda
Estructura de datos
Grafo
Creador
Edsger Dijkstra
Fecha
1959
Clase de complejidad
Tiempo de ejecucin
Peor caso
O(|E|+|V|log|V|)
La idea subyacente en este algoritmo consiste en ir explorando todos los caminos ms cortos
que parten del vrtice origen y que llevan a todos los dems vrtices; cuando se obtiene el
camino ms corto desde el vrtice origen, al resto de vrtices que componen el grafo, el
algoritmo se detiene. El algoritmo es una especializacin de la bsqueda de costo uniforme, y
como tal, no funciona en grafos con aristas de coste negativo (al elegir siempre el nodo con
distancia menor, pueden quedar excluidos de la bsqueda nodos que en prximas iteraciones
bajaran el costo general del camino al pasar por una arista con costo negativo).
ndice
[ocultar]
1 Algoritmo
2 Complejidad
3 Pseudocdigo
5 Vase tambin
6 Enlaces externos
Algoritmo[editar]
Teniendo un grafo dirigido ponderado de N nodos no aislados, sea x el nodo inicial, un vector
D de tamao N guardar al final del algoritmo las distancias desde x al resto de los nodos.
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 sera 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 nodos no marcados vi.
4. Para el nodo actual, calculamos la distancia tentativa desde dicho nodo a sus vecinos
con la siguiente frmula: dt(vi) = Da + d(a,vi). Es decir, la distancia tentativa del nodo
vi es la distancia que actualmente tiene el nodo en el vector D ms la distancia desde
dicho el nodo a (el actual) al nodo vi. Si la distancia tentativa es menor que la
distancia almacenada en el vector, actualizamos el vector con esta distancia tentativa.
Es decir: Si dt(vi) < Dvi Dvi = dt(vi)
5. Marcamos como completo el nodo a.
6. Tomamos como prximo 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.
Complejidad[editar]
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 montculo).
Podemos estimar la complejidad computacional del algoritmo de Dijkstra (en trminos de
sumas y comparaciones). El algoritmo realiza a lo ms n-1 iteraciones, ya que en cada
iteracin se aade un vrtice al conjunto distinguido. Para estimar el nmero total de
operaciones basta estimar el nmero de operaciones que se llevan a cabo en cada iteracin.
Podemos identificar el vrtice con la menor etiqueta entre los que no estn en Sk realizando n1 comparaciones o menos. Despus hacemos una suma y una comparacin para actualizar la
etiqueta de cada uno de los vrtices que no estn en Sk. Por tanto, en cada iteracin se
realizan a lo sumo 2(n-1) operaciones, ya que no puede haber ms de n-1 etiquetas por
actualizar en cada iteracin. Como no se realizan ms de n-1 iteraciones, cada una de las
cuales supone a lo ms 2(n-1) operaciones, llegamos al siguiente teorema.
TEOREMA: El Algoritmo de Dijkstra realiza O(n2) operaciones (sumas y comparaciones) para
determinar la longitud del camino ms corto entre dos vrtices de un grafo ponderado simple,
conexo y no dirigido con n vrtices.
Pseudocdigo[editar]
Estructura de datos auxiliar: Q = Estructura de datos Cola de prioridad (se puede implementar
con un montculo)
DIJKSTRA (Grafo G, nodo_fuente s)
para u V[G] hacer
distancia[u] = INFINITO
padre[u] = NULL
distancia[s] = 0
adicionar (cola, (s, distancia[s]))
mientras que cola no es vaca hacer
u = extraer_mnimo(cola)
para todos 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, distancia[v]))
booleano visto[n]
//vector de boleanos para controlar los vrtices de los que ya tenemos
la distancia mnima
para cada w V[G] hacer
Si (no existe arista entre s y w) entonces
distancia[w] = Infinito //puedes marcar la casilla con un -1 por
ejemplo
Si_no
distancia[w] = peso (s, w)
fin si
fin para
distancia[s] = 0
visto[s] = cierto
//n es el nmero de vrtices que tiene el Grafo
mientras que (no_estn_vistos_todos) hacer
vrtice = coger_el_mnimo_del_vector distancia y que no est visto;
visto[vrtice] = cierto;
para cada w sucesores (G, vrtice) hacer
si distancia[w]>distancia[vrtice]+peso (vrtice, w) entonces
distancia[w] = distancia[vrtice]+peso (vrtice, w)
fin si
fin para
fin mientras
fin funcin.
Al final tenemos en el vector distancia en cada posicin la distancia mnima del vrtice salida a
otro vrtice cualquiera.
Para el problema de la ruta corta tenemos varios algoritmos, en esta oportunidad se explicar el
algoritmo de dijkstra el cual usa una tcnica voraz (greedy). Al final del articulo se encuentran adjuntas
las implementaciones en C++ y JAVA.
Descripcin
El algoritmo de dijkstra determina la ruta ms corta desde un nodo origen hacia los dems nodos para
ello es requerido como entrada un grafo cuyas aristas posean pesos. Algunas consideraciones:
Si los pesos de mis aristas son de valor 1, entonces bastar con usar elalgoritmo de BFS.
Si los pesos de mis aristas son negativos no puedo usar el algoritmo de dijsktra, para pesos
negativos tenemos otro algoritmo llamado Algoritmo de Bellmand-Ford.
Como trabaja
Primero marcamos todos los vrtices como no utilizados. El algoritmo parte de un vrtice origen que
ser ingresado, a partir de ese vrtices evaluaremos sus adyacentes, como dijkstra usa una tcnica
greedy - La tcnica greedy utiliza el principio de que para que un camino sea ptim
Tanto java como C++ cuentan con una cola de prioridad ambos implementan un Binary Heap aunque
con un Fibonacci Heap la complejidad de dijkstra se reduce hacindolo mas eficiente, pero en un
concurso mas vale usar la librera que intentar programar una nueva estructura como un Fibonacci Heap,
claro que particularmente uno puede investigar y programarlo para saber como funciona internamente.
Algoritmo en pseudocdigo
Considerar distancia[ i ] como la distancia mas corta del vrtice origen ingresado al vrtice i.
1 mtodo Dijkstra(Grafo,origen):
2
creamos una cola de prioridad Q
3
agregamos origen a la cola de prioridad Q
4
mientras Q no este vaco:
5
sacamos un elemento de la cola Q llamado u
6
si u ya fue visitado continuo sacando elementos de Q
7
marcamos como visitado u
8
para cada vrtice v adyacente a u en el Grafo:
9
sea w el peso entre vrtices ( u , v )
10
si v no ah sido visitado:
11
Relajacion( u , v , w )
1
2
3
4
Algunas consideraciones para entender el cdigo que se explicara junto con el funcionamiento del
algoritmo.
1
2
3
Donde:
1
2
3
4
5
6
1
2
3
4
5
6
7
8
//funcin de inicializacin
void init(){
for( int i = 0 ; i <= V ; ++i ){
distancia[ i ] = INF; //inicializamos todas las distancias con valor infini
visitado[ i ] = false; //inicializamos todos los vrtices como no visitado
previo[ i ] = -1;
//inicializamos el previo del vrtice i con -1
}
}
De acuerdo al vrtice inicial que elijamos cambiara la distancia inicial, por ejemplo la ruta ms corta
partiendo del vrtice 1 a todos los dems vrtices:
El vrtice 1 es visitado, la distancia de vrtice 1 -> vrtice 1 es 0 por estar en el mismo lugar.
1
2
1
2
3
while( !Q.empty() ){
actual = Q.top().first;
inicial
Q.pop();
Si el tope ya fue visitado entonces no tengo necesidad de evaluarlo, por ello continuara extrayendo
elementos dela cola:
if( visitado[ actual ] ) continue; //Si el vrtice actual ya fue visitado entonces s
cola
En este caso al ser el tope el inicial no esta visitado por lo tanto marcamos como visitado.
1
2
3
4
for( int i = 0 ; i < ady[ actual ].size() ; ++i ){ //reviso sus adyacentes del vertic
adyacente = ady[ actual ][ i ].first;
//id del vertice adyacente
peso = ady[ actual ][ i ].second;
//peso de la arista que une actual con
)
if( !visitado[ adyacente ] ){
//si el vertice adyacente no fue visitado
Vemos que la distancia actual desde el vrtice inicial a 2 es , verifiquemos el paso de relajacin:
->
0+7<
->
7<
1
2
3
4
5
6
7
for( int i = 0 ; i < ady[ actual ].size() ; ++i ){ //reviso sus adyacentes del vertic
adyacente = ady[ actual ][ i ].first;
//id del vertice adyacente
peso = ady[ actual ][ i ].second;
//peso de la arista que une actual con
)
if( !visitado[ adyacente ] ){
//si el vertice adyacente no fue visitado
relajacion( actual , adyacente , peso ); //realizamos el paso de relajacion
}
}
1
2
3
4
5
6
7
8
9
//Paso de relajacion
void relajacion( int actual , int adyacente , int peso ){
//Si la distancia del origen al vertice actual + peso de su arista es menor a la
adyacente
if( distancia[ actual ] + peso < distancia[ adyacente ] ){
distancia[ adyacente ] = distancia[ actual ] + peso; //relajamos el vertice
previo[ adyacente ] = actual;
//a su vez actualizamo
Q.push( Node( adyacente , distancia[ adyacente ] ) ); //agregamos adyacente
}
}
De manera similar al anterior vemos que la distancia actual desde el vrtice inicial a 4 es ,
verifiquemos el paso de relajacin:
->
0+2<
->
2<
En cuanto a la cola de prioridad como tenemos un vrtice con menor peso este nuevo vrtice ira en el
tope de la cola:
Revisamos sus
adyacentes no visitados que serian vrtices 2, 3 y 5.
Empecemos por el vrtice 2:
Ahora vemos que la distancia actual del vrtice inicial al vrtice 2 es 7, verifiquemos el paso de
relajacin:
->
2+3<7
->
5<7
En esta oportunidad hemos encontrado una ruta mas corta partiendo desde el vrtice inicial al vrtice 2,
actualizamos la distancia en el vrtice 2 y actualizamos el vrtice previo al actual quedando:
En cuanto a la cola de prioridad como tenemos un vrtice con menor peso este nuevo vrtice ira en el
tope de la cola, podemos ver que tenemos 2 veces el mismo vrtice pero como usamos una tcnica
greedy siempre usaremos el valor ptimo:
Continuamos
con los Vrtices 3 y 5 como tienen valor si ser posible relajarlos por lo que sera similar a los pasos
iniciales solo que en los pasos iniciales distancia[ 1 ] era 0 en este caso distancia[ 4 ] es 2, quedando:
Hemos
terminado de evaluar al vrtice 4, continuamos con el tope de la cola que es vrtice 2, el cual marcamos
como visitado.
Los adyacentes
no visitados del vrtice 2 son los vrtices 3 y 5. Comencemos con el vrtice 3
Ahora vemos que la distancia actual del vrtice inicial al vrtice 3 es 10, verifiquemos el paso de
relajacin:
->
5 + 1 < 10
->
6 < 10
En esta oportunidad hemos encontrado una ruta mas corta partiendo desde el vrtice inicial al vrtice 3,
dicha ruta sera 1 -> 4 -> 2 -> 3 cuyo peso es 6 que es mucho menor que la ruta 1 > 4 -> 3 cuyo peso es
10, actualizamos la distancia en el vrtice 3 quedando:
El
siguiente
Vemos que la
distancia actual del vrtice inicial al vrtice 5 es 7, verifiquemos el paso de relajacin:
distancia[ 3 ] + 5 < distancia[ 5 ]
->
6+5<7
->
11 < 7
En esta oportunidad se no cumple por lo que no relajamos el vrtice 5, por lo que la tabla en cuanto a
distancias no sufre modificaciones y no agregamos vrtices a la cola:
Ahora tocara el vrtice 2 pero como ya fue visitado seguimos extrayendo elementos de la cola, el
siguiente vrtice ser el 5.
Al ser el ultimo
vrtice a evaluar no posee adyacentes sin visitar por lo tanto hemos terminado el algoritmo. En el grafico
anterior observamos que 2 aristas no fueron usadas para la relajacin, las dems si fueron usadas. La
tabla final quedara de la siguiente manera:
De la tabla si deseo saber la distancia mas corta del vrtice 1 al vrtice 5, solo tengo que acceder al valor
del arreglo en su ndice respectivo (distancia[ 5 ]).
1
2
3
4
5
6
//Impresion del camino mas corto desde el vertice inicial y final ingresados
void print( int destino ){
if( previo[ destino ] != -1 )
//si aun poseo un vertice previo
print( previo[ destino ] ); //recursivamente sigo explorando
printf("%d " , destino );
//terminada la recursion imprimo los vertices re
}