Problema: Supponiamo che un motociclista voglia raggiungere Genova partendo da Napoli. Avendo a disposizione una mappa dell’Italia in cui per ogni collegamento diretto tra città è segnata la sua lunghezza, come può il motociclista trovare il percorso minimo?
Dato un grafo pesato orientato G=(V,E), il peso di un cammino p=(v0,v1,…,vk) è dato dalla somma dei pesi degli archi che lo costituiscono.
Uno shortest path (cammino minimo) dal nodo u al nodo v di V è un cammino p = (u,v1,v2,…,v) tale che w(p) è minimo.
Il costo del cammino minimo da u a v è denotato con δ(u, v).
Se non esiste un cammino da u a v allora δ(u, v) = ∞
Dato un grafo pesato orientato G=(V,E) e uno shortest path p = (v0,v1,…,vk) da v0 a vk, qualsiasi sottocammino p’ = (vi,vi+1,…,vj) contenuto in p è anch’esso uno shortest path tra vi e vj
Dato un grafo pesato connesso orientato G=(V,E) e un nodo sorgente s di V, esistono diversi algoritmi per trovare uno SP da s verso ogni altro nodo di V (single-source shortest path problem). Dall’esecuzione di tali algoritmi si ottiene, per ogni nodo destinazione v di V, uno SP p (da s a v) e si calcola:
Inizializzazione: per ogni nodo v di V
L’idea è ad ogni passo d[v] tale che d[v] = δ(s, v). Durante l’esecuzione si usa la tecnica del rilassamento (relaxation) di un generico arco (u,v) di E, che serve a migliorare la nostra stima per d. Gli algoritmi si differenziano sulla modalità di eseguire il rilassamento:
Il rilassamento di un arco (u,v) di E, consiste nel valutare se, utilizzando u come predecessore di v, si può migliorare il valore corrente della distanza d[v] e, in tal caso, si aggiornano d[v] e π[v]
Procedura relax(u,v):
In (a), d[v] > d[u] + w(u,v). Quindi il valore di d[v] decresce
In (b), d[v] ≤ d[u] + w(u,v). Quindi d[v] non viene modificato
Tratto da: Introduzione agli algoritmi Di H.Cormen
Usando un array non ordinato per implementare la coda:
EXTRACT-MIN in tempo Θ(n), Relax in Θ(1)
Tempo totale: Θ(V + V V + E) = Θ(V2)
In un grafo non fortemtente conesso conviene usare un heap binario invece di una coda di priorità
Usando un heap, la complessità diventa: θ((V+E) logV)
Tratto da: Introduzione agli algoritmi di H.Cormen
Dopo aver effettuato l’inizializzazione, l’algoritmo fa |V| – 1 passate sugli archi del grafo: ogni passata è una iterazione del ciclo for delle linee 2-4 e consiste nel rilassare ogni arco del grafo una volta. Infine le linee 5-8 controllano l’esistenza di un ciclo di peso negativo e restituiscono il valore booleano appropriato.
L’algoritmo di Bellman – Ford richiede tempo O(VE), poiché l’inizializzazione in linea 1 richiede tempo θ(V) mentre i cicli for richiedono tempo O(E).
In definitiva l’algoritmo di Dijkstra è più conveniente rispetto a quello di Bellman-Ford, mentre l’ultimo algoritmo citato ha una duttilità maggiore perché é in grado di trovare il cammino minimo anche su grafi con archi di peso negativo.
Oltre ad algoritmi che risolvono il problema del cammino minimo su grafi con sorgente singola, ve ne sono alcuni che considerano il problema di trovare i cammini minimi tra tutte le coppie di vertici in un grafo.
Qui riportiamo l’algoritmo di Floyd-Warshall.
Si considerano tutti i cammini da i a j in cui vertici intermedi sono nell’insieme {1,…,k} e sia p un cammino minimo tra di essi.
E’ possibile definire una relazione tra p e i cammini minimi tra i vertici i e j i cui vertici intermedi sono nell’insieme {1,…,k-1}
Se k non è un vertice intermedio di p, allora tutti i vertici intermedi di p sono nell’insieme {1,…,k-1}
Questo significa che il peso di un cammino minimo da i a j in cui tutti i vertici intermedi sono in {1,…,k} è dato dal peso di un cammino minimo da i a j in cui tutti i vertci intermedi sono in {1,…,k-1}.
Se k è un vertice intermedio di p allora possiamo spezzare p così:
Questo significa che il peso di un cammino minimo da i a j in cui tutti i vertici intermedi sono in {1,…,k} è dato dal peso di un cammino minimo da i a k in cui tutti i vertici intermedi sono in {1,…,k-1} + il peso di un cammino minimo da k a j in cui tutti i vertci intermedi sono in {1,…,k-1}.
1. Introduzione al Corso - Il Linguaggio C (I parte)
2. Linguaggio C – Seconda Parte
3. Ordinamento, Ricorsione e Code di Priorità
4. Esercitazione su Ricorsione e Code di Priorità
5. Stack e Code
6. Esercitazione di Laboratorio su Stack e Code
7. Implementazioni di Liste puntate
8. Esercitazione di laboratorio su Liste Puntate Semplici
9. Implementazioni di Liste Doppiamente Puntate e Circolari
10. Esercitazione di laboratorio su Liste Doppiamente puntate
12. Esercitazione di laboratorio su Alberi Binari di Ricerca
13. Alberi Binari di Ricerca. Cancellazione di un nodo
14. Esercizio di Laboratorio. Gioco su alberi
15. Grafi: Implementazione ed operazioni di base
16. Esercitazione di laboratorio: Implementazione operazioni di bas...
17. Grafi: Inserimento e Cancellazione di un nodo. Visite in ampiez...
18. Esercitazione di laboratorio: Problema del venditore Prima part...
19. Componenti fortemente connesse e alberi minimi di copertura
20. Esercitazione di laboratorio: Problema del venditore Seconda pa...
H.Cormen, Introduzione agli algoritmi