Sei sulla pagina 1di 9

El Algoritmo de Dijkstra

Muchos problemas se pueden representar utilizando grafos en los que se le asigna un peso
a cada una de las aristas. Consideremos a modo de ilustración, la forma en que se representa
el sistema de vuelos de una línea aérea. Construimos el modelo básico representando las
ciudades mediante vértices y los vuelos mediante aristas. Los problemas relacionados con
distancias pueden representarse asignándole a las aristas las distancias entre ciudades. En la
figura 1, se presenta el mismo grafo, con diferentes pesos asignados a sus aristas, que
representan distintas problemáticas a resolver. ¿Cuál es el camino mas corto en distancia
aérea entre Boston y Los Ángeles? ¿Qué combinación de vuelos tiene el menor tiempo total
de vuelo entre Boston y Los Ángeles? ¿Cuál es la tarifa más barata entre esas dos ciudades?,
estos y muchos otros problemas pueden ser modelados con grafos ponderados, el algoritmo
de Dijkstra nos será de utilidad para ciertos problemas que impliquen encontrar la
combinación de aristas con pesos menores para poder trazar una trayectoria de nodo a
nodo.

Distancias

Figura 1. Grafos ponderados que repreentan una línea aérea.


Tiempo de vuelo

Tarifas

Figura 1. Grafos ponderados que representan una línea aérea.

El objetivo de este algoritmo es la determinación, en un grafo ponderado, no dirigido, con


pesos positivos, del camino más corto desde un vertice o nodo que se toma como origen, al
resto de vertices del grafo.

El algoritmo fue desarrollado por el físico e informatico Edsger Wybe Dijkstra en 1959 y es
un ejemplo más de algoritmo voraz.

El ejemplo típico de aplicación se presenta cuando estamos, por ejemplo, ante un mapa de
carreteras y se trata de encontrar el camino mas corto entre dos localidades cualesquiera,
midiendo siempre el recorrido siguiendo el criterio de la distancia más corta entre las
localidades, aunque, también se puede representar problemas distinto, como el costo de
cada viaje, o el tiempo de viaje, asignando los valores correspondientes a costo y tiempo, en
las aristas.

Algunas consideraciones que debemos tomar al inicio:

 Tomaremos como nodo de partida “a”.


 Para recordar las operaciones que se efectúan en cada paso para acceder a un
determinado nodo así como los lados recorridos para ir, anotaremos, al lado de ese
nodo, el peso total del camino recorrido hasta llegar a el, y la designación del nodo
anterior.
 La distancia de un nodo a si mismo será “0”.
 Con la etiqueta L(i) se indicará la longitud (peso) desde el nodo inicial hasta el nodo i.
 El algoritmo se basa en una actualización en cada paso de etiquetas L(i) de los nodos
pasando de temporal a permanente según la regla siguiente:

