La recherche de chemin optimal dans un graphe Un problème d’optimisation 1) Introduction 2) Définitions 3) Problème et méthode Solutions au problème 1) Relâchement d’une arête 2) Algorithme de Bellman-Ford 3) Algorithme de Dijkstra
I.1 Position du problème Une carte de 25 villes, reliées 2 à 2 entre elles Nombre de chemins de longueur 24 reliant la ville A à la ville B : 23! > 2*10^22 Supercalculateur Téra-10 (CEA) : 50 téraflops (50*10^12 op/sec) En supposant 1 calcul de chemin = 1 opération : 4*10^8 s > 12 années de calcul France : 36000 communes
I.2 Définitions Graphe : G = (S, A) S ensemble des sommets = [| 0 ; n -1 |] A ensemble des arêtes : A C S² w : A -> R fonction de pondération Poids d’une arête (u,v) : w (u,v) Chemin : suite (so,…, sn ) de sommets tels que pour i Є [| 0 ; n |] (si, si+1) Є A Les si sont dits accessibles depuis s0. Poids d’un chemin = somme des poids de ses arêtes Circuit absorbant = circuit de poids strictement négatif
I.3 Problème et méthodes Problème : pour (s, t ) Є S ² , déterminer un chemin de poids minimal de s à t s’il en existe un. Équivalent à : pour s Є S, déterminer pour tout sommet t Є S accessible depuis s un chemin de poids minimal de s à t . -> création d’une arborescence de racine s Méthode : attribution de poids provisoires à tous les sommets (initialement ∞, sauf pour s : 0 ) que l’on fait décroître progressivement. On associe à chaque sommet t un prédécesseur (initialement NIL ) qui sera le sommet précédant t dans le chemin minimal de s à t.
II.1 Relâchement d’une arête Si possible, diminue le poids provisoire du sommet d’arrivée en notant son prédécesseur. 2 4 8 1er cas Relâchement : diminution du poids 2 4 6 2 5 6 Relâchement : pas de changement 2ème cas 2 5 6 II.2 Algorithme de Bellman-Ford : Programmation dynamique, |S| - 1 fois le relâchement de toutes les arêtes. Fonctionne pour les poids négatifs, détecte les circuits absorbants. Complexité : O(S*A)
II.2 Algorithme de Bellman-Ford Fonctionne toujours, détecte les circuits absorbants Faire |S| -1 fois : relâcher toutes les arêtes du graphe Si certaines arêtes peuvent encore être relâchées, alors il y a un circuit absorbant 4 5 -2 3 -1 2 1 ∞ 4 5 -2 3 -1 2 1 ∞ 2 4 5 -2 3 -1 2 1 ∞ 4 5 -2 3 -1 2 1 6 4 5 -2 3 -1 2 1 6 Complexité : O(S*A)
II.3 Algorithme de Dijkstra Seulement si les poids sont positifs ou nuls. Stratégie « gloutonne ». F ensemble des sommets à traiter (initialement S). Tant que F ≠ Ø faire extraire de F le sommet u de poids provisoire minimal relâcher toutes les arêtes partant de u 2 3 4 1 ∞ 2 3 4 1 ∞ 2 3 4 1 ∞ 2 3 4 1 5 2 3 4 1 5 Complexité : dépend de l’implémentation Liste : O(S ² + A ) = O( S ² ) Tas binaire : O( (S+A ) lg S ) Tas de Fibonacci : O( S lg S + A )
Tas binomiaux Bk+1 B0 Bk Bk B3 B1 B2 Nombre de fils max pour un nœud d’un tas binomial à n nœuds : D(n) = log n [2]
Tas de Fibonacci min 21 4 2 12 8 14 7 5 17 Nombre de fils max pour un nœud d’un tas de Fibonacci à n nœuds : D(n) = log n [Ф]
CONSOLIDER (T) Pour i 0 à D(n(T)) faire A(i) NIL Pour chaque nœud w de la liste des racines de T faire x w d degré(x) tant que A(d) ≠ NIL faire y A(d) si clé(x) > clé(y) alors permuter x et y RELIER_TAS (T,x,y) A(d) NIL d d+1 A(d) x Min(T) NIL faire si A(i) ≠ NIL alors ajouter A(i) à la liste des racines de T si min(T) = NIL ou clé(A(i)) < clé(min(T)) alors min(T) A(i) Supprimer y de la liste des racines de T Faire de y un enfant de x, incrémenter degré(x) Marque(y) faux
DIMINUER_CLE (T,x,k) Si k > cle(x) alors renvoyer une erreur clé(x) k y p(x) Si y ≠ NIL et clé(x) < clé(y) alors COUPER (T,x,y) Si clé(x) < clé(min(T)) alors min(T) x COUPER (T,x,y) Supprimer x de la liste des enfants de y Décrémenter degré(y) Ajouter x à la liste des racines de T p(x) NIL marque(x) faux z p(y) si z ≠ NIL alors si marque(y) = faux alors marque(y) = vrai sinon COUPER (T,y,z)