corrigé - INRIA/pop-art

Université Joseph Fourier
RICM3
Polytech Grenoble
Année 2014–2015
Algorithmique avancée — TD no 6 : Algorithme de Dijkstra
Soit un graphe G = (S, A). Les arcs sont étiquetés au moyen d’une fonction ω : A → R. L’algorithme de Dijkstra
permet de déterminer le chemin de poids minimal entre deux sommets s0 et s1 , le poids d’un chemin étant défini par
la somme des étiquettes des arcs de ce chemin.
Algorithme de Dijkstra :
Dijkstra(s0 ) :
s ∈ S : d[s] ← +∞
d [ s0 ] ← 0
Q ← { s0 }
tant que Q 6 = ∅
u ← arg mins {d[s]|s ∈ Q}
Q ← Q − {u}
pour tout v t.q. ( u, v ) ∈ A
pv ← d[u] + ω (u, v)
si pv < d [ v ] alors
d[v] ← pv
Q ← Q ∪ {v}
pour tout
Exercice 1.
Cet algorithme termine-t-il toujours ? Si non, sous quelle(s) hypothèse(s) termine-t-il ?
L’algorithme ne termine pas s’il contient un circuit de poids négatif, atteignable depuis s0 .
Quelques conditions suffisantes (mais pas forcément nécessaires !) pour que l’algorithme termine :
— le poids des arcs est positif ou nul
— il n’existe pas de circuit de poids négatif
— il n’existe pas de circuit de poids négatif atteignable depuis s0 (condition nécessaire et suffisante).
Exercice 2.
Exécutez à la main cet algorithme sur le graphe suivant (avec s0 = A) :
3
A
5
4
B
2
1
4
1
E
2
C
D
1
Notez les valeurs de d et de Q à la fin de chaque itération.
0
1
2
3
4
5
A
0
0
0
0
0
0
B
+∞
3
3
3
3
3
1. Que représente d à la fin de l’exécution ?
d
C
+∞
5
4
4
4
4
D
E
Q
+∞ +∞
{ A}
+∞ +∞
{ B, C }
7
4
{C, D, E}
7
4
{ D, E}
6
4
{D}
6
4
∅
u
A
B
C
E
D
Pour tout s ∈ S, d[s] est le poids du plus court chemin de s0 à s.
2. Que peut-on dire de la séquence de valeurs d[s] (pour un s donné) ?
La séquence de valeur est décroissante : on ne modifie la valeur de d[s] que lors de l’instruction d[v] ← pv,
exécutée seulement si pv < d[v].
3. Donnez un invariant concernant la valeur de d.
Soit δs le poids du plus court chemin de s0 à s, on a d[s] ≥ δs .
Exercice 3. On suppose que tous les sommets du graphe sont atteignables depuis s0 . Donner un minorant et un
majorant du nombre d’insertions dans Q (nombre d’évaluations de «Q ← Q ∪ {v}»).
Minorant du nombre d’insertions : on insère une et une seule fois chaque sommet : |S|. Ce minorant est atteint pour
tout graphe de la forme :
s0
s1
s2
sn
···
Majorant du nombre d’insertions (en supposant que le graphe ne comporte aucun circuit de poids négatif ; sinon la
question n’a pas de sens) : à chaque itération, la valeur de d[u] croît, puisqu’on prend le sommet u ∈ Q tel que d[u]
est minimum, et qu’on ajoute ensuite dans Q uniquement des sommets v tels que d[v] > d[u].
Soit |S| = n : tout sommet du graphe est traité une et une seule fois (il ne peut être réinséré dans Q puisque d[u]
est croissant, et ∀v, d[v] décroissant).
Dans le pire cas, à chaque itération, on va «insérer» à nouveau tous les sommets non encore traités. Le majorant du
n ( n −1)
nombre d’insertions est donc ∑in=1 (n − i ) = 2 .
Exercice : construire un graphe (de 5 sommets) pour lequel ce majorant est atteint.
En déduire une estimation du coût de l’algorithme (réfléchir à cette occasion à l’implémentation de Q !).
Q est une file à priorité. Il y a n extractions et O(n2 ) «insertions» (ou modification de la priorité) dans Q. Le coût
«critique» est ici celui de l’insertion.
On peut implémenter Q de manière à avoir l’insertion/modification de la priorité en O(1), et l’extraction en O(n)
(par exemple si Q est implémenté par un tableau de booléens indicé par les sommets). Le coût de l’algorithme est
alors en O(n2 ) (n extractions en O(n), et O(n2 ) insertions en O(1)).
Exercice 4.
Modifier cet algorithme pour afficher le chemin de poids minimal entre deux sommets s0 et s1 donnés.
Il faut mémoriser, pour chaque sommet, le sommet précédent, c’est-à-dire celui depuis lequel on a calculé le plus
court chemin. Cette valeur est mise à jour lors de la mise à jour de d.
On utilise un tableau P de sommets, indicé par les sommets :
Dijkstra(s0 ) :
pour tout s ∈ S :
d[s] ← +∞
P[s] ← s
d [ s0 ] ← 0
Q ← { s0 }
Q 6= ∅
u ← arg mins {d[s]|s ∈ Q}
Q ← Q − {u}
v
(u, v) ∈ A
pv ← d[u] + ω (u, v)
pv < d[v]
d[v] ← pv
P[v] ← u
Q ← Q ∪ {v}
tant que
pour tout t.q.
si
Pour afficher un plus court chemin entre s0 et s :
AcherChemin(s):
si P[s] 6= s alors AcherChemin(P[s])
Acher(s)
alors