Algoritmo de Dijkstra

9

Click here to load reader

Transcript of Algoritmo de Dijkstra

Page 1: Algoritmo de Dijkstra

Ejecución del algoritmo de Dijkstra.

Algoritmo de Dijkstra

El algoritmo de Dijkstra, también llamado algoritmode caminos mínimos, es un algoritmo para ladeterminación del camino más corto dado un vérticeorigen al resto de vértices en un grafo dirigido y con pesosen cada arista. Su nombre se refiere a Edsger Dijkstra,quien lo describió por primera vez en 1959.

La idea subyacente en este algoritmo consiste en irexplorando todos los caminos más cortos que parten delvértice origen y que llevan a todos los demás vértices;cuando se obtiene el camino más corto desde el vérticeorigen, al resto de vértices que componen el grafo, elalgoritmo se detiene. El algoritmo es una especializaciónde la búsqueda de costo uniforme, y como tal, no funcionaen grafos con aristas de costo negativo (al elegir siempre elnodo con distancia menor, pueden quedar excluidos de la búsqueda nodos que en próximas iteracionesbajarían el costo general del camino al pasar por una arista con costo negativo).

Contenido

1 Algoritmo2 Complejidad3 Pseudocódigo4 Otra versión en pseudocódigo sin cola de prioridad5 Implementación

5.1 C++5.2 C++ un poco más simple con arreglos5.3 C++ mediante Heaps

6 Enlaces externos

Algoritmo

Teniendo un grafo dirigido ponderado de N nodos no aislados, sea x el nodo inicial, un vector D de tamañoN guardará al final del algoritmo las distancias desde x al resto de los nodos.

Inicializar todas las distancias en D con un valor infinito relativo ya que son desconocidas alprincipio, exceptuando la de x que se debe colocar en 0 debido a que la distancia de x a x sería 0.

1.

Sea a = x (tomamos a como nodo actual).2.Recorremos todos los nodos adyacentes de a, excepto los nodos marcados, llamaremos a estos vi.3.Si la distancia desde x hasta vi guardada en D es mayor que la distancia desde x hasta a sumada a la4.

1 of 9 11/02/2012 17:17

Page 2: Algoritmo de Dijkstra

distancia desde a hasta vi; esta se sustituye con la segunda nombrada, esto es:si (Di > Da + d(a, vi)) entonces Di = Da + d(a, vi)Marcamos como completo el nodo a.5.Tomamos como próximo nodo actual el de menor valor en D (puede hacerse almacenando los valoresen una cola de prioridad) y volvemos al paso 3 mientras existan nodos no marcados.

6.

Una vez terminado al algoritmo, D estará completamente lleno.

Complejidad

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 montículo).

Podemos estimar la complejidad computacional del algoritmo de Dijkstra (en términos de sumas ycomparaciones). El algoritmo realiza a lo más n-1 iteraciones, ya que en cada iteración se añade un vértice alconjunto distinguido. Para estimar el número total de operaciones basta estimar el número de operacionesque se llevan a cabo en cada iteración. Podemos identificar el vértice con la menor etiqueta entre los que noestán en Sk realizando n-1 comparaciones o menos. Después hacemos una suma y una comparación paraactualizar la etiqueta de cada uno de los vértices que no están en Sk. Por tanto, en cada iteración se realizan alo 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 O(n2) operaciones (sumas y comparaciones) para determinarla longitud del camino más corto entre dos vértices de un grafo ponderado simple, conexo y no dirigido conn vértices.

Pseudocódigo

Estructura de datos auxiliar: Q = Estructura de datos Cola de prioridad (se puede implementar con unmontículo)

DIJKSTRA (Grafo G, nodo_fuente s) para u ∈ V[G] hacer distancia[u] = INFINITO padre[u] = NULL distancia[s] = 0 Encolar (cola, grafo) mientras que cola no es vacía hacer u = extraer_minimo(cola) para v ∈ adyacencia[u] hacer si distancia[v] > distancia[u] + peso (u, v) hacer distancia[v] = distancia[u] + peso (u, v) padre[v] = u

Otra versión en pseudocódigo sin cola de prioridad

2 of 9 11/02/2012 17:17

Page 3: Algoritmo de Dijkstra

