Algoritmo de Dijkstra en pdf

11 – Matemáticas I : Teorı́a de Grafos
1.3
1.3 Caminos de peso mı́nimo. Algoritmo de Dijkstra
Caminos de peso mı́nimo. Algoritmo de Dijkstra
Cuando un grafo modela una situación, en general, las conexiones (aristas y arcos) tienen además unas
caracterı́sticas propias. Pensemos en el ejemplo de una red viaria o una red eléctrica, no sólo se trata de
dar el servicio (conexión), sino que también es interesante conocer las longitudes de las lı́neas a establecer
(cantidad de material), las dificultades para su trazado (coste en tiempo o monetario), etc.
En el grafo, estos valores se reflejan asignando a cada arista un valor que represente la magnitud que
queremos considerar (un peso a cada arista).
Consideremos el grafo de la derecha, donde a cada arista le hemos asignado una distancia.
Si buscamos ahora el camino “más corto” entre el vértice v1
v2
v4
v6
7
2
s
s
s
y v3 no consideraremos que es el que tiene menos aristas,
@
@
sino aquel que recorre en total menor distancia: C1 = v1 v3
@8
@1
3
v
3
5
s
4
1
@
@
recorre sólo una arista pero una distancia de 9, C2 = v1 v2 v3
@
@
@
7
9
tiene dos aristas y una distancia total de 6 y C3 = v1 v2 v5 v3
@
@
9@@
@s
@s
s
tiene tres aristas sin embargo la distancia recorrida es sólo
v3
1
v5
v7
de 5. Claramente, este último camino es más corto.
Definición 19.- Llamaremos peso de un grafo (o digrafo) G = (V, A) a una función real positiva sobre
el conjunto de aristas del grafo, es decir, a una función ω: A → IR+ .
El peso de cada arista lo denotaremos por ω({vi , vj }) = ωij y llamaremos peso de una trayectoria
a la suma de los pesos de las aristas que la componen.
En un grafo pesado, se llama matriz de pesos del grafo a la matriz Ω = (ωij )n×n , donde pondremos
ωij = ∞ si no hay arista desde el vértice vi al vértice vj y ωii = 0, ceros en la diagonal. (En ocasiones
puede resultar útil poner también en la diagonal el valor ∞ y, de hecho, algunos autores ası́ lo hacen.)
Nota: Con la introducción del peso, la búsqueda del camino más corto y la “matriz de alcance” vistas en
la sección anterior son un caso particular: cuando el peso de cada arista es 1.
Unifiquemos criterios y notación:
Definición 20.- Llamaremos peso mı́nimo de vi a vj al mı́nimo de los pesos de los caminos de vi a vj ,
∗ , y camino (de peso) mı́nimo de v a v a cualquier camino C
lo denotaremos por ωij
i
j
ij entre ellos que
∗ ).
tenga peso mı́nimo (es decir, ω(Cij ) = ωij
∗)
∗
La matriz de pesos mı́nimos es la matriz Ω∗ = (ωij
n×n con ωij = ∞ si no hay camino de vi a vj .
El siguiente resultado, conocido como el Principio de minimalidad de Bellman, y que básicamente
dice “cualquier camino contenido en un camino mı́nimo es también mı́nimo”, garantiza que los caminos
obtenidos –respetando este principio– son caminos mı́nimos.
Proposición 21.- Sea G un grafo pesado. Si x1 x2 · · · xp−1 xp es un camino mı́nimo, para cada i con
∗ = w∗ + w∗ .
1 < i < p, los caminos x1 x2 · · · xi y xi · · · xp−1 xp son caminos mı́nimos y w1p
1i
ip
Demostración:
Es claro el resultado, pues si el camino C1i = x1 x2 · · · xi−1 xi no es un camino mı́nimo, existe otro
0 = x x0 · · · x0
0
0
0
C1i
1 2
i−1 xi , con w(C1i ) < w(C1i ), con menor peso. Luego el camino x1 x2 · · · xi−1 xi · · · xp ,
0
de sustituir C1i por C1i , tiene menor peso que x1 x2 · · · xi−1 xi · · · xp , en contra de la hipótesis.
1.3.1
Algoritmo de Dijkstra
Uno de los algoritmos más usados para la búsqueda de caminos de peso mı́nimo es el de Dijkstra, que
proporciona los pesos mı́nimos desde un vértice dado al resto de los vértices.
Este algoritmo aplica el principio de Bellman de manera constructiva (casi al revés que lo que dice
la tesis de Bellman) formando un camino mı́nimo a partir de otro ya existente. El algoritmo devuelve
en realidad el peso mı́nimo no el camino mı́nimo propiamente dicho, pero permite obtener fácilmente el
camino mı́nimo recorriendo en sentido inverso la construcción (de ahı́ su popularidad).
Prof: José Antonio Abia Vian
I.T.I. en Electricidad
12 – Matemáticas I : Teorı́a de Grafos
1.3 Caminos de peso mı́nimo. Algoritmo de Dijkstra
Sea un grafo o digrafo pesado, con V = {v1 , v2 , . . . , vn } su conjunto de vértices y Ω = (ωij )n×n su
matriz de pesos, y sea vp el vertice inicial.
Dijkstra construye, en cada paso, un camino mı́nimo desde vp a otro vértice y se detiene cuando ha
construido uno para cada vértice (o no puede construir más). Para ello se usan una lista o conjunto: L,
que contendrá los vértices para los que ya hemos construimos un camino mı́nimo y un vector de pesos:
D , que contendrá al final los pesos mı́nimos. Inicialmente L = {vp } y D = Ω(p, : ), la p-ésima fila de la
matriz de pesos (la correspondiente al vértice inicial).
Algoritmo 4.- (de Dijkstra)
inicio: Ω; vp ; L = {vp }; D = Ω(p, : )
mientras sea V − L 6= ∅
tomar vk ∈ V − L con D(k) mı́nimo
hacer L = L ∪ {vk }
para cada vj de V-L
si D(j) > D(k) + ωkj
hacer D(j) = D(k) + ωkj
fin
fin
fin
El vector D final contiene los pesos mı́nimos desde el vértice inicial a los demás vértices –si alguno de los
pesos finales es ∞, no hay camino desde el vértice inicial–.
Observaciones 22.? Si varias posiciones de D coinciden en el valor del mı́nimo puede elegirse
cualquiera de ellos (ese valor ya no podrá minorarse, con lo que en los pasos siguientes tendrán que
irse elegiendo los otros).
? Cuando al comparar los valores para una posible minoración tengan ambos el mismo valor, querrá
decir que ambos caminos son igualmente pesados, por lo que podrá haber varios caminos mı́nimos
para el mismo vértice (ver el ejemplo siguiente).
? Como ya comentamos, aunque el algoritmo de Dijkstra obtiene pesos mı́nimos, puede obtenerse
fácilmente a partir del resultado un camino mı́nimo. Para ello sólo hay que tener en cuenta cuando
se produjo la última mejora del valor de D en cada vértice y con que vértice se mejoró; es decir,
si en un paso elegimos el vértice vk y la última minoración de D(k) se produjo tras la elección
del vértice vs , en el camino mı́nimo a vk el vértice anterior es vs . A su vez, podemos conocer el
vértice anterior en el camino mı́nimo a vs , y, sucesivamente, hasta llegar al vértice inicial.
Para la aplicación del algoritmo con lápiz y papel, se coloca el vector D inicial como la primera fila
de una tabla, de manera que en las filas sucesivas se van colocando los nuevos valores de D tras cada
minoración. Como en el ejemplo siguiente:
Ejemplo 23 Para el grafo de la figura, escribimos a su derecha la matriz de pesos Ω –en forma de tabla
con los vértices, para tener más presente los vértices implicados– y se aplica el algoritmo de Dijkstra para
hallar los pesos mı́nimos desde el vértice v1 a los otros vértices del grafo.
v2
s
@
7
v4
s
@
2
v6
s
@1
@8
3
v1 s
2 @
5 @
4
@
@
@
7
9
@
@
9@@
@s
@s
s
v3
1
v5
v7
Ω
v1
v2
v3
v4
v5
v6
v7
v1 v2 v3 v4 v5
0 3 9 ∞ ∞
3 0 2 7 1
9 2 0 7 1
∞ 7 7 0 5
∞ 1 1 5 0
∞ ∞ ∞ 2 9
∞ ∞ ∞ 8 ∞
v6
∞
∞
∞
2
9
0
4
v7
∞
∞
∞
8
∞
4
0
En la tabla hemos incluido una columna de Comentarios, donde destacamos los pesos mı́nimos obtenidos
∗
= . . . ) y el camino mı́nimo que se ha utilizado ( vi → vj = . . . ). Además, incluimos aquellas
∗
( ωij
Prof: José Antonio Abia Vian
I.T.I. en Electricidad
13 – Matemáticas I : Teorı́a de Grafos
1.3 Caminos de peso mı́nimo. Algoritmo de Dijkstra
comparaciones que minoran (o igualan) los valores existentes de D , D(i) + ωij ≤ D(j) , con la expresión
del camino que representan cada uno de esos valores.
L1 = {1} D(1) D(2) D(3) D(4) D(5) D(6) D(7) Comentarios
∗
−
3
9
∞
∞
∞
∞ ω12
= 3 = ω(v1 v2 )
∗
v1 → v2 = v1 v2
{1,2}
∗
D(2) + ω23 = ω(v1 → v2 v3 ) = 5 < 9 = ω(v1 v3 ) = D(3)
∗
D(2) + ω24 = ω(v1 → v2 v4 ) = 10 < ∞ = D(4)
∗
D(2) + ω25 = ω(v1 → v2 v5 ) = 4 < ∞ = D(5)
−
−
5
10
∞
4
∗
∗
ω15
= 4 = ω(v1 → v2 v5 )
∗
∗
v1 → v5 = v1 → v2 v5
∞
{1,2,5}
∗
∗
D(5) + ω53 = ω(v1 → v5 v3 ) = 5 = ω(v1 → v2 v3 ) = D(3)
∗
∗
D(5) + ω54 = ω(v1 → v5 v4 ) = 9 < 10 = ω(v1 → v2 v4 ) = D(4)
∗
D(5) + ω56 = ω(v1 → v5 v6 ) = 13 < ∞ = D(6)
−
−
5
9
−
13
∞
−
−
−
9
−
13
∞
∗
∗
∗
ω13
= 5 = ω(v1 → v5 v3 ) = ω(v1 → v2 v3 )
∗
∗
∗
v1 → v3 = v1 → v5 v3 ó v1 → v2 v3
∗
∗
ω14
= 9 = ω(v1 → v5 v4 )
∗
∗
v1 → v4 = v1 → v5 v4
{1,2,5,3}
{1,2,5,3,4}
∗
∗
D(4)+ω46 = ω(v1 → v4 v6 ) = 11 < 13 = ω(v1 → v5 v6 ) = D(6)
∗
D(4) + ω47 = ω(v1 → v4 v7 ) = 17 < ∞ = D(7)
−
−
−
−
−
11
∗
∗
ω16
= 11 = ω(v1 → v4 v6 )
∗
∗
v1 → v6 = v1 → v4 v6
17
{1,2,5,3,4,6}
∗
∗
D(6)+ω67 = ω(v1 → v6 v7 ) = 15 < 17 = ω(v1 → v4 v7 ) = D(7)
−
−
−
−
−
−
∗
∗
ω17
= 15 = ω(v1 → v6 v7 )
∗
∗
v1 → v7 = v1 → v6 v7
15
{1,2,5,3,4,6,7}
Antes de más comentarios, veamos como ha sido gráficamente el proceso realizado por el algoritmo:
(En cada paso, representamos con lı́neas continuas verdes los caminos que desde v1 ya son de peso mı́nimo y con
lı́neas discontinuas los caminos a los demás vértices que, en este momento del algoritmo, tienen menor peso –el
peso se indica entre corchetes–. Cuando el peso es ∞ , hemos dibujado en dorado unos caminos inexistentes para
visualizar mejor el proceso.)
r
v1
r
[∞]
[∞]
r
v2
r
[3]
v1
[9]
r
[∞]
r
r
r
3
[10]
[∞]
r
v1
r
r
[5]
[4]
r
r
v4
r
@
1
2
@
r
v3
[5]
[∞]
v2
v1
r
r
r
[9]
@1
@
@
@r
r
3
r
[∞]
3
v2
r
r
[11]
v1
5
@
@r
v5
v2
r
[17]
r
r
3
r
@
v4
r
2
v5
r
r
r
r
3
v4
r
@
1
2
@
r
v3
3
r
@
r
2
v5
[9]
r
[13]
@
@
@r
r
v5
r
[∞]
v6
r
4
5
@
@r
r
1
2
v3
v2
v1
[15]
v1
[∞]
r
2
v3
[13]
v6
1
5
@
@
@
r
r
v5
v2
r
r
v7
4
En la tabla que hemos usado para el ejemplo anterior, también hemos ido construyendo los caminos
postulados por Dijkstra en cada paso, almacenando el camino que representa en cada momento el valor
D(k) de cada vértice, con la intención de que el proceso algorı́tmico sea más claro. Pero esto es completamente innecesario para construir los caminos mı́nimos, pues basta con saber con que vértice se produce
la última minoración.
∗
∗ = 15 se obtiene al minorar
Por ejemplo, para construir el camino mı́nimo v1 → v7 : el peso mı́nimo ω17
∗
∗
∗
∗
17 con ω16 = 11 (el peso del camino mı́nimo v1 → v6 ), luego v1 → v7 = v1 → v6 v7 . Si encontramos
∗
v1 → v6 estará resuelto, pero para ello basta con repetir lo anterior para este camino y sucesivamente hasta
encontarar el camino final. En la tabla siguiente hemos descrito resumido todos los pasos a dar:
Prof: José Antonio Abia Vian
I.T.I. en Electricidad
14 – Matemáticas I : Teorı́a de Grafos
∗ = 15 se minora de 17 con ω ∗
ω17
16
∗ = 11 se minora de 13 con ω ∗
ω16
14
∗ = 9 se minora de 10 con ω ∗
ω14
15
∗ = 4 se minora de ∞ con ω ∗
ω15
12
∗ = ω(v v )
y ω12
1 2
1.3 Caminos de peso mı́nimo. Algoritmo de Dijkstra
luego
luego
luego
luego
luego
∗
v1 → v7
∗
v1 → v7
∗
v1 → v7
∗
v1 → v7
∗
v1 → v7
∗
= v1 → v6 v7
∗
= v1 → v4 v6 v7
∗
= v1 → v5 v4 v6 v7
∗
= v1 → v2 v5 v4 v6 v7
= v1 v2 v5 v4 v6 v7
En el segundo paso del algoritmo de Dijkstra hemos incluido el caso en que se produce una igualdad
en la comparación, lo que indica que ambos caminos son igualmente pesados. Tenemos ası́, que para el
∗ = 5 tanto v v v como v v v v son caminos mı́nimos de v a v .
peso mı́nimo ω13
1 2 3
1 2 5 3
1
3
1.3.2
Pesos mı́nimos con el algoritmo de Floyd
Usando el algoritmo de Floyd (Algoritmo 3) con una matriz de pesos, se obtinen los pesos mı́nimos entre
todos los vértices. Es decir, la matriz resultante contiene en cada posición el peso mı́nimo entre los vértices
correspondientes (el mismo resultado que si usaramos Dijkstra con cada uno de los vértices).
Es menos eficiente que Dijkstra, por lo que no es conveniente usarlo en su lugar; además es mucho más
costoso construir a partir de él los caminos mı́nimos entre los vértices. Está indicado cuando se trata de
encontrar los pesos mı́nimos entre todos los vértices, como en el ejemplo
Ejemplo 24 El grafo de la figura modela los costos del envı́o de mensajes entre las estaciones de una
red. Si deseo enviar un mensaje a cada una de las otras estaciones, ¿desde cuál debo hacerlo para que me
cueste menos?
Aplicamos el algoritmo de Floyd para hallarla matriz de peso mı́nimo
del grafo: 


v6
0 3 4 2 4 2
0 7 ∞ 2 ∞ 2
s
H
3 0 3 4 1 1
 7 0 4 4 1 1 
H 2
2




1 HH
4 3 0 6 4 2
∞ 4 0 ∞ ∞ 2 
HH s v3




∗
s 4
v1 s 7
Ω=

 −→ Ω = 




2
4
6
0
5
4
2
4
∞
0
∞
∞
@
v@
2




2@
4 1@
4 1 4 5 0 2
∞ 1 ∞ ∞ 0 ∞
@s
@s
2 1 2 4 2 0
2 1 2 ∞ ∞ 0
v4
v5
y, la respuesta a la pregunta inicial es la respuesta a: ¿desde cuál de los vértices la distancia a los otros es
menor? Ese debe ser el elegido (v6 ¿no? ¿por qué?).
4
1.3.3
Ejercicios
3.1 Utilizar el algoritmo de Dijkstra para encontrar, en
cada uno de los grafos de la derecha, los pesos mı́nimos
desde el vértice a a los demás. Dibujar los caminos
mı́nimos a medida que se obtengan
Para el primer grafo, encuentra la matriz de pesos
mı́nimos mediante el algoritmo de Floyd. ¿En la fila
correspondiente al vértice a aparecen los mismos valores que se obtuvieron con Dijkstra?
a
4
s
s
@
@5 2 @s
4 3 1
3
1 s
@
4 @3
s
@s
a
sHH4
s
Hs
A2
3 1
1 @
A
2@
4
s
@s
As
A 2
@ 5 1
3A
4
4@
As
@s
H
H
6 Hs 5
2
6
3.2 Modifica el algoritmo de Dijkstra, para que recoja en una lista el vértice que produce la última
minoración del peso mı́nimo para cada vértice.
3.3 La figura de la derecha corresponde a un plano de
calles con las direcciones de circulación y el tiempo en
minutos que se necesita para recorrer cada tramo.
Si un taller sito en la plaza B tiene que hacer una entrega
en la plaza F , calcular el tiempo mı́nimo que necesita
el repartidor para estar de nuevo disponible en el taller.
¿Cuál es el recorrido correspondiente?
Prof: José Antonio Abia Vian
A
3
7
5
3
6
B - C
5
- E
2
?
D
6
?
- F
I.T.I. en Electricidad
15 – Matemáticas I : Teorı́a de Grafos
1.3 Caminos de peso mı́nimo. Algoritmo de Dijkstra
3.4 Una empresa de autobuses tiene que ir de la ciudad
A a la ciudad D recogiendo viajeros en la ciudad C . Las
distancias en kilómetros entre las cinco ciudades A, B, C, D,
y E por las que puede pasar se dan en la tabla de la derecha.
¿Qué recorrido le conviene realizar a la empresa?
A B C D E
A − 20 80 50 30
B 20 − 60 70 −
C 80 60 − 90 40
D 50 70 90 − 10
E 30 − 40 10 −
3.5 Una empresa desea abrir sucursales en cinco ciudades A, B , C , D y E . Una de estas
sucursales actuará como central y será necesario el envı́o diario de camiones de la central al resto.
Cada camión sólo podrá abastecer a una de las sucursales. Las distancias entre ciudades en kilómetros
son: 300 entre A y B , 500 entre A y D , 200 entre B y C , 600 entre B y D , 400 entre C y
E y 100 entre E y D .
(a) ¿En qué ciudad interesa montar la central?
(b) ¿Qué recorrido deberá efectuar desde la central a las demás ciudades?
(c) Está previsto construir una carretera de 250 kilómetros que una las ciudades A y E . ¿Qué
soluciones propondrı́as entonces para los apartados a) y b)?
Hacer un programa en Matlab, que aplicando el algoritmo de Floyd, nos diga donde debemos
situar la central y el total de kilometros diarios que recorrerán los camiones de abastecimiento.
3.6
Sea W, la matriz de pesos del Ejemplo 24 anterior, V el conjunto de vértices y D la fila correspondiente al vértice v2 .
[i] Hacer L = [2] y construir F = V − L (es decir, eliminar el vértice 2 de V)
[ii] Obtener de mı́nimo valor de D, para los vértices de F, y el vértice al que corresponde
[iii] Si vk es el vértice obtenido en el apartado anterior, añadirlo a L y eliminarlo de F
[iv] Comprobar, para cada vértice vi que queda en F, si D(i) > D(k) + W (k, i); y si se cumple
hacer D(i) = D(k) + W (k, i)
Nota: Cada uno de los apartados anteriores representa uno de los pasos que se han de realizar en
el algoritmo de Dijkstra. Por ello, no hay que realizar las operaciones de manera especı́fica para
un dato sino de manera general, para que puedan implementarse en un programa común (es decir,
si por ejemplo el vértice obtenido en el apartado [ii] es el 5, no añadir a L el 5 sino añadir a L el
vértice almacenado en el resultado que nos dice que es el 5 ). [Para añadir elementos a un vector,
consultar la orden horzcat; y ver min aplicado a un vector, en el manual de Matlab ].
3.7
Para tener el algoritmo de Dijkstra, basta incorporar en un bucle los pasos construidos en el ejercicio
anterior. En esencia, el bucle while del algoritmo elige en cada “vuelta” el vértice que tiene el
valor mı́nimo en un vector y lo elimina, repitiendo el proceso hasta que no queda ningún vértice.
[i] Sea D=[3 0 1 2 5 3 4 6] y V=1:7. Tomemos el vértice que tenga el menor valor en D e
incorporarlo a L. Tomar ahora el vértice que tenga el menor valor en D, de los restantes, e
incorporarlo a L. Y sucesivamente...
[ii] Incorporar ahora dentro del bucle, el resto de los pasos del algoritmo de Dijkstra.. y ¡ya
tenemos el algoritmo completo!
[iii] ¿Puede sustituirse el bucle while por uno for? ¿Si?, ¿no?, ¿cómo?, ¿porqué?
[iv] Escribir el algoritmo en un fichero como una funcion (ver funtion).
[v] Incorpora al algoritmo la propuesta del ejercicio 3.2
Prof: José Antonio Abia Vian
I.T.I. en Electricidad