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 hallarla 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