Chap02 Graphes Parcours - Student
Chap02 Graphes Parcours - Student
Chap02 Graphes Parcours - Student
1 Graphes et accessibilité
1.1 Définitions de base et vocabulaire
Un graphe est un objet mathématique permettant de modéliser une relation binaire sur un ensemble
fini. Selon que cette relation est asymétrique ou symétrique, le graphe sera orienté ou non.
Définition 1.1
Un graphe orienté G = (S, A) est composé d’un ensemble fini S d’éléments appelés sommets et
d’une partie A de S × S dont les éléments sont appelés arcs.
Définition 1.2
Si (x, y) est un arc d’un graphe orienté, on dit alors :
• que x et y sont les extrémités de cet arc (x l’origine et y la destination) ;
• que x est un prédécesseur de y et que x est un successeur de y.
De plus, si x = y, l’arc (x, x) est appelé une boucle.
Définition 1.4
Un graphe non orienté G = (S, A) est composé d’un ensemble fini S d’éléments appelés sommets
et d’un ensemble de paires de S dont les éléments sont appelés arêtes.
Remarque 1.7
Étant donné un graphe orienté, sa version non orientée est obtenue en supprimant les boucles et en sub-
stituant à chaque arc restant (x, y) la paire {x, y} ♣ . Réciproquement la version orientée d’un graphe non
orienté est obtenue en substituant à chaque arête {x, y} les deux arcs (x, y) et (y, x).
♣. Comme il n’y a pas de doublons dans un ensemble, l’arête {x, y} est présente au plus une fois, même si les arcs
(x, y) et (y, x) étaient tous deux présents dans le graphe initial.
Définition 1.9
Soit G = (S, A) un graphe non orienté (resp. orienté). Soit G0 = (S 0 , A0 ) un graphe non orienté
(resp. orienté). On dit que G0 est un sous-graphe de G si et seulement si S 0 ⊆ S, et A0 ⊆ A.
Définition 1.11
Soit G = (S, A) un graphe non orienté (resp. orienté). Soit W ⊆ S un sous-ensemble de sommets.
Le sous-graphe de G induit par W est le graphe G0 = (W, A0 ) où A0 = {{x, y} ∈ A | (x, y) ∈ W 2 })
(resp. A0 = {(x, y) ∈ A | (x, y) ∈ W 2 })).
Définition 1.14
Deux sommets distincts d’un graphe orienté G = (S, A) (resp. non orienté) G sont adjacents
ou voisins s’ils sont les extrémités d’un même arc (respectivement d’une même arête), i.e. si
(x, y) ∈ A (resp. {x, y} ∈ A).
On appelle voisinage d’un sommet x l’ensemble des sommets qui lui sont adjacents, et degré de
x (noté deg(x)) le nombre de tels sommets.
Si x est un sommet d’un graphe orienté, on appelle degré sortant de x (noté d+ (x)) le nombre
de successeurs de x, et degré entrant de x (noté d− (x)) son nombre de prédecesseurs.
Définition 1.17
Soit G = (S, A) un graphe orienté (resp. non orienté). Un chemin (resp. une chaîne) est une suite
finie non vide de sommets c = (s0 , . . . , sp ) telle que : s0 = x, sp = y et ∀k ∈ J0, p−1K, (sk , sk+1 ) ∈ A
(resp. {sk , sk+1 } ∈ A).
La longueur c est p, c’est le nombre d’arcs (resp. d’arêtes) empruntées par ce chemin (resp. cette
chaîne) comptés avec multiplicité.
On dit qu’un chemin (resp. une chaîne) c = (s0 , . . . , sp ) est élémentaire si et seulement si ses
sommets sont deux à deux distincts, i.e. ∀(i, j) ∈ J0, pK2, si = sj ⇒ i = j.
Remarque 1.19
La définition ci-dessus de cycle fixe une origine : s0 . Néanmoins on assimilera souvent comme étant un
même cycle les suites s0 , s1 . . . sp et s00 , s01 . . . s0p s’il existe k ∈ J1, p − 1K tel que ∀i ∈ J0, pK, s0i = si+k mod p .
Définition 1.22
Soit G = (S, A) un graphe non orienté (resp. orienté). On dit que :
• G est acyclique dès lors qu’il n’existe aucun cycle élémentaire ;
• G est complet dès lors que A = {{x, y} | (x, y) ∈ A2 } (resp. A = S × S)
Soit G = (S, A) un graphe non orienté, on dit que :
• G est biparti dès lors qu’il existe une partition {W1 , W2 } de ses sommets tel que ∀{u, v} ∈
A, (u, v) ∈ W1 × W2 ∪ W2 × W1 ;
• G est un arbre si et seulement si G est connexe ♣ et acyclique.
Proposition 1.26
La relation ∼ est une relation d’équivalence.
Remarque 1.28
Pour les graphes non orientés, la relation binaire ∼ peut être définie comme la clôture reflexive transitive
de la relation ∼ sur S définie par A, i.e. telle que ∀(u, v) ∈ S 2 , u ∼ v ↔ u, v ∈ A.
Définition 1.29
Soit ∼ la relation définie ci-avant. On appelle composantes connexes de G les classes d’équiva-
lences de ∼. Elles forment une partition de S ♣ .
3 4 5
Définition 1.31
Soit W ⊆ S,
• le graphe G est dit connexe si et seulement s’il admet une seule composante connexe ;
• l’ensemble W est dit connexe si et seulement le sous-graphe induit par G sur W est connexe.
Remarque 1.33
De manière équivalente on peut dire qu’un ensemble de sommets W ⊆ S est connexe si et seulement si les
sommets de W sont deux à deux reliés par une chaîne de G n’empruntant que des sommets de W .
Proposition 1.35
Soit W ⊆ S, W est une composante connexe de G si et seulement si W est connexe maximale
(au sens de ⊆).
Remarque 1.38
∗
On note parfois u → v pour signifier ”v est accessible depuis u”. Cette notation est justifiée par le fait que
∗
→ est la clôture réflexive transitive de la relation → définie par A.
∗
On parlera parfois des descendants de u pour parler des sommets v tels que u → v et des descendants
∗ ∗
propres pour parler des sommets v tels que u → v et v → 6 u.
Dans la suite, on note ∼ la relation binaire sur S définie par ∀(u, v) ∈ S 2 , u ∼ v si et seulement si
u est accessible depuis v et v est accessible depuis u.
Proposition 1.39
La relation ∼ est une relation d’équivalence.
Remarque 1.41
Contrairement à ce qu’on avait pu faire pour les graphes non orientés, la relation binaire ∼ n’est pas la
clôture transitive de la relation définie sur S par ∀(u, v) ∈ S 2 , u v dès lors (u, v) ∈ A et (v, u) ∈ A.
En effet considérons le contre exemple ci-contre. Il y a une unique CFC contenant
les 3 sommets mais il n’existe aucun couple de sommets x et y tels que x y.
Définition 1.42
On appelle composantes fortement connexes (CFC) de G les classes d’équivalences de ∼.
0 1 2
3 4 5
Définition 1.44
Soit W ⊆ S,
• le graphe G est dit fortement connexe si et seulement s’il admet une seule composante for-
tement connexe ;
• l’ensemble W est dit fortement connexe si et seulement le sous-graphe induit par G sur W
est fortement connexe.
Remarque 1.45
Un ensemble de sommets W ⊆ S est connexe si et seulement si les sommets de W sont deux à deux reliés
par une chaîne de G.
Proposition 1.47
Si W est une composante fortement connexe d’un graphe G alors W est fortement connexe.
Proposition 1.48
Les composantes fortement connexes sont les ensembles de sommets fortement connexes maxi-
maux (au sens de ⊆).
Démonstration :
• Soit un ensemble V ⊆ S fortement connexe et maximal pour l’inclusion. On a V 6= ∅, en effet n’importe
quel singleton {s} avec s ∈ S est fortement connexe donc un sur-ensemble fortement connexe de ∅ ♣ .
Puisque V est fortement connexe, le graphe qu’il induit, GV = (V, A ∩ V 2 ), est fortement connexe.
Autrement dit V admet une unique classe pour la relation ∼GV . Ainsi pour tout (u, v) ∈ V 2 on a u ∼GV v,
et comme les arcs de GV sont des arcs de G on a aussi u ∼G v.
Ainsi V est non vide et ne contient que des éléments deux à deux équivalents pour ∼G , on peut donc
considérer W la classe d’équivalence des éléments de V pour ∼G . Par construction, W est une CFC de
G, et donc en particulier W est fortement connexe d’après la propriété précédente. Or on a V ⊆ W ,
donc par maximalité de V on en déduit V = W , ainsi V est bien une CFC de G.
• Réciproquement considérons V une CFC de G. D’après la propriété précédente, V est alors un ensemble
fortement connexe. Par l’absurde supposons qu’il existe W un sur-ensemble strict de V , i.e. V W,
fortement connexe. On peut alors considérer un sommet w ∈ W \ V . Considérons aussi u un sommet
de V (une CFC est nécessairement non vide) et donc de W . W étant fortement connexe, le graphe
GW = (W, A ∩ W 2 ) est fortement connexe, ainsi il existe dans GW un chemin de u à w et un chemin
de w à u. Puisque les arcs de GW sont des arcs de G, ces chemins existent aussi dans G, ainsi u ∼G w.
Or u ∈ V et V est une classe d’équivalence de ∼G en tant que CFC de G, donc w ∈ V par transitivité.
Absurde.
Ainsi V n’admet est bien fortement connexe maximal.
Exemple 1.50
Lemme 1.52
S’il existe un chemin de Ci à Cj dans G,
b alors il existe un chemin dans G de n’importe quel sommet
de Ci à n’importe quel sommet de Cj .
Proposition 1.54
Un graphe réduit est acyclique.
Démonstration : Corollaire direct du lemme : s’il y avait un circuit dans G, b il existerait deux classes dis-
tinctes Ci et Cj telles que dans G il existe un chemin de Ci à Cj et de Cj à Ci . D’après le lemme, pour tout
b
s ∈ Ci et t ∈ Cj il existe alors dans G un chemin de s à t et un chemin de t à s. Ainsi t ∼ s, donc s et t sont
dans la même CFC, soit Ci = Cj . Absurde.
Proposition 1.55
Si G est sans circuit alors :
• chacun de ses sommets est seul dans sa CFC ;
• le graphe réduit de G est G lui-même.
Définition 2.1
La bordure d’un ensemble de sommets V ⊆ S, notée B(V ), est l’ensemble des successeurs des
sommets de V n’étant pas eux-mêmes dans V .
B(V ) = {u ∈ S \ V | ∃v ∈ V, (v, u) ∈ A}
0 1 2 3
4 5 6
Définition 2.3
Un parcours (Li )i∈J1,nK de G est une permutation des sommets telle que :
Autrement dit, un parcours est une permutation dans laquelle un sommet n’apparaît que s’il est
dans la bordure des sommets précédents, à moins que celle-ci ne soit vide.
Définition 2.4
Soit L = (Li )i∈J1,nK un parcours de G et i ∈ J1, nK.
On dit que Li est un point de régénération de L dès lors que B({Lj | j ∈ J1, iJ }) = ∅.
Remarque 2.5
Le premier sommet d’un parcours est toujours un point de régénération car B(∅) = ∅.
Lemme 2.7
Si V ⊆ S est tel que B(V ) = ∅, alors il n’existe aucun chemin allant d’un sommet de V vers un
sommet de S \ V .
Remarque 2.8
Attention il peut exister des chemins allant de S \ V vers S car la bordure ne regarde que les successeurs,
pas les prédécesseurs.
Lemme 2.11
Soit L = (Li )i∈J1,nK un parcours de G. Soit (p, q) ∈ J1, nK2 tel que p < q.
S’il existe un chemin de Lp à Lq , alors aucun des Lk pour k ∈Kp, qK n’est point de régénération.
Démonstration : Soit (p, q) ∈ J1, nK2 tels que p < q. Supposons qu’il existe un chemin de Lp à Lq . Par
l’absurde supposons qu’il existe aussi k ∈Kp, qK tel que Lk est un point de régénération. Par définition
B({Lj | j ∈ J1, kJ}) = ∅. On déduit donc du lemme 2.7 qu’il n’existe aucun chemin allant d’un sommet de
{Lj | j ∈ J1, kJ} vers un sommet de {Lj | j ∈ Jk, nK}. Le chemin de Lp à Lq contredit ce fait. Absurde.
Lemme 2.12
Soit L = (Li )i∈J1,nK un parcours de G. Soit Lr pour r ∈ J1, nK un point de régénération de L.
Notons Cr la partie associée à ce point de régénération dans le partition associée à L.
Tout sommet de Cr est accessible depuis Lr par un chemin dont tous les sommets sont dans Cr .
Démonstration : Par définition de la partition associée à un parcours, il existe q ∈ J1, nK tel que Cr =
{Lr , Lr+1 , . . . , Lq }. On peut alors montrer par récurrence forte et finie sur k ∈ Jr, qK la propriété suivante.
Proposition 2.13
Dans un graphe non orienté, la partition associée à un parcours quelconque est la décomposition
en composantes connexes.
Proposition 2.14
Dans un graphe orienté, un parcours offre une partition dont la décomposition en CFC est un
raffinement. Autrement dit les parties associées à un parcours sont toujours des unions de CFC.
Exemple 2.15
Le parcours (a, c, b, d, e) (dont on a souligné les points de régénération) du graphe ci-dessous conduit à la
partition {a, c, b}, {d}, {e} de S. On remarque que les composantes fortement connexes sont {a, b}, {c},
Exemple 2.19
2 4 5 4 5 3
c d f c d f
a b e a b e
1 3 6 1 2 6
a b e a b e
2 6 1 4 2 3
Dans le cas d’un parcours en largeur (resp. en profondeur), le père d’un sommet est fixé pour chaque
sommet Lk (non point de régénération) comme étant le premier (resp. dernier) sommet ouvert à
l’étape k. Aussi la forêt associé à un tel parcours est unique.
♣. les arcs sont de la forme (Li , Lj ) avec i < j, assurant la stricte croissance des indices le long d’un chemin
Remarque 2.24
La forêt associée à un parcours en largeur (resp. profondeur) est aussi une forêt associée à ce parcours vu
comme un parcours quelconque, mais la réciproque n’est pas toujours vraie.
Remarque 2.25
On peut définir formellement la notion de forêt associée à un parcours L de G = (S, A) comme un sous-
graphe (S, A0 ) où l’ensemble d’arcs A0 associe un père à chaque sommet non point de régénération dans L.
• Si L est un parcours en largeur (resp. en profondeur), pour tout k ∈ J1, nK tel que Lk n’est pas point de
régénération, on peut associer à k l’indice π(k) du premier (resp. dernier) sommet ouvert à l’étape k
de L, ainsi π(k) < k et (Lπ(k) , Lk ) est un arc de G. Dans ce cas la forêt associée au parcours en largeur
(resp. en profondeur) L est définie par l’ensemble A0 ci-dessous.
• Si L est un parcours quelconque, pour tout k ∈ J1, nK tel que Lk n’est pas point de régénération, on peut
associer à k un indice π(k) < k tel que Lk ∈ Succ(Lπ(k) ), i.e. tel que (Lπ(k) , Lk ) est un arc de G. Dans ce
cas une forêt associée au parcours L est définie par l’ensemble A0 ci-dessous.
On remarque que B(L) évolue peu entre chaque tour, puisqu’un seul élément est ajouté à L. Tirant
profit de cette observation, on raffine l’algorithme précédent en l’algorithme 2.
Algorithme 2 : Parcours avec ensemble todo
Entrée : Un graphe G = (S, A)
Sortie : Un parcours de G
1 L ← liste vide ;
2 Visités ← ∅ ;
3 Procedure ExploreDescendants :
Hypothèse : B(Visités) = ∅
Entrée : s ∈ S \ Visités
Effet : Ajoute à L et à Visités s et tous ses descendants, de manière à ce que L reste un
parcours partiel ♥ .
4 todo ← {s} ;
5 tant que todo 6= ∅ faire
6 Choisir un sommet u ∈ todo ;
7 Ôter u de todo ;
8 / Visités alors
si u ∈
9 Ajouter u à Visités ;
10 L ← L · u;
11 pour tout v ∈ succ(u) faire
12 Ajouter v à todo ;
Proposition 2.30
Si le graphe G est représenté par table de listes d’adjacence ♣ , l’algorithme 2 effectue O(n + m)
itérations de la boucle ligne 5 et O(m) itérations de la boucle ligne 11.
Démonstration : Ces complexités découlent du fait que la boucle tant que de la ligne 5 est exécutée une
fois pour chaque ajout d’un élément dans todo, en effet chaque tour de cette boucle extrait un élément de
todo. Estimons donc le nombre de fois qu’un sommet est ajouté à todo.
Remarquons au préalable que le corps de la conditionnelle si ligne 8 est exécuté une seule fois par sommets
du graphe. En effet, pour un sommet u ∈ S donné, u n’est initialement pas dans Visités, y est ajouté si le
corps du si ligne 8 est exécuté pour u, et ne peut par la suite plus en être retiré, ce qui exclut d’entrer à
nouveau dans le si ligne 8.
♥. Pour les besoins des énoncés, on dira ici qu’une séquence de sommets L est un parcours partiel de G si L est un
parcours du sous-graphe de G induit par l’ensemble des sommets figurant dans L.
Il est important de remarquer ici que l’algorithme de parcours ne se contente pas de visiter une fois chaque
sommet, mais visite en fait aussi une fois chaque arc du graphe.
Remarque 2.31
Intéressons nous aux structures de données concrètes usuellement utilisées pour implémenter les objets
todo et Visités.
• Les opérations requises pour l’objet todo sont l’ajout et l’extraction, aussi peut-on se contenter d’une
structure de pile ou de file pour l’implémenter. Les lignes 4, 5, 6, 7 et 12 se font alors en O(1).
• Les opérations requises pour l’objet Visités sont le test d’appartenance et l’ajout. On reconnaît le type
de données abstrait Ensemble. Dans le cas, usuel, où l’ensemble S des sommets peut être assimilé à
J0, n − 1K on utilise un tableau de booléens indicé par S = J0, n − 1K pour implémenter Visités. Les
lignes 8 et 9 se font alors en O(1).
Dans le cas où S = J0, n − 1K, le cumul des recherches des points de régénération (ligne 14) peut se faire
en O(n) ♣ . Ainsi on retiendra que, sous ces hypothèses, la complexité d’un parcours de graphe est en
O(n + m).
Remarque 2.32
Le choix d’une implémentation de todo au moyen d’une pile (resp. d’une file) conduit à un parcours en
profondeur (resp. largeur).
40 let s = ref 0 in
41 (* tant qu'il existe un sommet s non encore visité *)
42 while (!s < n) do
43 (* on visite s et ses descendants *)
44 explore_descendants !s;
45 (* on calcule un nouveau sommet s non encore visité *)
46 s := trouve_non_visites (!s + 1)
47 done;
48 List.rev !l
49
En utilisant la récursivité. Dans le cas d’un parcours en profondeur, on peut utiliser la pile des
appels récursifs pour gérer les sommets à visiter plutôt qu’une pile explicite. En remplaçant la fonc-
tion explore_descendants de l’implémentation précédente par la fonction récursive ci-dessous, on
obtient une nouvelle implémentation de parcours en profondeur.
1 let rec explore_descendants (s: int) : unit =
2 (* on teste si u a déjà été visité *)
3 if not (visites.(s)) then
4 (* si ce n'est pas le cas on le visite *)
5 begin
6 (* on l'ajoute aux visités pour ne pas le revisiter plus tard *)
7 visites.(s) <- true;
Remarque 2.34
Dans cette section, nous avons fourni une implémentation “générique” de parcours : on se contente de
calculer une liste de sommets qui forme un parcours. Il faut être capable d’adapter cet algorithme pour
répondre à divers problèmes, par exemple : le langage d’un automate est-il vide ? Tous les sommets sont-
ils accessibles depuis un certain sommet ? Combien de composantes connexes a ce graphe non orienté ?
Donner un circuit de ce graphe orienté ….
1 type page = string (* Une page Web est identifiée par son URL *)
2
Remarquons qu’avec de telles définitions il est possible de faire des parcours partiels (en profondeur,
en largeur, ou autres) des graphes implicites ainsi représentés. De plus, afin de réaliser de tels
parcours, on se dotera d’une structure de données pour représenter l’ensemble des sommets visités
(un ensemble d’etat ou de page ci-dessus), ainsi que des structures de mise en attente (pile, file,
ou autres, d’etat ou de page). Dans le cas du graphe du Web par exemple, on pourrait opter en
OCaml pour un ensemble des visités représenté au moyen d’une (string, bool) Hashtbl.t et une
structure de mise en attente de type String Queue.t.
Définition 4.1
Soit T = (Ti )i∈J1,nK une permutation des sommets de G.
On dit que T est un tri topologique de G dès lors que ∀(i, j) ∈ J1, nK2 , (Ti , Tj ) ∈ A ⇒ i 6 j,
autrement dit tous les arcs de G ont leur origine placée avant leur destination dans T .
0 1 2
3 4 5 6 7 8
9 10 11
déf
rgT (u) = min{i ∈ J1, nK | Ti ∼G u}
On dit que T est un tri préfixe de G dès lors que ∀(u, v) ∈ S 2 , (u, v) ∈ A ⇒ rgT (u) 6 rgT (v).
Remarque 4.7
Dans un tri topologique, on demande que si (u, v) est une arête du graphe alors u est placé avant v dans le tri
topologique ; dans le cas du tri préfixe on demande seulement que la composante connexe de u apparaisse
avant la composante connexe de v au sens où un des éléments de cette composante apparaît avant tous les
éléments de l’autre composante.
Lemme 4.8
Pour tout (u, v) ∈ S 2 et toute permutation des sommets T , u ∼ v ⇔ rgT (u) = rgT (v).
Démonstration :
• Si u ∼G v alors {i ∈ J1, nK | Ti ∼G u} = {i ∈ J1, nK | Ti ∼G v} et donc rgT (u) = rgT (v).
• Si rgT (u) = rgT (v) alors soit i = rgT (v), on a donc u ∼ Ti ∼ v.
Proposition 4.10
Un tri topologique est un tri préfixe.
Un tri préfixe d’un graphe sans circuit est un tri topologique.
Démonstration : S’il existe un tri topologique, le graphe est nécessairement sans circuit, et donc sa décom-
position en CFC est la partition en singletons. Ainsi le rang de n’importe quel sommet Ti est simplement
i, son propre indice puisqu’il est le seul, et donc le premier, représentant de sa classe. La condition sur T
pour qu’il soit tri préfixe se réécrit alors exactement comme la condition pour qu’il soit un tri topologique :
Calcul d’un tri préfixe. On s’intéresse à la mise en place d’un algorithme permettant le calcul
d’un tri préfixe. On remarque le fait suivant : si tous les descendants stricts d’un sommet u (les
descendants de u qui ne sont pas dans la même composante fortement connexe que u) sont déjà
placés dans une liste, alors on peut placer u en tête de cette liste.
Exercice de cours 4.12
Justifier cette affirmation.
Ainsi on construit le tri préfixe de la fin vers le début en adoptant la politique suivante : on ajoute
x en tête après que tous ses descendants stricts ont été ajoutés. On remarque que cet ajout corres-
pond alors au moment où on ferme l’appel récursif à explore_descendants lors d’un parcours en
profondeur. On implémente donc cette politique avec un algorithme récursif. La fonction Insérer
prend en argument un sommet x et insére le sommet x et ses descendants non encore ajoutés au
tri préfixe en cours de calcul. Si x n’est pas encore visité, on appelle récursivement Insérer sur les
descendants de x afin de s’assurer que tous les descendants de x sont déjà rangés dans le tri préfixe,
Algorithme 4 : Dijkstra
Entrée : Un graphe pondéré G = (S, A, c) où c : A → R+ , une source s ∈ S
Sortie : Une table indexée par S donnant la distance depuis s de chaque sommet de G
1 Initialiser µ[u] à +∞ pour chaque u ∈ S ;
2 Initialiser π[u] à ?? pour chaque u ∈ S ;
3 µ[s] ← 0 ; (* La source est à distance 0 d'elle-même *)
4 π[s] ← s ; (* La source est la racine de l’arborescence *)
5 todo ← {s} ; (* La source est le premier sommet à traiter*)
6 Procedure Relâcher((u, v)) :
Entrée : u et v deux sommets voisins dans G ;
7 si µ[u] + c((u, v)) < µ[v] alors
8 µ[v] ← µ[u] + c((u, v)) ;
9 π[v] ← u ;
10 Ajouter v à todo ;
11 tant que todo 6= ∅ faire
12 Extraire de todo un sommet u minimisant µ[u] ;
13 pour tout v ∈ succ(u) faire
14 Relâcher(u, v)
15 retourner µ
g
5
1 5
5
1 e
1 1
1
a b 3 s 2 2 f
1
3 2 3 1
c 1 d
5.2 L’algorithme A?
L’algorithme de Dijkstra est un algorithme de calcul des plus courts chemins depuis une source du
graphe, vers chacun des sommets du graphe. Il n’est pas rare que l’on ait seulement besoin d’un plus
court chemin entre deux sommets bien identifiés d’un graphe : non seulement nous disposons d’un
sommet source mais aussi d’un sommet objectif. On présente donc dans cette section une variante
de l’algorithme de Dijkstra : l’algorithme A? , qui met à profit cette information supplémentaire
d’objectif pour essayer d’améliorer le temps de calcul de l’algorithme de Dijkstra.
Exercice de cours 5.6
Si l’on connaît le sommet objectif o, et que l’on cherche à calculer seulement la distance d(s, o), peut-on arrêter
l’algorithme de Dijkstra dès que µ[o] prend une valeur finie ? Dès que o est sorti de todo ? Dans chaque cas, si
la réponse est oui justifier, et sinon donner un contre exemple.
L’algorithme de Dijkstra visite les sommets du graphe à la manière d’un parcours en largeur depuis
la source : les sommets sont parcourus par distances croissantes depuis la source. Toutefois dans
le cas où l’on connaît l’objectif, on aimerait aussi que l’algorithme cherche “dans la direction” de
l’objectif.
On fournit ci-dessous des exemples sur un graphe non orienté représentant les déplacements sur
les cases d’une grille : d’une case (un sommet du graphe) on peut se déplacer (les arcs du graphe)
dans une des 8 cases adjacentes ♣ , à moins qu’elles ne soient des murs. Les arcs sont pondérés de la
manière suivante : on se déplace√ horizontalement et verticalement pour un coût de 1, on se déplace
en diagonale pour un coût de 2.
On présente ci-dessous (Figure 1) les sommets parcourus par l’algorithme de Dijkstra lors de la
recherche d’un chemin depuis la source (en bas à gauche) et vers l’objectif . Les cases repré-
sentent des murs et ne sont donc pas accessibles. Les cases sont les sommets qui ont été visités
tandis que les cases sont les sommets se trouvant dans la file de priorité. Finalement les traits
représentent les plus courts chemins, soit l’arborescence encodée par le tableau de prédécesseurs
π. Cet exemple met en avant le fait que l’algorithme de Dijkstra parcourt les sommets ”en largeur”
depuis la source.
♣. Les huit cases sont : haut, bas, gauche, droite, coin haut gauche, coin haut droite, coin bas gauche, coin bas droite.
grâce à une fonction heuristique qui donne, pour chaque sommet du graphe, une estimation de la
distance restant à parcourir pour atteindre l’objectif. Dans ce cadre, on appelle heuristique une
fonction associant un score positif à chaque sommet du graphe, h : S → R+ .
Définition 5.7
On dit d’une heuristique qu’elle est admissible dès lors qu’elle donne une minoration de la dis-
tance à l’objectif dans le graphe. Autrement dit, h est une heuristique admissible dès lors que
pour tout sommet u ∈ S tel qu’il existe un chemin de u au sommet objectif o de longueur l, on a
h(u) 6 l.
Une telle heuristique est alors utilisée pour choisir le sommet à traiter dans l’ensemble todo comme
étant, non pas un de ceux qui minimisent y 7→ µ[y], mais un de ceux qui minimisent y 7→ µ[y]+h(y).
Autrement dit, au lieu de choisir d’explorer un sommet dont la distance connue à la source est
minimale ♣ , on choisit d’explorer un sommet par lequel pourrait passer un chemin de longueur
minimale reliant s et o si on en croit l’heuristique. On présente ci-dessus (Figure 2) les sommets
parcourus par l’algorithme de A? lors de la recherche d’un chemin dans les mêmes conditions que
précédemment, en utilisant la distance à vol d’oiseau comme heuristique.
Exercice de cours 5.8
Expliquer pourquoi la distance à vol d’oiseau donne une heuristique admissible.
On présente ci-dessous l’algorithme A? en mettant en jaune les lignes qui changent par rapport à
♣. ce qui assure en fait, on le rappelle, que la distance exacte à la source est minimale,
Théorème 5.10
Si l’heuristique utilisée est admissible, l’algorithme A? est correct.
Démonstration :
1) Remarquons tout d’abord que pour tout sommet x ∈ S, la valeur de µ[x] ne fait que décroître à me-
sure que l’algorithme A? s’exécute. En effet les valeurs de µ ne sont modifiées qu’à la ligne 10 de la
fonction Relâcher, et d’après le test qui précède cette instruction, la valeur est modifiée pour une valeur
strictement plus petite.
• Initialement (i.e. l.6), le seul sommet dans todo est s, et on a alors µ[s] = 0 < +∞ d’où l’invariant
I2 . De plus le chemin (γ0 = s) étant un chemin de longueur nulle reliant s et s, l’invariant I1 est
bien vérifié.
♣. les mêmes que l’algorithme de Dijkstra
3) Soit γ un chemin reliant s = γ0 à o = γq de longueur minimale. On montre ici qu’à n’importe quelle
/ todo, alors
étape de l’algorithme après la ligne 6, et pour tout k ∈ J0, qJ, si µ[γk ] = d(s, γk ) et γk ∈
µ[γk+1 ] = d(s, γk+1 ).
Entre t0 et t la valeur de µ[γk+1 ] n’a pu que décroître d’après le point 1) et celle de µ[γk ] n’a pas
changé, ainsi à l’instant t on a :
Enfin par l’invariant vu au point 2) on a µ[γk+1 ] > d(s, γk+1 ), d’où l’égalité souhaitée :
4) On montre ici que dès la ligne 6 et tant que o n’est pas extrait de todo, il existe un sommet x ∈ S
vérifiant les trois conditions suivantes :
a) x est dans todo ;
b) µ[x] = d(s, x) ;
c) il existe un chemin minimal de s à o passant par x.
On a traité plus haut le cas où d(s, o) = ∞, on traite donc ici le cas où o est accessible depuis s. Ainsi
l’invariant du point 4) assure que la condition de sortie de la boucle while ne peut être satisfaite tant que
o n’a pas été traité, autrement dit on sort nécessairement de l’algorithme par la ligne 17.
Notons x un sommet vérifiant a), b), c) au moment où o est sélectionné à la ligne 15 précédant cette sortie.
Puisque x ∈ todo (propriété a), le choix de o comme sommet à traiter assure η[o] 6 η[x]. Or :
et h(o) = 0 puisque h est minorante. On en déduit que µ[o] + 0 6 d(s, o), or µ[o] > d(s, o) par invariant du
point 2) donc µ[o] = d(s, o) et la valeur renvoyée est bien celle attendue.
Remarque 5.12
On remarque que dans l’algorithme A? , contrairement à ce qui se passe dans l’algorithme de Dijkstra, un
sommet x peut être extrait de todo avec une valeur µ[x] > d(s, x) (et risque dans ce cas d’y être à nouveau
ajouté avec une valeur de µ[x] réduite), et qu’un arc peut être relâché plusieurs fois ♣ .
Remarque 5.13
Pour démontrer la correction de l’algorithme de Dijkstra, on peut utiliser de même la remarque du point 1)
et les invariants I1 et I2 , mais les points suivants se simplifient, car on peut directement montrer que
lorsqu’un sommet x est extrait de todo la valeurs µ[x] vaut d(s, x).
♣. Cf. le sommet a et l’arc (a, o) dans l’exercice de cours 5.11 pour l’heuristique non monotone.
Ainsi, pour un sommet x fixé, la suite des valeurs prises par µ[x] à chaque fois que x est traité est une suite
strictement décroissante à valeurs dans un ensemble fini — à savoir l’ensemble des longueurs des chemins
élémentaires dans G — et donc finie.
Définition 5.15
On dit qu’une heuristique h est monotone dès lors que ∀(u, v) ∈ A, h(u) 6 c(u, v) + h(v)
Proposition 5.17
Si l’heuristique utilisée est admissible et monotone, chaque sommet u n’est traité qu’une seule
fois par l’algorithme A? , et dès qu’il est traité, µ[u] vaut d(s, u).
Démonstration : On reprend les invariants démontrés dans la preuve de la proposition 5.10. Ils assurent en
particulier qu’un sommet x hors de todo tel que µ[x] = d(s, x) ne peut pas être y ajouté à nouveau, car on
ajoute un sommet x dans todo que lorsqu’on donne à µ[x] une valeur strictement plus petite, or I1 assure
que µ[x] est déjà au minimum. Ainsi il suffit de montrer que lorsqu’un sommet u est traité la valeur µ[u]
donne d(s, u). On montre ce résultat par récurrence finie sur les étapes de traitement de sommet.
Lors de la première étape c’est le sommet s qui est traité, il a bien la valeur µ[s] = 0 = d(s, s).
Plaçons nous à une étape suivante de l’algorithme, et supposons que tous les sommets traités auparavant
vérifient la propriété. Notons v (v 6= s) le sommet traité à l’étape courante, et supposons par l’absurde que
µ[v] 6= d(s, v). D’après l’invariant I2 , µ[v] < ∞ puisque v ∈ todo, et donc µ[v] > d(s, v) par l’invariant
I1 . Ainsi on suppose en fait que µ[v] > d(s, v). Il existe donc un chemin γ de γ0 = s à γq = v de longueur
d(s, v) < µ[v]. On sait que γ0 = s a déjà été traité, et γq = v pas encore, on peut donc considérer k ∈ J0, qJ
l’indice du dernier sommet traité le long de γ. Ainsi par hypothèse de récurrence, µ[γk ] = d(s, k). Lors du
traitement de γk , son successeur γk+1 a été ajouté à todo et a reçu la valeur µ[γk+1 ] = µ[γk ] + c(γk , γk+1 ) =
d(s, k) + c(γk , γk+1 ) = ♣ d(s, γk+1 ). Ainsi d(s, v) la longueur du chemin γ se réécrit comme µ[γk+1 ] + l où
l = c(γk+1 , γk+2 ) + c(γk , γk+1 ) · · · + c(γq−1 , γq ) désigne la longueur du chemin γk+1 . . . γq .
• Si k = q − 1, lors du traitement du sommet γk , le sommet v a reçu la valeur µ[v] = µ[γk ] + c(γk , γk+1 )
soit d(s, v). Absurde.
• Si k < q − 1, on utilise la monotonie de h le long du chemin γk+1 . . . γq .
♣. On utilise implicitement le fait que le chemin γ0 . . . γk+1 est minimal, en tant que sous-chemin d’un chemin
minimal, et donc de longueur est d(s, γk+1 ).
or il est absurde que µ[v] + h(v) > µ[γk+1 ] + h(γk+1 ) sans quoi on aurait traité γk+1 (présent dans todo
et de meilleur score) et pas v à cette étape.
Dans les deux cas on arrive à une contradiction, ainsi on a bien µ[v] = d(s, v).