120 likes | 245 Views
Single-Source Shortest Paths “Camino más corto desde/hacia una fuente”. Agustín J. González ELO320: Estructura de Datos y Algoritmos. Introducción.
E N D
Single-Source Shortest Paths“Camino más corto desde/hacia una fuente” Agustín J. González ELO320: Estructura de Datos y Algoritmos
Introducción • Un estudiante quiere buscar el camino más corto que le permita llegar desde su casa, a la de la polola (pololo) pasando sólo por avenidas (para hacerlo doble sentido). • Un computador debe determinar la ruta más conveniente para un paquete hacia su destino. • Se trata entonces de buscar la ruta más “económica” para ir desde un nodo a cada uno de los otros. Se trata de buscar las rutas de menor costo a cada uno de los nodos. • El algoritmo breadth-first search obtiene la ruta más corta en grafos sin peso, en el cual cada arco se puede considerar de peso unitario. • Variantes: Camino más corto a un destino único Camino más corto entre dos nodos cualquiera.
6 3 s s s 0 0 0 4 2 1 7 2 2 5 5 11 6 x y 6 3 6 3 4 2 1 7 2 4 2 1 7 2 2 5 5 11 2 u u u v v v 6 5 5 11 6 x y 3 9 3 9 3 9 x y Ejemplos de Rutas más cortas desde una fuente Grafo original Otra solución Una solución
u v u v v u u v 2 2 2 2 5 6 5 6 9 5 5 7 Observaciones • Lema: Subcaminos del camino más corto son caminos más cortos.Dado un grafo dirigido con peso G=(V,E), sea p = <v1,v2,v3,..,vk> el camino más corto desde v1 a vk, para 1 i j k sea pij = <vi,vi+1, ,, vj> el subcamino de p desde el vértice vi al vj. Entonces pij es un camino más corto para ir de vi a vj. • “Relajación” a través de un arco: Sea d[v] la estimación para la distancia más corta desde el nodo fuente. Al estudiar el arco (u,v) podemos mejorar la estimación dependiendo si la ruta vía u es mejor. Esta operación es conocida como “Relajar”. El algoritmo es:Relax (u,v, w){ if (d[v] > d[u] + w(u,v) ){ d[v] = d[u] + w(u,v); p[v] = u; }} Relax(u,v) Relax(u,v)
Algoritmo de Dijkstra Dijkstra(G, w, s) { for (cada vértice v en V[G] ) { d [v] = infinito; /* -”MAX_INT” por ejemplo*/ p [v] = NIL; } d [s] = 0; S = {}; /* S Contiene el arreglo de los nodos cuyo camino más corto ya ha sido encontrado */ Q = V [G]; while (Q != {}) { u = Extract_Min(Q); /* d [u] es Mínimo, lg V, |V| veces*/ S = S {u}; for ( cada vértice v en adj[u] ) /* un total de |E| veces */ Relax(u,v, w); /* O(lg V) */ }}
1 1 1 10 10 10 9 9 9 0 0 0 s 2 3 s 2 s 2 3 3 6 6 6 4 4 4 7 7 7 5 5 5 7 5 5 2 2 2 x y x y x y v u u 1 9 1 10 8 8 1 10 10 9 2 9 3 6 2 9 3 4 6 0 0 0 s s s 2 3 4 6 4 7 7 7 5 7 5 5 u u u u v v v v v 7 5 5 2 7 5 2 2 10 8 8 13 14 9 x y x y x y Ejemplo de ejecución del algoritmo de Dijkstra:
Comentarios sobre Algoritmo de Dijkstra • El tiempo de ejecución depende de la implementación de la cola de prioridad. Si se usara un arreglo lineal, Extract_Min toma O(V) con lo cual el algoritmo toma O(V2 +E) = O(V2).Si se usa un heap binario, Extract_Min toma O(lg V) con lo cual se reduce el tiempo total a =O(E lg V). Dijkstra(G, w, s) { for (cada vértice v en V[G] ) { d [v] = infinito; p [v] = NIL; } d [s] = 0; S = {}; /* S Contiene el arreglo de los nodos cuyo camino más corto ya ha sido encontrado */ Q = V [G]; while (Q != {}) { u = Extract_Min(Q); S = S {u}; for ( cada vértice v en adj[u] ) Relax(u,v, w); }} O(|V|) O(log|V|) cada vez, |V| veces |E| veces O(log |V|) Total=O(|V| log|V| +|E| log|V|) = O(|E| log |V|)
Comentarios sobre Algoritmo de Dijkstra • Se supone que todos los arcos tienen peso no negativo. ¿Qué pasa si hay pesos negativos? • Recordemos el supuesto del algoritmo: cuando extraemos un mínimo, la incorporación de nuevos nodos no mejorará su distancia a la fuente. Si tenemos arcos negativos, entonces puede ocurrir que nodos por incorporar mejoren la situación de aquellos ya ingresados a S. Esta situación no es considerada en Dijkstra -10 s S
v 1 6 -2 0 2 -4 z 8 -3 7 7 9 x y u Algoritmo de Bellman-Ford • Se trata de resolver el mismo problema en el caso de pesos negativos y positivos. • Observamos que le camino de menor peso deberá tener un largo menor a |V|-1 que es la ruta más larga posible. • La idea es aplicar relajación tantas veces como el camino más largo que pueda existir entre un la fuente y un nodo. • Con ello deberemos encontrar la mejor ruta. • Ojo que si hay un ciclo con suma negativa, no existe solución.
Algoritmo de Bellman-Ford int Bellman-Ford(G, w, s) { for (cada vértice v en V[G] ) { d [v] = infinito; /* -”MAX_INT” por ejemplo*/ p [v] = NIL; } d [s] = 0; for (i=1 to |V [G]| -1 ) /* total de |V|-1 veces */ for (cada arco (u,v) en E[G] ) /*cada vez |E| veces */ Relax(u,v,w); for ( cada arco (u,v) en E[G] ) if(d [v] > d [u]+w(u,v)) return 0; /* False; no existe camino mínimo */ return 1; }
v v v v v 1 1 1 4 4 4 6 6 6 -2 -2 -2 0 2 -4 z 8 0 2 -4 z 8 0 2 -4 z 8 -3 -3 7 -3 7 7 7 7 7 7 2 7 9 9 9 x y x y x y 1 1 6 6 -2 -2 0 0 2 2 -4 -4 z z 8 8 -3 -3 7 7 u u u u u 7 7 -2 2 7 7 6 6 2 2 9 9 x x y y Ejemplo: Algoritmo de Bellman-Ford
Algoritmo de Bellman-Ford El tiempo de ejecución es O(|V|+ (|V|-1)*|E| + |E|) = O(|V||E|). int Bellman-Ford(G, w, s) { for (cada vértice v en V[G] ) { d [v] = infinito; p [v] = NIL; } d [s] = 0; for (i=1 to |V [G]| -1 ) for (cada arco (u,v) en E[G] ) Relax(u,v,w); for ( cada arco (u,v) en E[G] ) if(d [v] > d [u]+w(u,v)) return 0; return 1; } O(|V|) |V|-1 veces Cada vez |E| veces O(1) cada vez O(|E|)