L'algorithme de Dijkstra dans Python & C++ (Exemple)
โก Rรฉsumรฉ intelligent
L'algorithme de Dijkstra calcule le chemin le plus court entre un sommet source et tous les autres sommets d'un graphe pondรฉrรฉ dont les arรชtes sont non nรฉgatives. Cette mรฉthode gloutonne sous-tendโฆ Google Routage cartographique, routage IP OSPF et d'innombrables cas d'utilisation du chemin le plus court sur le rรฉseau.

Quel est le chemin le plus court ou la distance la plus courte ?
Le chemin le plus court, ou chemin de distance minimale, relie un sommet source ร un sommet destination. En thรฉorie des graphes, plusieurs chemins sont possibles entre une source et une destination. Parmi ces chemins, s'il en existe un dont le coรปt est minimal, on l'appelle le chemin le plus court.
Ici, le terme ยซ coรปt ยป dรฉsigne le nombre de nลuds du chemin ou la somme des coรปts de chaque arรชte. Un chemin peut comporter une ou plusieurs arรชtes. La connexion entre deux sommets est appelรฉe ยซ arรชte ยป. Il existe diffรฉrents algorithmes de calcul du plus court chemin, tels que l'algorithme de Dijkstra et l'algorithme de Bellman-Ford.
Nous allons ici aborder l'algorithme de Dijkstra. Prenons l'exemple du graphe pondรฉrรฉ suivant :
Un graphique non pondรฉrรฉ
- Le terme ยซ pondรฉrรฉ ยป dรฉsigne le coรปt du dรฉplacement d'un nลud ร un autre. Par exemple, pour passer du nลud 1 au nลud 2, le coรปt ou poids est de 1.
- Le chemin entre le nลud 1 et le nลud 2 est appelรฉ l'arรชte.
- ยซ Non orientรฉ ยป signifie que vous pouvez passer d'un nลud ร un autre et revenir au nลud prรฉcรฉdent. Ainsi, si nous cherchons tous les itinรฉraires du nลud 1 au nลud 7, ils seront :
| Itinรฉraire ou chemin | Prix |
|---|---|
| 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 |
Parmi ces quatre itinรฉraires, on constate que le premier coรปte 7. C'est donc le chemin le plus court en termes de coรปt.
Le plus court chemin
Comment fonctionne l'algorithme de Dijkstra
L'algorithme de Dijkstra permet de trouver le chemin le plus court dans les graphes pondรฉrรฉs, orientรฉs ou non. Cet algorithme est dit glouton car il choisit systรฉmatiquement le nลud le plus proche de l'origine. Le terme ยซ glouton ยป signifie que, parmi plusieurs rรฉsultats possibles, l'algorithme sรฉlectionne le meilleur.
Ici, nous cherchons ร trouver les chemins les plus courts parmi tous les itinรฉraires possibles. L'algorithme de Dijkstra trouve donc tous les chemins les plus courts ร partir d'un nลud source unique. Par consรฉquent, il se comporte comme unโฆ algorithme gourmand.
Dans la section ยซ exemple ยป ci-dessous, vous trouverez la dรฉmarche รฉtape par รฉtape. Elle fonctionne comme suit :
รtape 1) Initialisez le nลud de dรฉpart avec un coรปt de 0 et les autres nลuds avec un coรปt infini.
รtape 2) Conservez un tableau ou une liste pour conserver track des nลuds visitรฉs.
รtape 3) Mettez ร jour le coรปt du nลud avec le coรปt minimal. Cela peut se faire en comparant le coรปt actuel avec le coรปt du chemin (comme illustrรฉ dans l'exemple).
รtape 4) Poursuivez l'รฉtape 3 jusqu'ร ce que tous les nลuds soient visitรฉs.
Aprรจs avoir complรฉtรฉ toutes ces รฉtapes, nous trouverons le chemin qui coรปte le moins cher de la source ร la destination.
Diffรฉrence entre Dijkstra et BFS, DFS
La principale diffรฉrence entre l'algorithme de Dijkstra et les algorithmes BFS et DFS rรฉside dans leur nature : Dijkstra est un algorithme de recherche du plus court chemin, tandis que BFS et DFS sont des algorithmes de recherche de chemin gรฉnรฉral. En rรจgle gรฉnรฉrale, BFS et DFS ne tiennent pas compte du coรปt des arรชtes lors de la recherche du chemin. Par consรฉquent, ces algorithmes ne garantissent pas de trouver le chemin le plus court.
Dรฉmonstration sur une grille 2D du fonctionnement de la recherche en largeur (BFS)
Algosketch, montrant la dรฉmonstration BFS
Cette dรฉmonstration indique que BFS trouve uniquement le chemin. Toutefois, il ne se soucie pas du poids du chemin. BFS (Recherche en largeur d'abord) suppose que voyager dโun nลud ร un autre ne coรปtera que 1.
Prenons l'exemple d'un graphique :
Ici, le parcours en largeur (BFS) trouve un chemin au niveau 2. Le parcours en largeur parcourt le graphe niveau par niveau. Il se dรฉroule donc comme suit :
รtape 1) Commencez par le nลud ยซ 1 ยป et visitez tous les nลuds adjacents 2, 3, 4.
รtape 2) Marquez les nลuds 2, 3 et 4 comme รฉtant de niveau 1 et visitez leurs nลuds adjacents. L'exploration se poursuivra jusqu'ร atteindre le nลud de destination.
En termes de DFS, il parcourra le chemin de 1 ร 7 comme suit :
- 1 โ 2 โ 3 โ 7 (coรปt initial 10, coรปt DFS 3)
- 1 โ 2 โ 6 โ 7 (coรปt initial 7, coรปt DFS 3)
- 1 โ 3 โ 7 (coรปt initial 8, coรปt DFS 2)
- 1 โ 4 โ 5 โ 7 (coรปt initial 13, coรปt DFS 3)
Comme on le voit, le parcours en profondeur (DFS) calcule le coรปt de son chemin en fonction du nombre d'arรชtes. Le DFS procรจde comme suit :
- DFS peut trouver un chemin depuis la source (sommet de dรฉpart) jusqu'ร la destination.
- Il ne peut pas garantir si le chemin dรฉcouvert du nลud source ร la destination est le chemin le plus court ou non.
Cependant, l'algorithme de Dijkstra sรฉlectionne les arรชtes en fonction de leur coรปt. รtant un algorithme glouton, il choisit les chemins de coรปt minimal.
Exemple de l'algorithme de Dijkstra
L'algorithme de Dijkstra utilise le coรปt ou le poids pour calculer le coรปt total du chemin.
L'objectif de l'algorithme de Dijkstra est de minimiser ce coรปt ou ce poids total. Dans l'exemple ci-dessus, nous trouvons les meilleurs chemins du nลud 1 au nลud 7, puis calculons tous les coรปts.
L'algorithme de Dijkstra trouve les plus courts chemins en calculant des poids. Il ne recherche pas tous les chemins possibles. Prenons un exemple pour illustrer son fonctionnement : supposons que l'on vous demande de trouver le plus court chemin du nลud 1 au nลud 7.
Pour ce processus, les รฉtapes sont indiquรฉes ci-dessous :
รtape 1) Initialisez le coรปt du nลud de dรฉpart ร 0. Attribuez ยซ Inf ยป pour les autres nลuds. Cela signifie qu'il n'existe aucun chemin entre la source et le nลud, ou que ce chemin n'a pas encore รฉtรฉ parcouru.
รtape 2) Lorsque vous sรฉlectionnez le nลud 1, il est marquรฉ comme visitรฉ. Mettez ensuite ร jour tous les nลuds voisins du nลud 1. Les nลuds 2, 3 et 4 sont les nลuds voisins du nลud 1.
Lors de la mise ร jour d'un coรปt, nous devons suivre la procรฉdure ci-dessous :
Nous pouvons mettre ร jour le coรปt de chaque nลud ร l'aide de la formule ci-dessus. Par exemple, nous รฉtions au nลud 1 et nous devions mettre ร jour le coรปt de ses nลuds adjacents 2, 3 et 4. Aprรจs la mise ร jour, les coรปts se prรฉsenteront comme suit :
รtape 3) Pour le nลud ยซ 2 ยป, les voisins sont 6 et 3. Nous mettons ร jour le coรปt du nลud ยซ 6 ยป en comparant lโinfini (valeur actuelle) avec le coรปt du nลud 2 plus le coรปt du chemin de 2 ร 6. Autrement dit, le nลud ยซ 6 ยป aura un coรปt de 1+3, soit 4.
Le nลud 3 est voisin du nลud 2. Cependant, nous avons calculรฉ son coรปt ร l'รฉtape prรฉcรฉdente, qui รฉtait de 7. Maintenant, si notre chemin est 1-2-3, le nลud 3 aura un coรปt de 10. Chemin 1-2- 3 coรปtera 10, tandis que 1 ร 3 coรปtera 7.
รtape 4) Pour le nลud 3, le nลud voisin est 7. En comparant la valeur actuelle du nลud 7 avec le coรปt du chemin (7+1), soit 8, on met ร jour le coรปt du nลud 7, qui est de 8. On trouve donc un chemin du nลud 1 au nลud 7 : 1โ3โ7. Son coรปt est de 8.
รtape 5) Pour le nลud 4, nous mettrons ร jour le coรปt de son nลud adjacent en consรฉquence. Ainsi, le coรปt du nลud ยซ 5 ยป sera mis ร jour ร 8. Aprรจs les รฉtapes 4 et 5, le rรฉsultat sera le suivant :
Le chemin 1-3-7 a un coรปt de 8 (prรฉcรฉdemment). Le nลud 7 n'a pas รฉtรฉ marquรฉ comme visitรฉ car il est accessible depuis le nลud 6. Le chemin 1-2-6 avait un coรปt de 4. Le chemin 1-2-6-7 aura donc un coรปt de 7.
Comme 7 < 8, le chemin le plus court du sommet source ยซ 1 ยป au sommet destination ยซ 7 ยป sera 1-2-6-7, et son coรปt est de 7. Auparavant, il รฉtait 1-3-7, et son coรปt รฉtait de 8. Le graphe final ressemblera donc ร ceci :
Le bord marquรฉ d'une ligne noire est notre chemin le plus court de 1 ร 7, et il nous en coรปtera 7.
Faux Code L'algorithme de Dijkstra
Voici le pseudo-code de l'algorithme de Dijkstra :
Dijkstra(G, S): for each vertex V in G distance[V] <- Infinity previous[V] <- NULL if V does not equal S, then, (priority queue) Q.push(V) distance[S] = 0 While Q is not empty U <- Extract the MIN from Q For each unvisited adjacent V of U TotalDistance <- distance[U] + edge_cost(U, V) if TotalDistance is less than distance[V], then distance[V] <- TotalDistance previous[V] <- U return distance, previous
C++ Implรฉmentation de l'algorithme de Dijkstra
Pour implรฉmenter l'algorithme de Dijkstra en utilisant C++Voici le code :
#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] && 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] && graph[U][j] && 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); }
Sortie :
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 Implรฉmentation de l'algorithme de Dijkstra
Pour implรฉmenter l'algorithme de Dijkstra en utilisant PythonVoici le code :
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] <= _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 < 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)
Sortie :
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
On peut constater que l'algorithme calcule la distance la plus courte ร partir du nลud source.
Application de l'algorithme de Dijkstra
L'algorithme de Dijkstra a de nombreuses applications. Parmi celles-ci, il est largement utilisรฉ dans le domaine des rรฉseaux. Voici quelques exemples concrets d'utilisation de l'algorithme de Dijkstra :
Dijkstra dans Google Plans: Cet algorithme constitue la base de la recherche des chemins les plus courts, comme nous pouvons le constater dans l'extrait de code ci-dessus.
Google n'utilise pas l'algorithme de Dijkstra simple. Il utilise plutรดt une version modifiรฉe. Lorsque vous sรฉlectionnez une destination, il vous affiche plusieurs chemins possibles. Google Cartes. Parmi ces itinรฉraires, certains sont triรฉs pour l'utilisateur. Ces itinรฉraires sont sรฉlectionnรฉs en fonction du ยซ temps ยป. Ainsi, le ยซ temps ยป reprรฉsente le coรปt du parcours le plus court.
Dijkstra dans le routage IP : Routage IP Le terme ยซ routage ยป dรฉsigne un terme de rรฉseau. Il dรฉcrit comment un paquet de donnรฉes est acheminรฉ jusqu'ร son destinataire via diffรฉrents chemins. Ces chemins comprennent des routeurs, des serveurs et d'autres รฉquipements. Le routage IP utilise diffรฉrents protocoles.
Ces protocoles aident le routeur ร trouver les chemins les plus courts pour acheminer les donnรฉes. L'un d'eux est OSPF (Open Shortest Path First). OSPF utilise l'algorithme de Dijkstra. Le routeur maintient une table de routes. Chaque routeur partage sa table avec ses voisins. Aprรจs rรฉception de la table mise ร jour, il doit recalculer tous les chemins. C'est alors que le routeur utilise l'algorithme de Dijkstra.
Limitation de l'algorithme de Dijkstra
L'algorithme de Dijkstra ne garantit pas le chemin le plus court dans un graphe comportant des arรชtes nรฉgatives. Il repose sur les principes suivants :
- Un chemin le plus court sera empruntรฉ dโun nลud ร un autre.
- Une fois le chemin le plus court entre deux nลuds sรฉlectionnรฉ, il ne sera plus calculรฉ.
Ici, remarquez deux exemples avec des bords nรฉgatifs.
Dans le graphique de gauche, Il y a trois sommets. L'algorithme de Dijkstra s'exรฉcutera sur le graphe comme suit :
รtape 1) Le sommet de dรฉpart ยซ 1 ยป sera initialisรฉ ร zรฉro. Les autres nลuds auront l'infini.
รtape 2) Marquez le nลud ยซ 1 ยป comme visitรฉ et incluez-le dans le chemin le plus court.
รtape 3) La distance entre le nลud source 1 et les nลuds 2 et 3 est initialement fixรฉe ร l'infini, car le chemin le plus court n'a pas encore รฉtรฉ calculรฉ. Par consรฉquent, tout chemin de coรปt infรฉrieur ร l'infini sera ajoutรฉ au chemin le plus court (approche gloutonne).
รtape 4) Mise ร jour de la distance entre le sommet source ยซ 1 ยป et le sommet ยซ 2 ยป. Le poids actuel est de 5 (5 < โ). De mรชme, mise ร jour de la distance entre le nลud ยซ 1 ยป et le sommet ยซ 3 ยป avec un poids de 3.
รtape 5) Si l'on vรฉrifie les distances les plus courtes ร partir du nลud ยซ 1 ยป, on constate que 5 est la distance la plus courte pour l'arรชte 1โ2. Le nลud ยซ 2 ยป sera donc marquรฉ comme visitรฉ. De mรชme, le nลud ยซ 3 ยป sera รฉgalement marquรฉ comme visitรฉ, car la distance la plus courte est de 3.
Cependant, on constate qu'il existe un chemin 1-3-2 dont le coรปt est de seulement 2. Or, l'algorithme de Dijkstra indique que la distance la plus courte entre le nลud ยซ 1 ยป et le nลud ยซ 2 ยป est de 5. Par consรฉquent, Dijkstra n'a pas calculรฉ correctement la distance la plus courte. Ceci s'explique par le fait que Dijkstra est un algorithme glouton. Ainsi, une fois qu'un nลud est marquรฉ comme visitรฉ, il n'est plus considรฉrรฉ, mรชme s'il existe un chemin plus court. Ce problรจme survient uniquement lorsque les arรชtes ont un coรปt ou un poids nรฉgatif.
Dans ce cas prรฉcis, l'algorithme de Dijkstra ne parvient pas ร calculer le chemin le plus court entre deux nลuds. Il prรฉsente donc certaines limitations. Pour rรฉsoudre ce problรจme d'arรชtes nรฉgatives, on utilise un autre algorithme, appelรฉ ยซ algorithme de Bellman-Ford ยป, capable de traiter les arรชtes nรฉgatives.
Complexitรฉ de l'algorithme de Dijkstra
L'implรฉmentation ci-dessus utilisait deux boucles ยซ for ยป. Ces boucles s'exรฉcutent pour le nombre de sommets. La complexitรฉ temporelle est donc O(Vยฒ)Ici, le terme ยซ O ยป est une notation qui donne une hypothรจse pour l'algorithme de Dijkstra.
On peut stocker le graphe ร l'aide d'une file de prioritรฉ. Une file de prioritรฉ est une structure de donnรฉes de type tas binaire. Elle sera plus efficace qu'une matrice 2D. Une arรชte de coรปt minimal aura une prioritรฉ รฉlevรฉe. La complexitรฉ temporelle sera alors de O(n log n). O(E log V). Ici, E est le nombre dโarรชtes et V est le nombre de sommets.
La complexitรฉ de l'espace est O(Vยฒ), car nous utilisons une matrice de contiguรฏtรฉ (tableau 2D). La complexitรฉ de l'espace peut รชtre optimisรฉe ร l'aide d'une liste de contiguรฏtรฉ ou d'une structure de donnรฉes de file d'attente.















