Algoritmo di Dijkstra
L'algoritmo di Dijkstra è un algoritmo utilizzato per cercare i cammini minimi in un grafo con o senza ordinamento, ciclico e con pesi non negativi sugli archi. Fu inventato nel 1956 dall'informatico olandese Edsger Dijkstra che lo pubblicò successivamente nel 1959. Tale algoritmo trova applicazione in molteplici contesti quale l'ottimizzazione nella realizzazione di reti (idriche, telecomunicazioni, stradali, circuitali, ecc.) o l'organizzazione e la valutazione di percorsi runtime nel campo della robotica. AlgoritmoSupponiamo di avere un grafo con n vertici contraddistinti da numeri interi {1,2,...,n} e che uno di questi nodi sia quello di partenza e un altro quello di destinazione. Il peso sull'arco che congiunge i nodi j e k è indicato con p(j,k). A ogni nodo, al termine dell'analisi, devono essere associate due etichette, f(i) che indica il peso totale del cammino (la somma dei pesi sugli archi percorsi per arrivare al nodo i-esimo) e J(i) che indica il nodo che precede i nel cammino minimo. Inoltre definiamo due insiemi S e T che contengono rispettivamente i nodi a cui sono già state assegnate le etichette e quelli ancora da scandire.
PseudocodiceNel seguente algoritmo, il codice 1 function Dijkstra(Grafo, sorgente): 2 For each vertice v in Grafo: // Inizializzazione 3 dist[v] := infinito ; // Distanza iniziale sconosciuta 4 // dalla sorgente a v 5 precedente[v] := non definita ; // Nodo precedente in un percorso ottimale 6 end for // dalla sorgente 7 8 dist[sorgente] := 0 ; // Distanza dalla sorgente alla sorgente 9 Q := L'insieme di tutti i nodi nel Grafo ; // Tutti i nodi nel grafo sono 10 // Non ottimizzati e quindi stanno in Q 11 while Q non è vuota: // Loop principale 12 u := vertice in Q con la più breve distanza in dist[] ; // Nodo iniziale per il primo caso 13 rimuovi u da Q ; 14 if dist[u] = infinito: 15 break ; // tutti i vertici rimanenti sono 16 end if // inaccessibili dal nodo sorgente 17 18 For each neighbour v di u: 20 alt := dist[u] + dist_tra(u, v) ; 21 if alt < dist[v]: // questa condizione e' sempre false se v e' gia stato rimosso da Q 22 dist[v] := alt ; 23 precedente[v] := u ; 24 decrease-key v in Q; // Riordina v nella coda 25 end if 26 end for 27 end while 28 return dist; Se siamo interessati solo al percorso minimo tra due nodi 1 S := sequenza vuota 2 u := destinazione 3 while precedente[u] è definito: // Costruisci il cammino minimo con uno stack S 4 inserisci u all'inizio di S // Esegui il push del vertice sullo stack 5 u := precedente[u] // Traverse da destinazione a sorgente. 6 end while ; Adesso la sequenza Tempo di esecuzioneLa complessità computazionale dell'algoritmo di Dijkstra può essere espressa in funzione di ed ossia, rispettivamente, il numero di nodi e degli archi appartenenti al grafo sul quale viene eseguito. L'algoritmo utilizza una coda di priorità su cui vengono effettuate tre operazioni: la costruzione della coda, l'estrazione dell'elemento minimo e la riduzione del valore di un elemento. La struttura dati utilizzata per l'implementazione della coda di priorità determina la complessità di queste tre operazioni e, di conseguenza, quella dell'algoritmo. In generale, la complessità, , dell'algoritmo di Dijkstra è limitata superiormente da dove , e sono le complessità necessarie alle operazioni di costruzioni di una coda con elementi, estrazione del minimo da una coda con elementi e la riduzione di un valore in una coda con elementi. Di seguito sono riportate le complessità di , , e dell'algoritmo di Dijkstra nel caso in cui le code di priorità siano implementate tramite array, heap binarie o heap di Fibonacci.
È interessante notare che, nel caso in cui il grafo non sia sufficientemente sparso e , la soluzione basata sugli array è più efficiente di quella implementata tramite le heap binarie. EsempioAlla base di questi problemi c'è lo scopo di trovare il percorso minimo (più corto, più veloce, più economico…) tra due punti, uno di partenza e uno di arrivo. Con il metodo che si vedrà è possibile ottenere non solo il percorso minimo tra un punto di partenza e uno di arrivo ma l'albero dei cammini minimi, cioè tutti i percorsi minimi tra un punto di partenza e tutti gli altri punti della rete. Come per praticamente tutti i problemi riguardanti le reti la cosa migliore è fare una schematizzazione della situazione per risolvere l'esercizio più agevolmente e avere sempre a disposizione i dati necessari. Una buona schematizzazione per i problemi di percorso minimo deve includere tutti i possibili collegamenti tra i nodi (e i relativi costi) e deve essere fissato un nodo di partenza. Si consideri un problema in cui si vuole calcolare il percorso minimo tra casa e il posto di lavoro. Si schematizzino tutti i possibili percorsi e il relativo tempo di percorrenza (supponendo di voler calcolare il percorso più breve in fatto di tempo di percorrenza). I nodi A, B, C, D, E indicano le cittadine per cui è possibile passare. Ecco una schematizzazione della rete: Bisogna ora assegnare a ogni nodo un valore, chiamato “potenziale”, seguendo alcune regole:
Si veda come si risolve questo esercizio nella pratica. Questa è la rete in cui sono indicati anche i potenziali: Seguendo le regole appena fissate si consideri il nodo con potenziale minore (“casa”) e lo si renda definitivo (colorandolo di rosso) e si aggiornino tutti i nodi adiacenti sommando l'attuale valore del potenziale (ovvero zero) al costo del percorso. Si aggiornino i potenziali perché si aveva, nel caso di A, potenziale infinito mentre ora il potenziale è 2. Ricordando che il potenziale minore è sempre preferibile. Ecco come si è aggiornata la rete: Bisogna ora considerare il nodo non definitivo (ovvero quelli scritti in nero) con potenziale minore (il nodo A). Lo si rende definitivo e si aggiornano i potenziali dei nodi adiacenti B e C. Si indichi con una freccia da dove proviene il potenziale dei nodi resi definitivi. Il nodo con potenziale minore ora è C. lo si rende definitivo e si aggiornano quelli adiacenti. Va notato come il nodo D abbia ora potenziale 6 in quanto 6 è minore di 8 e quindi lo si aggiorna. Se si fosse ottenuto un valore maggiore di quello che già c'era si sarebbe dovuto lasciare invariato. Si renda definitivo il nodo D e si aggiorni il grafico: Il nodo con potenziale minore restante è B e lo si rende definitivo aggiornando di conseguenza il grafico: Restano da considerare il nodo E e da aggiornare “ufficio”. Seguendo all'indietro le frecce si ottiene il percorso minimo da casa a ufficio che misura (come indicato dal potenziale) “10”. Bisogna notare come questo algoritmo ci dia non solo la distanza minima tra il punto di partenza e quello di arrivo ma la distanza minima di tutti i nodi da quello di partenza, da cui si può realizzare l'albero dei cammini minimi semplicemente eliminando gli archi utilizzati da nessun cammino. Note
Bibliografia
Voci correlate
Altri progetti
|