Les algorithmes de Dijkstra et Bellman-Ford
Page Wikipedia de l'algorithme de Dijkstra

Pour trouver les routes les plus courtes depuis A vers tous les autres sommets (ou ville dans la version originale de Dijkstra), nous choisissons Ă chaque tour de boucle while:
- la ville non encore choisie qui a la plus courte distance depuis A
et nous mettons à jour toutes les distances qui sont diminuées en passant par une route depuis la ville choisie.
Ainsi à chaque tour de boucle, un sommet/ville est ajouté à l'ensemble grossissant.
La boucle continue jusqu'à épuisement des sommets/ villes.
Voici une implémentation en Python:
| Python |
|---|
| graphe = {
"A": [("B", 85), ("C", 217), ("E", 173)],
"B": [("A", 85), ("F", 80)],
"C": [("A", 217), ("G", 186), ("H", 103)],
"D": [("H", 183)],
"E": [("A", 173), ("J", 502)],
"F": [("B", 80),("I",250)],
"G": [("C", 186)],
"H": [("C", 103), ("D",183),("J",167)],
"I": [("F", 250), ("J", 84)],
"J": [("E", 502), ("I", 84),("H",167)]
}
def dijkstra(graphe, depart):
# Retourne un dictionnaire des distances minimales pour tous les sommets accessibles du graphe
ensemble_grossisant = {depart:0} # Va contenir tous les sommets accessibles et la distance minimale
distances = {sommet:float('inf') for sommet in graphe}
distances[depart] = 0 # le premier sommet qui sera sorti l'ensemble des sommets
while any(distances[s] != float('inf') for s in distances):
choix = min([sommets for sommets in distances], key= lambda x: distances[x])
print(choix)
dist_min_ajout = distances.pop(choix) # contient le couple sommet choisi, et distance minimale depuis depart
ensemble_grossisant[choix] = dist_min_ajout
# Mise Ă jour des distances des voisins du choix
for couple in graphe[choix]:
voisin, dist_voisin = couple
if (voisin in distances) and distances[voisin] > dist_min_ajout + dist_voisin:
distances[voisin] = dist_min_ajout + dist_voisin
return ensemble_grossisant
print(dijkstra(graphe,'A'))
|
Trouver par quel chemin passer...
L'algorithme précédent permet de déterminer les distances minimales depuis un sommet (ou une ville) donné, à condition que les poids des arêtes soient positifs.
Cependant, pour qu'il soit utilisable dans un système de routage (comme sur Internet), il manque une information essentielle : le chemin parcouru pour atteindre la destination.
Pour remédier à cela, nous allons adapter l'algorithme afin que chaque sommet ajouté à l'ensemble des distances minimales soit associé au sommet précédent depuis lequel cette distance minimale a été trouvée.
Ainsi, en remontant les sommets (ou villes) du parcours optimal, nous pourrons retrouver l'itinéraire exact emprunté depuis la ville de départ.
👉 C'est ainsi que les routeurs déterminent la passerelle vers laquelle un paquet doit être envoyé pour atteindre sa destination finale.
Exercice
Adapter le code précédent pour retourner dans l'ensemble grossissant, non plus des couples (sommet, distance optimale) mais des 3-uplets (sommet, distance optimale, voisin d'origine du parcours optimal)
Code à compléter:
| Python |
|---|
| def dijkstra(graphe, depart):
# Retourne un dictionnaire des distances minimales pour tous les sommets accessibles du graphe
ensemble_grossisant = {depart:(0,depart)} # Va contenir tous les sommets accessibles et la distance minimale
distances = {sommet:(float('inf'),sommet) for sommet in graphe}
distances[depart] = (0,depart) # le premier sommet qui sera sorti l'ensemble des sommets
while any(distances[s][0] != float('inf') for s in distances):
choix = min([sommets for sommets in distances], key= lambda x: distances[x][0])
dist_min_ajout, voisin_orig = distances.pop(choix) # contient le couple sommet choisi, et distance minimale depuis depart
ensemble_grossisant[choix] = (...,...) # ajout de la distance trouvée et du voisin qui précède.
# Mise Ă jour des distances des voisins du choix
for couple in graphe[choix]:
voisin, dist_voisin = couple
if (voisin in distances) and distances[voisin][0] > ....:
distances[voisin] = ....
return ensemble_grossisant
|
Le résultat à trouver avec le graphe en exemple est :
{'A': (0, 'A'), 'B': (85, 'A'), 'F': (165, 'B'), 'E': (173, 'A'), 'C': (217, 'A'), 'H': (320, 'C'), 'G': (403, 'C'), 'J': (675, 'E'), 'I': (759, 'J')}
Algorithme de Bellman-Ford
L'algorithme de Bellman-Ford calcule, étant donnés un graphe sans cycle de poids négatif et un sommet source (depart), un plus court chemin de depart à chaque sommet de graphe.
Pour cela, il détermnine à chaque tour de boucle si une arête permet de diminuer (relaxer) une distance entre deux sommets:
Exercice
Complète le code:
| Python |
|---|
| def BellmanFord(graphe, depart):
distances = {sommet:float('inf') for sommet in graphe}
distances[depart] = 0
# Répétition de la boucle autant de fois qu'il y'a de sommets moins 1
for _ in range(...):
# Parcours de toutes les arĂŞtes
for sommet in graphe:
for voisin, dist_voisin in graphe[sommet]:
if distances[voisin] > ...:
...
return distances
|