función Dijkstra (Grafo G, nodo_salida s) //Usaremos un vector para guardar las distancias del nodo salida al resto entero distancia[n] //Inicializamos el vector con distancias iniciales booleano visto[n] //vector de boleanos para controlar los vertices de los que ya tenemos la distancia mínima 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 número de vertices que tiene el Grafo mientras que (no_esten_vistos_todos) hacer vertice = coger_el_minimo_del_vector distancia y que no este visto; visto[vertice] = cierto; para cada w ∈ sucesores (G, vertice) hacer si distancia[w]>distancia[vertice]+peso (vertice, w) entonces distancia[w] = distancia[vertice]+peso (vertice, w) fin si fin para fin mientrasfin función

Al final tenemos en el vector distancia en cada posición la distancia mínima del vertice salida a otro verticecualquiera.

Implementación

C++

3 of 9 11/02/2012 17:17

Page 4: Algoritmo de Dijkstra

#include <cmath>#include <cstring>#include <iostream>using namespace std; int destino, origen, vertices = 0;int *costos = NULL; void dijkstra(int vertices, int origen, int destino, int *costos) { int i, v, cont = 0; int *ant, *tmp; int *z; /* vertices para los cuales se conoce el camino minimo */ double min; double *dist = new double[vertices]; /* vector con los costos de dos caminos */ /* aloca las lineas de la matriz */ ant = new int[vertices]; tmp = new int[vertices]; z = new int[vertices]; for (i = 0; i < vertices; i++) { if (costos[(origen - 1) * vertices + i] !=- 1) { ant[i] = origen - 1; dist[i] = costos[(origen-1)*vertices+i]; } else { ant[i]= -1; dist[i] = HUGE_VAL; } z[i]=0; } z[origen-1] = 1; dist[origen-1] = 0; /* Bucle principal */ do { /* Encontrando el vertice que debe entrar en z */ min = HUGE_VAL; for (i=0;i<vertices;i++) if (!z[i]) if (dist[i]>=0 && dist[i]<min) { min=dist[i];v=i; } /* Calculando las distancias de los nodos vecinos de z */ if (min != HUGE_VAL && v != destino - 1) { z[v] = 1; for (i = 0; i < vertices; i++) if (!z[i]) { if (costos[v*vertices+i] != -1 && dist[v] + costos[v*vertices+i] < dist[i] = dist[v] + costos[v*vertices+i]; ant[i] =v; } } } } while (v != destino - 1 && min != HUGE_VAL); /* Muestra el resultado de la búsqueda */ cout << "\tDe " << origen << " para "<<destino<<" \t"; if (min == HUGE_VAL) { cout <<"No Existe\n"; cout <<"\tCoste: \t- \n";4 of 9 11/02/2012 17:17

Page 5: Algoritmo de Dijkstra

C++ un poco más simple con arreglos

Otra posible implementación del algoritmo de Dijkstra incluye un arreglo para las distancias. Esto hace laimplementación más simple, pero no es eficiente en uso de memoria y además la versión presentada estálimitada en el número de nodos del grafo.

5 of 9 11/02/2012 17:17

Page 6: Algoritmo de Dijkstra

#include <iostream>#include <limits.h>#include <float.h> #define MAXNODOS 500 // tamaño máximo de la red#define INDEFINIDO -1 int N;float dist[MAXNODOS][MAXNODOS];float minimo[MAXNODOS];int anterior[MAXNODOS];bool visitado[MAXNODOS]; bool minimoNodoNoVisitado (int * nodo){

