Nous retrouvons cet algorithme sous le nom de Ford-Bellman, ou aussi Bellman-Ford.
Nous sommes à la recherche du chemin le plus court. Cela signifie que la somme des poids des arcs du chemin sélectionné doit être le plus faible possible.
L'algorithme de Ford-Bellman est différent des algorithmes que nous avons vu jusqu'à présent (Bellman-Kalaba, Moore-Dijkstra), qui nous permettaient une exploration progressive, sans back-tracking[2].
Ford-Bellman remet en cause les résultats précédents car il accepte les circuits et les pondérations quelconques (alors que Moore-Dijkstra, s'il accepte les circuits, n'accepte pas de pondérations négatives).
La présence de pondérations négatives dans un graphe avec circuits ne permet plus de marquer définitivement un sommet, car le passage par un arc négatif décroît la valeur du chemin à chaque passage par ce circuit.
Le principe général est le même que pour le relâchement dans Moore-Dijkstra : nous utiliserons une valeur tellement haute[3] que tout au long de l'exécution de l'algorithme nous ne devrons jamais définir de nouvelles valeurs qui lui soient égales ou supérieures.
Nous diminuerons (relâcherons) cette valeur à chaque fois que nous trouverons une valeur plus intéressante.
Les résultats ne sont pas définitifs, et sont remis en cause à chaque itération si un chemin plus court permet d'atteindre un sommet.
A chaque passage dans la boucle, nous devons trouver pour le sommet x les arcs incidents à x vers l'intérieur[4], arcs (y -> x).
Nous calculons ensuite le coût (le poids) nécessaire pour atteindre x en passant par y. Ce coût (r -> y -> x) est calculé en ajoutant à la pondération de l'arc (y -> x), le poids du chemin de la racine vers y.
Si le poids minimum calculé pour x et tout y possible est inférieur à l'ancien poids (r -> x), nous devons remplacer ce poids par la nouvelle valeur, et mémoriser (par l'étiquette) que y est le meilleur précédent actuel pour atteindre x car nous vennons de découvrir un chemin plus court (de poids inférieur) de la racine vers x.
Nous pouvons décomposer notre algorithme en deux phases : une phase d'initialisation des valeurs, et une phase d'exécution qui décrit ce qui se passe lorsque l'algorithme est exécuté.
Dans cette approche, nous utilisons des collections indexées (par exemple des tableaux) pour maintenir les différentes informations relatives aux états de chaque objet Sommet. Dans une approche "orienté-objet", nous pouvons maintenir ces informations par exemple dans l'objet Sommet lui même.
Nous utiliserons aussi certaines variables supplémentaires :
arrayFirstIndex : indice du premier élément dans un tableau[5].
//Nombre maximum, sinon ∃ un circuit absorbant //pour tout sommet excepté la racine /*j est l'indice de y ∃ y lié à x par un arc et le coût de y est ≤ au coût de tous les arcs*/ /*le poids temporaire de x est le minimum des poids des chemins pour atteindre x en passant par chaque y*/ pathTempWeights[i] := minimum{(pathMinWeights[j]+weight(i,j))∀j}; //poids plus court, amélioration //adaptation de l'étiquette verticesLabels[i] := j; //si aucune amélioration n'a été détectée //arrêt du programme afficher("fin de la recherche"); //si il reste des sommets à traiter //on diminue le nombre de passages restant k := k+1; /*des amélioration ont été détectées on met à jour le tableau des coûts*/ pathMinWeights[i] := pathTempWeights[i]; //arrêt du programme afficher("Détection de circuit absorbant");
A la ligne 2, le test de comparaison k < n s'applique aux langages pour lesquels le premier indice d'un tableau est 0. Dans le cas où le premier indice est 1, nous devons remplacer le test par k ≤ n.
La condition ∧ pathMinWeights[j] ≤ maxCost n'est pas strictement nécessaire (car les mises à jour ne se font que lorsqu'une optimisation est détectée), mais elle permet d'éviter de prendre en compte inutilement certaines valeurs lors de la recherche de minimum.
Le pseudo code si ∃ y : (y,x) ∈ A ∧ pathMinWeights[j] ≤ maxCost alors correspond en réalité à un pour_tout car nous devons itérer tous les arcs candidats.
//Nombre maximum, sinon ∃ un circuit absorbant //si modified=true, cela signifie qu'une optimisation a été détectée //pour tout sommet excepté la racine //j est l'indice de y, précédent de x /*le poids pour atteindre x en passant par ce sommet y*/ pathTempWeight := pathMinWeights[j]+weight(i,j); //poids plus court, amélioration //mise à jour de la meilleur valeur pathMinWeights[i] := pathTempWeight; //adaptation de l'étiquette verticesLabels[i] := j; //Au moins une améliorationa été détectée //on diminue le nombre de passages restant k := k+1; //Aucune amélioration n'a été détectée //arrêt du programme afficher("fin de la recherche"); /*Si un chemin avait été trouvé, le programme se serait arêté avant ce point*/ afficher("Détection de circuit absorbant");
Dans cette version, nous ne vérifions plus la valeur de k en cours d'itération. Si aucune amélioration n'est plus possible avant de sortir de la boucle principale, l'algorithme s'arrête de lui même.
Si nous arrivons au terme de la boucle principale sans quitter, c'est que nous sommes en présence d'un circuit absorbant.
Dans la version de base, nous devons parcourir tout le tableau des poids si ∀j : pathTempWeights[j]=pathMinWeights[j] alors, alors que nous sommes déjà dans une boucle, ce qui est très coûteux.
Une variable modified nous permet de détecter si les poids ont été mis à jour. Nous ne devons plus utiliser un tableau pathTempWeights, ni itérer ce dernier pour mettre à jour le tableau des poids. La variable modified sert donc de condition de sortie de l'algorithme, au méme titre que la boucle bornée de départ.
Lorsque nous ne connaissons pas précisément la destination, et que nous sommes en présence de circuits et de pondérations quelconques, nous operons pour l'algorithme de Ford-Bellman, car des circuits avec pondérations négatives risquent de former des circuits absorbants.
Si nous sommes en possession de plus d'informations, nous nous dirigerons vers un algorithme de type heuristique A*, qui limitera l'exploration à une direction qui correspond à la destination.
Une variante de Ford-Bellman est utilisée dans les protocoles de routage à vecteur-distance, comme par exemple RIP (Routing Information Protocol).
Vous pouvez modifier vos préférences dans votre profil pour ne plus afficher les interactions avec les réseaux sociaux sur ces pages.
32 mots clés dont 25 définis manuellement (plus d'information...).
Vous pouvez modifier vos préférences dans votre profil pour ne plus afficher le nuage de mots clés.
Cours de Théorie des Graphes et réseaux de Petri(Septembre 2008)
L'algorithme de Bellman-Ford et ses variantes(28/12/09)
Démonstration interactive par applet Java(version 28/12/09)
Ces références et liens indiquent des documents consultés lors de la rédaction de cette page, ou qui peuvent apporter un complément d'information, mais les auteurs de ces sources ne peuvent être tenus responsables du contenu de cette page.
L'auteur de ce site est seul responsable de la manière dont sont présentés ici les différents concepts, et des libertés qui sont prises avec les ouvrages de référence. N'oubliez pas que vous devez croiser les informations de sources multiples afin de diminuer les risques d'erreurs.
Recherche (afficher)
Utilisateur (masquer)
Navigation (masquer)
Apparence (afficher)
Stats (afficher)
Citation (masquer)