Algoritmo di Dijsktra: C++, Python Esempio di codice
Qual รจ il percorso piรน breve o la distanza piรน breve?
Un percorso dal vertice di origine al vertice di destinazione che costa un minimo รจ il percorso piรน breve o la distanza piรน breve. Nella teoria dei grafi รจ possibile avere piรน percorsi da una sorgente a una destinazione. Tra questi percorsi, se esiste un percorso che costa un importo minimo, possiamo chiamarlo algoritmo del percorso piรน breve.
Qui โCostoโ indica il numero di nodi nel percorso o la somma dei costi su ciascun percorso. Un percorso puรฒ avere uno o piรน bordi. La connessione tra due vertici รจ chiamata โspigoloโ. Esistono vari tipi di algoritmi del percorso piรน breve, come l'algoritmo di Dijkstra, l'algoritmo di Bellman-Ford, ecc.
Qui discutiamo dell'algoritmo di Dijkstra
Diamo un'occhiata al seguente grafico ponderato:

- Il termine โponderatoโ significa spostare i costi da un nodo allโaltro. Ad esempio, spostandosi dal nodo 1 al nodo 2, il costo o peso รจ 1.
- Il percorso tra il nodo 1 e il nodo 2 รจ chiamato bordo.
- "Non indirizzato" significa che puoi spostare un nodo su un altro e tornare al nodo precedente. Quindi, se proviamo a trovare tutti i percorsi dal nodo 1 al nodo 7, allora sarร :
| Itinerario o percorso | Costo |
|---|---|
| 1-2-6-7 | (1+3+3) = 7 |
| 1-2-3-7 | (1+9+1) = 11 |
| 1-3-7 | (7+1) = 8 |
| 1-4-5-7 | (6+2+5) = 13 |
Tra questi quattro percorsi, possiamo vedere che il primo percorso ci costerร 7. Quindi รจ il percorso piรน breve in termini di costo.
Come funziona l'algoritmo di Dijkstra
L'algoritmo di Dijkstra puรฒ trovare la distanza piรน breve nei grafi pesati sia diretti che non orientati. Questo algoritmo รจ avido perchรฉ sceglie sempre il nodo piรน corto o piรน vicino all'origine. Il termine โavidoโ significa che tra una serie di esiti o risultati, lโalgoritmo sceglierร il migliore tra loro.
Qui stiamo cercando di trovare i percorsi piรน brevi tra tutti gli altri percorsi. Quindi, l'algoritmo di Dijkstra trova tutti i percorsi piรน brevi da un singolo nodo di destinazione. Di conseguenza, si comporta come a algoritmo avido.
Nella sezione "esempio" di seguito vedrai l'approccio passo passo. Funziona come segue:
Passo 1) Inizializza il nodo iniziale con 0 costi e il resto del nodo come Costo Infinito.
Passo 2) Mantieni un array o un elenco per tenere traccia dei nodi visitati
Passo 3) Aggiorna il costo del nodo con il costo minimo. Puรฒ essere fatto confrontando il costo attuale con il costo del percorso. (La dimostrazione รจ mostrata nella sezione di esempio)
Passo 4) Continuare il passaggio 3 finchรฉ non viene visitato tutto il nodo.
Dopo aver completato tutti questi passaggi, troveremo il percorso che costa il minimo dalla fonte alla destinazione.
Differenza tra Dijkstra e BFS, DFS
La differenza principale tra Dijkstra e BFS-DFS รจ che Dijkstra รจ l'algoritmo di pathfinding piรน breve e BFS, DFS รจ l'algoritmo di pathfinding. In generale, BFS e DFS non considerano il costo durante la ricerca del percorso. Pertanto, questi algoritmi non possono garantire il percorso piรน breve.
Dimostrazione in griglia 2D di come funziona BFS
Questa dimostrazione indica che BFS trova solo il percorso. Tuttavia, non si preoccupa del peso del percorso. BFS (Ricerca in base all'ampiezza) presuppone che viaggiare da un nodo a un altro nodo costerร solo 1.
Ma vediamo un grafico di esempio:
Qui trova un percorso nel livello 2. BFS attraversa il grafico in ordine di livello. Quindi viaggia come:
Passo 1) Inizia dal nodo โ1โ e visita tutti i nodi adiacenti 2,3,4
Passo 2) Contrassegna il nodo 2,3,4 come livello 1 e visita i nodi adiacenti. Continuerร ad esplorare tutti i nodi adiacenti fino a raggiungere il nodo di destinazione.
In termini di DFS, attraverserร il percorso da 1 a 7 come segue:
- 1โ2โ3โ7 (Costo originale 10, costo DFS 3)
- 1โ2โ6โ7 (Costo originale 7, costo DFS 3)
- 1โ3โ7 (Costo originale 8, costo DFS 2)
- 1โ4โ5โ7 (Costo originale 13, costo DFS 3)
Come vediamo, DFS calcola il costo del percorso con il numero di profonditร o il numero di bordi.
DFS esegue le seguenti operazioni:
- DFS puรฒ trovare un percorso dall'origine (vertice iniziale) alla destinazione.
- Non puรฒ garantire se il percorso scoperto dal nodo di origine alla destinazione sia il percorso piรน breve o meno.
Tuttavia, in termini di algoritmo Dijkstra, sceglierร i bordi in base al loro costo. Come un algoritmo avido, sceglierร il percorso migliore o quello a costo minimo.
Esempio dell'algoritmo di Dijkstra
L'algoritmo di Dijkstra utilizza il costo o il peso per calcolare il costo totale del percorso.
L'obiettivo dell'algoritmo di Dijkstra รจ ridurre al minimo questo costo o peso totale. Nell'esempio mostrato sopra, troviamo i percorsi migliori dal nodo 1 al nodo 7, quindi calcoliamo tutti i costi.
Nell'algoritmo di Dijkstra, troverร i percorsi piรน brevi calcolando i pesi. Non cercherร tutti i percorsi possibili. Dimostriamo l'algoritmo di Dijkstra con un esempio. Ad esempio, ti รจ stato chiesto di trovare il percorso piรน breve dal nodo 1 al nodo 7.
Per questo processo, i passaggi sono indicati di seguito:
Passo 1) Inizializza il costo del nodo iniziale su 0.
Resto del nodo, assegna โInfโ. Significa che non esiste alcun percorso tra il vertice iniziale (la sorgente) e il nodo, oppure il percorso non รจ ancora stato visitato.
Passo 2) Quando selezioni il nodo 1, verrร contrassegnato come visitato. Quindi aggiorna tutti i vicini adiacenti del nodo 1. 2,3,4 sono i nodi vicini del nodo 1.
Durante l'aggiornamento di un costo, dobbiamo seguire la procedura seguente:
Possiamo aggiornare il costo di ciascun nodo utilizzando la formula sopra. Ad esempio, eravamo al nodo 1 e dovevamo aggiornare il costo del nodo adiacente 2,3,4.
Dopo l'aggiornamento, il costo o il peso sarร simile a questo:
Passaggio 3) Per il nodo โ2โ, i vicini sono 6 e 3. Stiamo aggiornando il costo a โ6โ confrontando l'infinito (valore attuale). Il costo del nodo 2 + costo del percorso da 2 a 6. In parole povere, il nodo โ6โ avrร il costo di 1+3 o 4.
Il nodo 3 รจ vicino al nodo 2. Tuttavia, abbiamo calcolato il suo costo nel passaggio precedente, che era 7. Ora, se il nostro percorso รจ 1-2-3, il nodo 3 avrร un costo pari a 10. Percorso 1-2- 3 costeranno 10, mentre da 1 a 3 costeranno 7.
Passaggio 4) Per il nodo 3, il nodo vicino รจ 7. Quindi, confrontando il valore attuale del nodo 7 con il costo del percorso (7+1) o 8, aggiorneremo il costo del nodo 7. Cioรจ 8.
Quindi, troviamo un percorso dal nodo 1 al nodo 7, ed รจ 1โ 3โ 7. Il costo รจ 8.
Passaggio 5) Per il nodo 4, aggiorneremo di conseguenza il costo del nodo adiacente. Quindi, il nodo โ5โ avrร un costo aggiornato pari a 8. Dopo il passaggio 4,5, sarร simile a questo:
Adesso il percorso 1-3-7 ha il costo di 8 (in precedenza). Il nodo โ7โ non รจ stato contrassegnato come visitato perchรฉ possiamo raggiungere il nodo โ7โ dal nodo โ6โ. Il percorso โ1-2-6โ aveva un costo pari a 4. Quindi il percorso 1-2-6-7 avrร un costo pari a 7.
Poichรฉ 7 < 8, ecco perchรฉ il percorso piรน breve dal vertice di origine โ1โ al vertice di destinazione โ7โ sarร 1-2-6-7 e il costo รจ 7. In precedenza era 1-3-7 e il costo erano le 8.
Quindi, il grafico finale sarร simile a questo:
Il bordo contrassegnato da una linea nera รจ il nostro percorso piรน breve da 1 a 7 e ci costerร 7.
Algoritmo di Pseudocodice Dijkstra
Ecco lo pseudo-codice per l'algoritmo di Dijkstra
Dijkstra(G, S):
for each vertex V in G
distance[V] & lt; - Infinity
previous[V] & lt; - NULL
if V does not equal S, then,
(priority queue) Q.push(V)
distance[S] = 0
While Q is not empty
U & lt; - Extract the MIN from Q
For each unvisited adjacent V of U
TotalDistance & lt; - distance[U] + edge_cost(U, V)
if TotalDistance is less than distance[V], then
distance[V] & lt; - TotalDistance
previous[V] & lt; - u
return distance, previous
C++ implementazione dell'algoritmo di Dijkstra
Per implementare l'algoritmo di Dijkstra utilizzando C++, ecco il codice:
#include <bits/stdc++.h>
using namespace std;
#define size 7
int minimumDistance(int distance[], bool visited[]) {
int min = INT_MAX;
int min_index = INT_MAX;
for (int i = 0; i < size; i++) {
if (!visited[i] & amp; & distance[i] <= min) {
min = distance[i];
min_index = i;
}
}
return min_index;
}
void printParentPath(int parent[], int i) {
if (parent[i] == -1) {
return;
}
printParentPath(parent, parent[i]);
cout << i + 1 << " ";
}
void dijkstra(int graph[size][size], int source) {
int distance[size];
bool visited[size];
int parent[size];
for (int i = 0; i < size; i++) {
parent[0] = -1;
distance[i] = INT_MAX;
visited[i] = false;
}
distance[source] = 0;
for (int i = 0; i < size - 1; i++) {
int U = minimumDistance(distance, visited);
visited[U] = true;
for (int j = 0; j < size; j++) {
int curr_distance = distance[U] + graph[U][j];
if (!visited[j] & amp; & graph[U][j] & amp; &
curr_distance < distance[j]) {
parent[j] = U;
distance[j] = curr_distance;
}
}
}
cout << "Vertex\t\tDistance\tPath" << endl;
for (int i = 1; i < size; i++) {
cout << source + 1 << "->" << i + 1 << "\t\t" << distance[i] << "\t\t"
<< source + 1 << " ";
printParentPath(parent, i);
cout << endl;
}
}
int main() {
int graph[size][size] = {{0, 1, 7, 6, 0, 0, 0}, {1, 0, 9, 0, 0, 3, 0},
{7, 9, 0, 0, 0, 0, 1}, {6, 0, 0, 0, 2, 0, 0},
{0, 0, 0, 2, 0, 0, 0}, {0, 3, 0, 0, 0, 0, 3},
{0, 0, 0, 0, 5, 3, 0}};
dijkstra(graph, 0);
}
Produzione:
Vertex Distance Path 1->2 1 1 2 1->3 7 1 3 1->4 6 1 4 1->5 8 1 4 5 1->6 4 1 2 6 1->7 7 1 2 6 7
Python implementazione dell'algoritmo di Dijkstra
Per implementare l'algoritmo di Dijkstra utilizzando python, ecco il codice:
num_of_vertex = 7
def minimumDistance(distance, visited):
_min = 1e11
min_index = 1e11
for i in range(num_of_vertex):
if not visited[i] and distance[i] & lt; = _min:
_min = distance[i]
min_index = i
return min_index
def printParentNode(parent, i):
if parent[i] == -1:
return
printParentNode(parent, parent[i])
print("{} ".format(i + 1), end = "")
def dijkstra(graph, src):
distance = list()
visited = list()
parent = list()
for i in range(num_of_vertex):
parent.append(-1)
distance.append(1e11)
visited.append(False)
distance[src] = 0
for i in range(num_of_vertex - 1):
U = minimumDistance(distance, visited)
visited[U] = True
for j in range(num_of_vertex):
curr_distance = distance[U] + graph[U][j]
if not visited[j] and graph[U][j] and curr_distance & lt;
distance[j]: parent[j] = U
distance[j] = curr_distance
print("Vertex\t\tDistance\tPath")
for i in range(num_of_vertex):
print("{}->{}\t\t{}\t\t{}
".format(src + 1, i + 1, distance[i], src + 1), end = "")
printParentNode(parent, i)
print("")
graph = [
[0, 1, 7, 6, 0, 0, 0],
[1, 0, 9, 0, 0, 3, 0],
[7, 9, 0, 0, 0, 0, 1],
[6, 0, 0, 0, 2, 0, 0],
[0, 0, 0, 2, 0, 0, 0],
[0, 3, 0, 0, 0, 0, 3],
[0, 0, 0, 0, 5, 3, 0]
]
dijkstra(graph, 0)
Produzione:
Vertex Distance Path 1->1 0 1 1->2 1 1 2 1->3 7 1 3 1->4 6 1 4 1->5 8 1 4 5 1->6 4 1 2 6 1->7 7 1 2 6 7
Possiamo vedere che l'algoritmo calcola la distanza piรน breve dal nodo sorgente.
Applicazione dell'algoritmo di Dijkstra
Il Dijkstra Algo ha una vasta gamma di utilizzi. Tra questi, รจ ampiamente utilizzato nel campo del networking. Ecco alcuni esempi di utilizzo nella vita reale dell'algoritmo di Dijkstra:
Dijkstra nella mappa di Google: Fondamentalmente, questo algoritmo รจ la spina dorsale per trovare i percorsi piรน brevi. Come possiamo vedere dall'output del frammento di codice sopra.
Google non utilizza il semplice algoritmo Dijkstra. Utilizza invece una versione modificata. Quando selezioni una destinazione, ti mostra piรน percorsi nella mappa di Google. Tra questi percorsi, alcuni percorsi sono smistati per l'utente. Questi percorsi selezionati sono basati sul โtempoโ. Quindi, il โtempoโ รจ un costo vantaggio per il percorso piรน breve.
Dijkstra nel routing IP: Instradamento IP รจ una terminologia di rete. Significa come il tuo pacchetto di dati viene inviato al ricevitore attraverso percorsi diversi. Questi percorsi sono costituiti da router, server, ecc. Nel routing IP esistono diversi tipi di protocolli.
Questi protocolli aiutano il router a trovare i percorsi piรน brevi per inviare i dati. Uno dei nomi dei protocolli รจ "OSPF (Open Shortest Path First)". OSPF utilizza l'algoritmo di Dijkstra. Il router mantiene una tabella di percorsi. Ogni router condivide la sua tabella con i router vicini. Dopo aver ricevuto la tabella aggiornata, devono calcolare di nuovo tutti i percorsi. A quel punto, il router utilizza l'algoritmo di Dijkstra.
Limitazione dell'algoritmo di Dijkstra
L'algoritmo Dijkstra non puรฒ garantire il percorso piรน breve nel grafico del bordo negativo. L'algoritmo di Dijkstra segue queste metodologie:
- Da un nodo all'altro verrร preso il percorso piรน breve.
- Una volta selezionato il percorso piรน breve tra due nodi, questo non verrร ricalcolato.
Qui, notate due esempi con bordi negativi.
Nel grafico a sinistra, ci sono tre vertici. Dijkstra eseguirร il grafico come segue:
Passo 1) Il vertice iniziale โ1โ verrร inizializzato a zero. Gli altri nodi avranno infinito.
Passo 2) Contrassegna il nodo โ1โ come visitato e includilo nel percorso piรน breve.
Passo 3) La distanza del nodo sorgente 1 dai nodi โ2โ e โ3โ รจ impostata su infinito, poichรฉ il percorso piรน breve deve ancora essere calcolato. Pertanto, qualsiasi percorso che costerร meno dell'infinito verrร aggiunto al percorso piรน breve (approccio goloso).
Passo 4) Aggiornamento della distanza dal vertice di origine o dal nodo di origine da โ1โ a โ2โ. Il peso attuale sarร 5 (5
Passo 5) Ora, se controlliamo le distanze piรน brevi dal nodo โ1โ, troviamo che 5 รจ la distanza piรน breve per il bordo 1โ> 2. Quindi, il nodo โ2โ verrร contrassegnato come visitato.
Allo stesso modo, anche il nodo โ3โ verrร contrassegnato come visitato poichรฉ la distanza piรน breve รจ 3.
Tuttavia, se osserviamo, c'รจ un percorso 1-3-2 che costerร solo 2. Ma Dijkstra mostra che dal nodo "1" al nodo "2", la distanza piรน breve รจ 5. Quindi, Dijkstra non รจ riuscita a calcolare il percorso piรน breve distanza correttamente. Il motivo per cui รจ successo รจ che Dijkstra รจ un algoritmo avido. Pertanto, una volta che un nodo viene contrassegnato come visitato, non verrร riconsiderato, anche se potrebbe essere disponibile un percorso piรน breve. Questo problema si verifica solo quando i bordi hanno costi negativi o bordi con peso negativo
Dijkstra non riesce a calcolare il percorso piรน breve tra due nodi in questo scenario. Di conseguenza, questo algoritmo presenta alcuni inconvenienti. Per risolvere questo problema del bordo negativo, viene utilizzato un altro algoritmo chiamato โAlgoritmo Bellman-Fordโ. Questo algoritmo puรฒ funzionare con bordi negativi.
Complessitร dell'algoritmo di Dijkstra
L'implementazione sopra ha utilizzato due cicli "for". Questi cicli vengono eseguiti per il numero di vertici. Quindi, la complessitร temporale รจ O(V2). Qui, il termine โ0โ indica una notazione che fornisce un presupposto per lโalgoritmo di Dijkstra.
Possiamo memorizzare il grafico utilizzando la โCoda prioritariaโ. Una coda con prioritร รจ una struttura di dati heap binaria. Sarร piรน efficiente di una matrice 2D. Un vantaggio che avrร un costo minimo avrร una prioritร alta.
Quindi la complessitร temporale sarร O(E log(V)). Dove E รจ il numero di spigoli e V รจ il numero di vertici.
La complessitร dello spazio รจ O(V2), poichรฉ stiamo utilizzando una matrice di adiacenza (matrice 2D). La complessitร dello spazio puรฒ essere ottimizzata utilizzando una lista di adiacenza o una struttura dati di coda.