int candidato = -1;for (int n = 0; n < N; n++) {

if (!visitado[n] && (minimo[n] < minimo[candidato] || candidato == -1)candidato = n;

}}*nodo = candidato;return candidato != -1;

} void dijkstra (int origen, int destino) {

minimo[origen] = 0.0;int nodo;

while (minimoNodoNoVisitado(&nodo)) {

if (minimo[nodo] == FLT_MAX)return; // otros vértices son inaccesibles

visitado[nodo] = true;for (int n = 0; n < N; n++) {

if (!visitado[n] && distancia[nodo][n] < 1.0) {float posibleMin = minimo[nodo] + distancia[nodo][n];if (posibleMin < minimo[n]) {

minimo[n] = posibleMin;anterior[n] = nodo;

}}

}}

} void camino_minimo (int inicio, int final){

if (inicio != final) {camino_minimo (inicio, anterior[final]);cout << ", ";

}cout << final;

} int main(int argc, char **argv){

int M, inicio, final;

// inicializar matriz de distancias y camino minimocin >> N;for (int k = 0; k < N; k++) {

anterior[k] = INDEFINIDO;visitado[k] = false;minimo[k] = MAXINT;6 of 9 11/02/2012 17:17

Page 7: Algoritmo de Dijkstra

C++ mediante Heaps

Otra posible implementación del algoritmo de Dijkstra es mediante montículos binarios.

7 of 9 11/02/2012 17:17

Page 8: Algoritmo de Dijkstra

struct T_Heap{ A monticulo; int num_elem;}; void CrearHeap(T_Heap& heap){ heap.num_elem= 0; for (int i=0;i<MAX_HEAP;i++){ heap.monticulo[i]= NULL; }//for}void Intercambiar(T_Heap& heap, int i, int j){ T_Lista aux; aux= heap.monticulo[i]; heap.monticulo[i]= heap.monticulo[j]; heap.monticulo[j]= aux;}void Meter(T_Heap& heap, const T_Lista& elem){ int k; k= heap.num_elem; heap.monticulo[k]= elem; while(k != 0 || (heap.monticulo[k]->peso > heap.monticulo[((k-1)/ 2)]->peso){ Intercambiar(heap,k,((k-1)/2)); k= (k-1)/2; }//while heap.num_elem++;}void Sacar(T_Heap& heap, int& elem){ int k; elem= heap.monticulo[0]; heap.monticulo[0]= heap.monticulo[heap.num_elem-1]; heap.monticulo[heap.num_elem-1]= NULL; heap.num_elem--; k= 0; while(k<heap.num_elem && (heap.monticulo[k]->peso < heap.monticulo[2*k+1]->peso || heap.monticulo[k]->peso < heap.monticulo[2*k+2]->peso)){ if (heap.monticulo[k] < heap.monticulo[2*k+1]){ Intercambiar(heap,k,2*k+1); k= 2*k+1; }else{ Intercambiar(heap,k,2*k+2); k= 2*k+2; }//if }//while}bool HeapLleno(const T_Heap& heap){ return(heap.num_elem== MAX_HEAP);}bool HeapVacio(const T_Heap& heap){ return(heap.num_elem== 0);}void DestruirHeap(T_Heap& heap){ for (int i=0;i<MAX_HEAP;i++){ heap.monticulo[i]= NULL; }//for heap.num_elem= 0;}

8 of 9 11/02/2012 17:17

Page 9: Algoritmo de Dijkstra

Esta es una implementación del algoritmo de Dijkstra mediante montículos binarios, que es capaz de dar losmejores resultados para que el algoritmo de Johnson sea más eficiente. La implementación del algoritmodevuelve un array de elementos precedentes y otro de distancias, mediante el primero se puede seguir elcamino de menor coste desde el nodo pasado como argumento a cualquier otro nodo del grafo, y siparalelamente vamos sumando las distancias del otro array, obtenemos el coste total de dichos caminosmínimos.

void Dijkstra(const T_Grafo& grafo, int origen, T_Vector& distancias, T_Vector& previos{ T_Vector marcados; T_Heap colap; T_Lista aux; InicializarVector(distancias); // inicializa los elementos a -1 InicializarVector(previos); InicializarVector(marcados); distancias[origen]= 0; marcados[origen]= 0; CrearHeap(colap); MeterAdyacentes(colap, grafo, origen, marcados); while (!HeapVacio(colap){ aux = Sacar(colap); marcados[aux->origen]= 0; MeterAdyacentes(colap, grafo, aux->origen, marcados); while (aux != NULL){ if (distancias[aux->destino] > (distancias[aux->origen] + aux->peso)){ distancias[aux->destino]= distancias[aux->origen] + aux->peso; padre[aux->destino] = aux->origen; }//if aux= aux->sig; }//while }//while}

Enlaces externos

Presentación del Algoritmo de DijkstraApplets en Java para probar el algoritmo de Dijkstra (Inglés)Graph módulo Perl en CPANBio::Coordinate::Graph módulo Perl en CPAN que implementa el algoritmo de DijkstraVideo Tutorial en VideoPractico.com de Dijkstragiswiki.net Algoritmo de Dijkstra en lenguajes como PHP, Actionscript y otros

Categorías: Algoritmos de búsqueda | Algoritmos de grafos

9 of 9 11/02/2012 17:17