𝐿(𝑖) = min{(𝑇𝑒𝑚𝑝. 𝑎𝑛𝑡𝑒𝑟𝑖𝑜𝑟), (𝑢𝑙𝑡𝑖𝑚𝑎 𝑝𝑒𝑟𝑚. ) + (𝑑 𝑎𝑙 𝑛𝑜𝑑𝑜 𝑖 𝑑𝑒𝑠𝑑𝑒 𝑒𝑙 𝑝𝑒𝑟𝑚. 𝑎𝑛𝑡𝑒𝑟𝑖𝑜𝑟}

En el inicio de la ejecución del algoritmo, a cada vértice u de G se le da una etiqueta L(i) que
indica la mejor estimación actual de la longitud de la ruta mas corta de “a” a “i” L(a)=0 ya
que el camino mas corto de “a” a “a” tiene longitud 0, pero debido a que no existe ninguna
información previa acerca de las longitudes de las trayectorias mas cortas de “a” a cualquier
otro vértice de G, la etiqueta L(i) de cada vértice i diferente de “a” se hace inicialmente igual
al número, que se denota por ∞, que es mayor que la suma de los pesos de todas las aristas
de G. Como la ejecución de los progresos de algoritmo, se cambian los valores de L(i),
convirtiéndose en las longitudes reales de las trayectorias más cortas, de “a” a “i” en G.

Porque t se construye hacia el exterior desde “a” en cada etapa de ejecución del algoritmo,
los únicos vértices que son candidatos a formar parte de t son los que están junto al menos
un vértice de t. así en cada etapa del algoritmo de Dijkstra el grafo G puede pensarse como
dividido en 3 partes:
El árbol t que se está construyendo en el conjunto de vértices marginales que son adyacentes
al menos a un vértice del árbol y el resto de los vértices de G. cada franja de vértices es un
candidato hacia el siguiente vértice, agregado t. el elegido es aquel para que la longitud de
la trayectoria más corta de “a” a t es un mínimo entre todos los vértices de la franja.

Una observación fundamental subyacente al algoritmo de Dijkstra es que después de cada


adición de un vértice v a t, solo los vértices de la franja para una trayectoria más corta de “a”
se pueda encontrar, son aquellas que son adyacentes a “v”.

10 b
a
5
9
3 c
f
8
5 4
15

e 2 d

Asignamos distancia 0 para ir de “a” a “a”, es decir, L(a)=0 y L(i)=∞ para el resto de los nodos. La
distancia al nodo “a” de partida llevará la, etiqueta p indicando así que tal distancia ya será
permanente durante las primeras etapas del algoritmo. Las distancias al resto de nodos serán ∞ y
temporales t.

Llevando esto al formalismo matemático, el algoritmo de Djikstra nos queda como:

𝑆𝑒𝑎 𝐺 𝑢𝑛 𝑔𝑟𝑎𝑓𝑜 𝑐𝑜𝑛 𝑐𝑜𝑛𝑗𝑢𝑛𝑡𝑜 𝑑𝑒 𝑣é𝑟𝑡𝑖𝑐𝑒𝑠 𝑉(𝐺) = {1, 2, … , 𝑛} 𝑦 𝑠𝑒𝑎 𝑐: 𝐴(𝐺) → ℝ

𝑡𝑎𝑙 𝑞𝑢𝑒 𝑐(𝑎) ≥ 0 𝑝𝑟𝑎 𝑡𝑜𝑑𝑎 𝒂 ∈ 𝐴(𝐺). 𝑆𝑖 𝑎 = (𝑖, 𝑗)

El numero c(a) puede interpretarse como el costo de ir directamente de i a j. El problema de la ruta


más corta consiste en hallar una trayectoria dirigida P del vértice 1 al vértice n tal que:

𝑐(𝑃) = ∑ 𝑐(𝑎)
𝑎∈𝐴(𝑃)
Sea mínimo.

Si ( 𝑖 , 𝑗 ) ∈ 𝐴(𝐺), extenderemos la función al conjunto 𝑉 × 𝑉, definiendo 𝑐𝑖𝑖 = 0, para todo


𝑖 = 1,2, … , 𝑛 y definiendo 𝑐𝑖𝑘 = ∞ si ( 𝑖 , 𝑘 ) ∉ 𝐴(𝐺), 𝑖 ≠ 𝑘.

Pseudocódigo. Dijkstra

Entrada: G [un grafo conexo simple con un peso positivo para cada arista], ∞ [un número mayor que
la suma de los pesos de todas las aristas del grafo], 𝑤(𝑢, 𝑣) [el peso de la arista {𝑢, 𝑣}], 𝑎 [El vértice
inicial], 𝑧 [el vértice final].

1.- Inicializa G como el grafo con el vértice a y sin aristas. Sea V(G) el conjunto de los vértices de G y
sea A(G) el conjunto de aristas de G.

2.- Sea L(a)=0 y para todos los vértices en G excepto a, sea L(u) = ∞ . [el número L(x) se llama etiqueta
de x].

3.- Inicialice v a igual a y F será {a}. [El símbolo v se utiliza para denotar el vértice agregado a G].

4.- while (𝑧 ∉ 𝑉(𝐺))

4a.- 𝐹 ≔ (𝐹 − {𝑣}) ∪ {𝑣𝑒𝑟𝑡𝑖𝑐𝑒𝑠 𝑞𝑢𝑒 𝑠𝑜𝑛 𝑎𝑑𝑦𝑎𝑐𝑒𝑛𝑡𝑒𝑠 𝑎 𝑣 𝑦 𝑛𝑜 𝑒𝑠𝑡á𝑛 𝑒𝑛 𝑉(𝐺)}

[El conjunto F se llama la franja. Cada vez que se agrega un vértice a G, se elimina de la franja
y se agregan los vértices adyacentes a la franja si ya no están en la franja o en el árbol G].

4b.- Para cada vértice u que es adyacente a v y no está en V(G).

𝑖𝑓 𝐿(𝑢) + 𝑤(𝑣, 𝑢) < 𝐿(𝑢) 𝑡ℎ𝑒𝑛

𝐿(𝑢) ≔ 𝐿(𝑣) + 𝑤(𝑣, 𝑢)

𝐷(𝑢) ≔ 𝑣

[Observe que agregar v a G no afecta las etiquetas de los vértices en la franja


F excepto aquellas adyacentes a v. También, cuando L(u) se cambia a un valor
menor, se introduce la notación D(u) para realizar un seguimiento de que
vértice en G dio lugar al valor menor]
4c.- Encuentre un vértice x en F con la etiqueta más pequeña

Agregue el vértice x a V(G) y se agrega una arista {D(x),x} a A(G)

𝑣 ≔ 𝑥 [Este enunciado establece la notación para la siguiente iteración del bucle].

end while

Salida: 𝐿(𝑧) [𝐿(𝑧) es un numero entero no negativo, es la longitud de la trayectoria más corta de a a
z].

Para ilustrar mejor el mecanismo de acción del algoritmo, veremos un ejemplo por pasos
para encontrar la ruta mas corta de a a z.

Solución

Paso 1: Va al bucle 𝑤ℎ𝑖𝑙𝑒 𝑉(𝐺) = {𝑎}, 𝐴(𝐺) = ∅ 𝑦 𝐹 = {𝑎}

Durante la iteración:

𝐹 = {𝑏, 𝑐}, 𝐿(𝑏) = 3, 𝐿(𝑐) = 4

Ya que 𝐿(𝑏) < 𝐿(𝑐), 𝑏 𝑠𝑒 𝑎𝑔𝑟𝑒𝑔𝑎 𝑎

𝑉(𝐺)𝑦 (𝑎, 𝑏) 𝑠𝑒 𝑎𝑔𝑟𝑒𝑔𝑎 𝑎 𝐴(𝐺)


Paso 2: Va al bucle 𝑤ℎ𝑖𝑙𝑒: 𝑉(𝐺) = {𝑎, 𝑏}, 𝐴(𝐺) = {(𝑎, 𝑏)}

Durante la iteración:

𝐹 = {𝑐, 𝑑, 𝑒}, 𝐿(𝑐) = 4, 𝐿(𝑑) = 9, 𝐿(𝑒) = 8

𝑃𝑢𝑒𝑠𝑡𝑜 𝑞𝑢𝑒 𝐿(𝑐) < 𝐿(𝑑) 𝑦 𝐿(𝑐) < 𝐿(𝑒), 𝑐

𝑠𝑒 𝑎𝑔𝑟𝑒𝑔𝑎 𝑎 𝑉(𝐺)𝑦 (𝑎, 𝑐) 𝑠𝑒 𝑎𝑔𝑟𝑒𝑔𝑎 𝑎 𝐴(𝐺)

Paso 3: Va al bucle 𝑤ℎ𝑖𝑙𝑒: 𝑉(𝐺) = {𝑎, 𝑏, 𝑐}, 𝐴(𝐺) = {(𝑎, 𝑏), (𝑎, 𝑐)}

Durante la iteración:
𝐹 = {𝑑, 𝑒}, 𝐿(𝑑) = 9, 𝐿(𝑒) = 5, 𝐿(𝑒) = 5
𝐿(𝑒)𝑠𝑒 ℎ𝑎𝑐𝑒 5 𝑝𝑜𝑟𝑞𝑢𝑒 𝑎 − 𝑐 − 𝑒, 𝑞𝑢𝑒 𝑡𝑖𝑒𝑛𝑒
𝑙𝑜𝑛𝑔𝑖𝑡𝑢𝑑 5, 𝑒𝑠 𝑢𝑛𝑎 𝑡𝑟𝑎𝑦𝑒𝑐𝑡𝑜𝑟𝑖𝑎 𝑚á𝑠 𝑐𝑜𝑟𝑡𝑎
𝑎 𝑒 𝑞𝑢𝑒 𝑎 − 𝑏 − 𝑒, 𝑞𝑢𝑒 𝑡𝑖𝑒𝑛𝑒 𝑙𝑜𝑛𝑔𝑖𝑡𝑢𝑑 8.
𝑌𝑎 𝑞𝑢𝑒 𝐿(𝑒) < 𝐿(𝑑), 𝑒 𝑠𝑒 𝑎𝑔𝑟𝑒𝑔𝑎 𝑎 𝑉(𝐺) 𝑦
(𝑐, 𝑒)𝑠𝑒 𝑎𝑔𝑟𝑒𝑔𝑎 𝑎 𝐴(𝐺).

Paso 4: Va al bucle 𝑤ℎ𝑖𝑙𝑒: 𝑉(𝐺) = {𝑎, 𝑏, 𝑐, 𝑒}, 𝐴(𝐺) = {(𝑎, 𝑏), (𝑎, 𝑐), (𝑐, 𝑒)}

Durante la iteración:
𝐹 = {𝑑, 𝑧}, 𝐿(𝑑) = 7, 𝐿(𝑧) = 17
𝐿(𝑑)𝑠𝑒 ℎ𝑎𝑐𝑒 7 𝑝𝑜𝑟𝑞𝑢𝑒 𝑎 − 𝑐 − 𝑒 − 𝑑, 𝑞𝑢𝑒
𝑡𝑖𝑒𝑛𝑒 𝑙𝑜𝑛𝑔𝑖𝑡𝑢𝑑 7, 𝑒𝑠 𝑢𝑛𝑎 𝑡𝑟𝑎𝑦𝑒𝑐𝑡𝑜𝑟𝑖𝑎 𝑚á𝑠
𝑐𝑜𝑟𝑡𝑎 𝑎 𝑑 𝑞𝑢𝑒 𝑎 − 𝑏 − 𝑑, 𝑞𝑢𝑒 𝑡𝑖𝑒𝑛𝑒 𝑙𝑜𝑔𝑖𝑡𝑢𝑑
9. 𝑌𝑎 𝑞𝑢𝑒 𝐿(𝑑) < 𝐿(𝑧), 𝑑 𝑠𝑒 𝑎𝑔𝑟𝑒𝑔𝑎 𝑎 𝑉(𝐺)
𝑦 (𝑒, 𝑑) 𝑠𝑒 𝑎𝑔𝑟𝑒𝑔𝑎 𝑎 𝐴(𝐺).
Paso 5: Va al bucle 𝑤ℎ𝑖𝑙𝑒: 𝑉(𝐺) = {𝑎, 𝑏, 𝑐, 𝑒, 𝑑}, 𝐴(𝐺) = {(𝑎, 𝑏), (𝑎, 𝑐), (𝑐, 𝑒), (𝑒, 𝑑)}

Durante la iteración: 𝐹 = {𝑧}, 𝐿(𝑧) = 14


𝐿(𝑧) 𝑠𝑒 𝑐𝑜𝑛𝑣𝑖𝑒𝑟𝑡𝑒 𝑒𝑛 14 𝑝𝑜𝑟𝑞𝑢𝑒 𝑎, 𝑐, 𝑒, 𝑑, 𝑧,
𝑞𝑢𝑒 𝑡𝑖𝑒𝑛𝑒 𝑙𝑜𝑛𝑔𝑖𝑡𝑢𝑑 14, 𝑒𝑠 𝑢𝑛𝑎 𝑡𝑟𝑎𝑦𝑒𝑐𝑡𝑜𝑟𝑖𝑎
𝑚á𝑠 𝑐𝑜𝑟𝑡𝑎 𝑎 𝑧 𝑞𝑢𝑒 𝑎, 𝑏, 𝑑, 𝑧, 𝑞𝑢𝑒 𝑡𝑖𝑒𝑛𝑒 17.
𝑝𝑢𝑒𝑠𝑡𝑜 𝑞𝑢𝑒 𝑧 𝑒𝑠 𝑒𝑙 𝑢𝑛𝑖𝑐𝑜 𝑣é𝑟𝑡𝑖𝑐𝑒 𝑒𝑛 𝐹, 𝑠𝑢
𝑒𝑡𝑖𝑞𝑢𝑒𝑡𝑎 𝑒𝑠 𝑢𝑛 𝑚í𝑛𝑖𝑚𝑜 𝑦 𝑝𝑜𝑟 𝑙𝑜 𝑞𝑢𝑒 𝑧 𝑠𝑒
𝑎𝑔𝑟𝑒𝑔𝑎 𝑎 𝑉(𝐺)𝑦 (𝑒, 𝑧)𝑠𝑒 𝑎𝑔𝑟𝑒𝑔𝑎 𝑎 𝐴(𝐺).

La ejecución del algoritmo termina en este momento porque 𝑧 ∈ 𝑉(𝐺) . La trayectoria más
corta de 𝑎 a 𝑧 tiene longitud 𝐿(𝑧) = 14.

Siguiendo los pasos en una tabla es una forma conveniente de mostrar la acción del
algoritmo de Dijkstra. La tabla 1 hace esto para el grafo del ejemplo.

Paso 𝑉(𝐺) 𝐴(𝐺) 𝐹 𝐿(𝑎) 𝐿(𝑏) 𝐿(𝑐) 𝐿(𝑑) 𝐿(𝑒) 𝐿(𝑧)


0 {𝑎} ∅ {𝑎} 0 ∞ ∞ ∞ ∞ ∞
1 {𝑎} ∅ {𝑏, 𝑐} 0 3 4 ∞ ∞ ∞
2 {𝑎, 𝑏} {(𝑎, 𝑏)} {𝑐, 𝑑, 𝑒} 0 3 4 9 8 ∞
3 {𝑎, 𝑏, 𝑐} {(𝑎, 𝑏), (𝑎, 𝑐)} {𝑑, 𝑒} 0 3 4 9 5 ∞
4 {𝑎, 𝑏, 𝑐, 𝑒} {(𝑎, 𝑏), (𝑎, 𝑐), (𝑐, 𝑒)} {𝑑, 𝑧} 0 3 4 7 5 17
5 {𝑎, 𝑏, 𝑐, 𝑒, 𝑑} {(𝑎, 𝑏), (𝑎, 𝑐), (𝑐, 𝑒), (𝑒, 𝑑)} {𝑧} 0 3 4 7 5 14
6 {𝑎, 𝑏, 𝑐, 𝑒, 𝑑, 𝑧} {(𝑎, 𝑏), (𝑎, 𝑐), (𝑐, 𝑒), (𝑒, 𝑑), (𝑒, 𝑧)}

El algoritmo de Dijkstra determina la longitud del camino más corto entre dos vértices de
un grafo ponderado simple, conexo y no dirigido.
Podemos estimar la complejidad computacional del algoritmo de Dijkstra (en términos de
sumas y comparaciones). El algoritmo realiza a lo más n-1 iteraciones, ya que en cada
iteración se añade un vértice al conjunto distinguido. Para estimar el número total de
operaciones basta estimar el número de operaciones que se llevan a cabo en cada iteración.
Podemos identificar el vertice con la menor etiqueta entre los que no estan en el conjunto
realizando n-1 comparaciones o menos. Después hacemos una suma y una comparación
para actualizar la etiqueta de cada uno de los vértices. Por lo tanto en cada iteración se
realizan a lo sumo 2(n-1) operaciones, ya que no puede haber más de n-1 etiquetas por
actualizar en cada iteración. Como no se realizan más de n-1 iteraciones cada una de las
cuales supone a lo más 2(n-1) operaciones, llegamos al siguiente teorema:

Teorema. El algoritmo de Dijkstra realiza 𝑂(𝑛2 ) operaciones (sumas y


comparaciones) para determinar la longitud del camino más corto entre dos
vértices de un grafo ponderado simple, conexo y no dirigido con n vértices.

Potrebbero piacerti anche