Le problème Algorithme de Bellman-Ford

Transcription

Le problème Algorithme de Bellman-Ford
20052006
Algorithmique Avancée et Complexité
Master 1 d'Informatique
S.Tison
UFR IEEA
Graphes: Chemins Minimaux
Le problème
On se place dans le cas des graphes pondérés: un graphe pondéré est un triplet
et
P
(S, A, P ) où (S, A) est un graphe
est une fonction qui associe à chaque arc un poids réel, pas forcément positif.
c = v0 v1 , v1 v2 , ....vk−1 vk dans un graphe pondéré est juste la somme des poids des
P
P (c) = k−1
i=0 P (vi vi+1 ). Si le poids d'un arc est 1, on retrouve la notion de longueur dans
Le poids d'un chemin
arcs le composant:
un graphe non pondéré.
Le problème général est donc: étant donnés deux noeuds
a
et
b,
si il existe. On notera
δ(s, b)
s
et
b,
trouver le chemin de poids minimal reliant
le poids minimal d'un chemin reliant
a
et
b.
On parlera de:
. problème à source et destination uniques quand on veut juste calculer
pour un seul couple d'états
. problème à source unique (resp. destination unique) si, pour
un chemin correspondant) pour tout
b
(resp.
( et un chemin correspondant)
s
(resp.
b)
donné, on veut calculer
δ(s, b)
( et
s),
.de problème toutes sources, toutes destinations si on on veut calculer
pour tous les couples
δ(s, b)
(s, b),
δ(s, b)
( et un chemin correspondant)
(s, b).
Remarques:
. Si il n'y a pas de chemin de
s
à
b,
on prendra comme convention
δ(s, b) = +∞
. Il se peut qu'il n'y ait pas de poids minimal, si le graphe contient des circuits négatifs ou absorbants, i.e.
dont la somme des poids est strictement négative. On aura alors
δ(s, b) = −∞
pour certains sommets.
Algorithme de Bellman-Ford
On se place dans le cas du problème à source unique.
G = (S, A, P )
est un graphe orienté pondéré,
s
la source.
L'algorithme est basé sur quelques observations très simples:
.pour tout arc
u, v , δ(s, v) ≤ δ(s, u) + p(u, v)
.Si il n'y a pas de circuit négatif dans le graphe, on a:
δ(s, v) = 0 si s = v
δ(s, v) = min{δ(s, u) + p(u, v)/(u, v) ∈ A} si s diérent de v .
.Tout chemin minimal -si il existe- de s à v comporte au plus |S| − 1
arcs.
Le cas général
//G=(S,A,P) orienté pondéré
//s source fixée
Algorithme de Bellman-Ford{
//init
pour tout v D[v]=+ ∞ ;
D[s]=0;
//fin init
pour i de 1 à |S|-1
//ETAPE
//invariant: D(v) supérieur ou égal à δ(s, v)
//invariant: D(v) inférieur ou égal au poids de tout chemin de s à v
//
d'au plus i-1 arcs
}
pour tout (u,v) dans A
si (D[v]> D[u]+p(u,v)) alors D[v]= D[u]+p(u,v); //relaxation de l'arc (u,v)
fin pour;
//fIN ETAPE
fin pour;
//détection des circuits négatifs
pour tout (u,v) dans A
si (D[v]> D[u]+p(u,v)) alors raise ``CIRCUIT Négatif'';
fin pour;
// G est sans circuit négatif
// pour tout v D[v]=δ(s, v);
La complexité de l'algorithme sera donc en
O(|S|∗|A|), en supposant qu'on peut énumérer les arcs en O(|A|)
Q?: qu'est-ce que cela présuppose sur l'implémentation choisie?
Remarque: on pourrait aussi répéter Etape jusqu'à obtenir un point xe. On arrête dès qu'une étape n'a
rien modié. Pour certains graphes, le nombre d'étapes eectuées pourra être nettement inférieur à
|S| − 1.
Q?: pour lesquels?
Le cas acyclique
Dans le cas où le graphe est acyclique, on peut simplier l'algorithme. Une version naïve serait:
//G orienté sans circuit
D(s,but){
si s=but alors 0
sinon min(chemin(s,noeud) + D(noeud, but)) pour tout arc (noeud,but) de A
}
Si le graphe n'a pas de circuit l'arrêt et la correction sont donc assurés.
sauf dans des cas très particuliers (lesquels?), plusieurs appels à
D(s, noeud)
On peut alors remarquer que
seront eectués avec les mêmes
paramètres. On peut donc appliquer le principe de la programmation dynamique, ce qui conduità:
//DejaCalcule table de booléens indexée par S
//TD table de poids indexée par S
//si DejaCalcule[v]=True, TD[v]=δ(s, v)
Drecdyn(b){ //retourne δ(s, b)
si non DejaCalcule[but] alors //on calcule!
{DejaCalcule[but]=True;
si but=s alors TD[but]=0;
sinon
TD[but]=min(Drecdyn(noeud) + p(noeud, but))
return TD[but]}
Dans ce cas la complexité sera de
O(|A| + |S|),
pour tout arc (noeud,but) de A};
si on suppose par exemple que le graphe est implémenté par
listes d'adjacence (donc l'accès aux arcs arrivant dans un sommet se fait en temps proportionnel au nombre
d'arcs).
Exercice: comment programmer cet algorithme de façon itérative?
Algorithme de Dijkstra
On se place toujours dans le cas d'un graphe pondéré orienté; On suppose de plus que les poids des arcs sont
positifs ou nuls. L'algorithme de Dijkstra est de type glouton; à chaque étape, il ajoute un sommet à l'ensemble
des noeuds pour lesquels
δ(s, n)
est déjà calculé, en le sélectionnant de façon gloutonne.
2
//G=(S,A,p) graphe pondéré avec poids >=0
Dijkstra{
pour chaque noeud D(noeud)=+∞;
D(source)=0;
pour chaque noeud Marquer noeud NonDécouvert;
Marquer source Adévelopper;
tant que Adévelopper non vide //
//choix glouton
prendre n dans Adévelopper tel que D(n) soit minimum.
pour tout arc (n,b) dans A {
si b NonDécouvert
alors {Marquer b Adévelopper; D(b)=D(n)+p(n,b);Pred(b)=n;}
sinon si b Adévelopper et D(b)>D(n)+p(n,b)
alors {D(b)=D(n)+p(n,b);Pred(b)=n;}
fin pour;
marquer n Exploré;
fin tant que;
//pour tout noeud b D(b)=δ(s, b)
}
Correction: On peut montrer la correction en utilisant un raisonnement de type glouton. Soit l'énumération
des sommets
δ(source, b)
b
δ(source, b) minimal. Si l'algorithme
D(b) soit diérent de δ(source, b). Il est
dans l'ordre de
minimal tel que
n'était pas correct, soit le sommet
b
de
assez facile de montrer qu'il existerait
n
de poids inférieur sur lequel l'algorithme se tromperait, d'où la contradiction.
On peut aussi montrer que les propriétés suivantes constituent l'invariant de la boucle principale:
.si
b
est Exploré
D(b) = δ(source, b).
.si
b
est Adévelopper
D(b)
est le poids minimal d'un chemin de
source
à
b
ne passant que par des noeuds
Exploré.
.Tout successeur d'un noeud Exploré est Exploré ou Adévelopper.
Exercice: Compléter la preuve...
Complexité:
La complexité dépend de la structure choisie pour l'ensemble des noeuds Adévelopper.
Les
opérations à réaliser sont: Ajouter un élément, Extraire un élément de poids minimum, Modier le poids d'un
élément. Un élément est ajouté et extrait une seule fois, mais par contre il peut être modié plusieurs fois.
Si on implémente la structure via un tableau indexé par les numéros de sommets, Ajouter, Modier seront
en
O(1) et Extraire_Min sera en O(|S|).
On aura donc un coût total en
O(|S|2 +|A|) soit O(|S|2 ) (en supposant
que l'accès aux arcs arrivant dans un sommet se fait en temps proportionnel au nombre d'arcs).
On peut aussi implémenter la structure via un minimier(tas) binaire. Ajouter, Modier et Extraire_Min
seront en
O(log(|S|)).
On aura donc un coût total en
O(|S|log(|S|) + |A|log(|S|), soit O(|A|log(|S|)) si le graphe
est connexe. Cette implémentation sera donc intéressante par rapport à la première, si le graphe est peu dense.
Q?: Comment implémenter un minimier?
On peut encore améliorer et obtenir une complexité en
O(|A| + |S|log(|S|))
noeuds Adévelopper comme un tas de Fibonacci qui permet un coût amorti en
en implémentant l'ensemble des
O(1)
pour Modier.
Remarque: Cet algorithme qui est de type glouton - à chaque étape, on choisit le sommet qui respecte le
critère glouton (n tel que
D(n)
minimum)- peut être vu comme un parcours générique de graphe où la gestion
des noeuds Adévelopper se fait via une le de priorité.
Le cas toutes sources, toutes destinations
Le graphe considéré est orienté et pondéré. On supposera que le graphe ne contient pas de circuit négatif. Les
sommets seront numérotés de
d'adjacence
p. p(i, j)
sera
+∞
1
|S|.
(i, j)
à
si
On supposera de plus que le graphe est ici représenté par une matrice
n'est pas un arc, 0 si
3
i = j,
le poids de l'arc
(i, j)
sinon.
Un premier algorithme
L'idée est simplement de calculer le poids minimal des chemins de longueur 1, puis 2...
Soit
dl (i, j)
le poids minimal d'un chemin de
i
à
j
de longueur au plus l .
On a donc:
d0 (i, i) = 0, d0 (i, j) = +∞ si i 6= j .
dl (i, j) = min(dl−1 (i, j), min(k,j)∈A (dl−1 (i, k) + p(k, j)))
δ(i, j) = d|S|−1 (i, j):
chemin de poids minimal ne contient pas de circuit et donc est de longueur au plus |S| − 1.
Calcul basique: On peut représenter les valeurs dl (i, j) dans une matrice carrée Dl . Le calcul de Dl
3
4
fonction de Dl−1 sera eectué en O(|S| ). Le calcul total est donc en O(|S| .
Enn, on peut remarquer que si le graphe ne contient pas de circuits négatifs, alors
Calcul amélioré:
un
en
on peut remarquer que ce calcul peut être vu comme un calcul de multiplication de
min, la multiplication l'addition. La matrice Dl peut donc être vue
l
comme P , avec P la matrice des poids. On peut alors utiliser une technique type exponentation indienne
|S|−1 en O(log(|S|) multiplication de matrices, soit un coût total en O(|S|3 log(|S|).
pour calculer P
matrices, l'addition usuelle étant ici le
Exercice: écrire l'algorithme amélioré, rééchir à pourquoi ça marche...
L'algorithme de Floyd-Warshall
L'idée est un peu similaire.
Mais au lieu de faire varier la longueur des chemins, on va faire varier les som-
mets intermédiaires autorisés; on s'intéresse d'abord aux chemins sans sommet intermédiaire, puis de sommet
intermédiaire éventuel
ak (i, j) sera
part i et j ).
1,
puis
1
et
2...
le poids minimal d'un chemin de
i
à
j
n'utilisant que des sommets de numéro au plus
k
(mis à
On aura donc: a0 (i, j) = p(i, j)
ak (i, j) = min(ak−1 (i, j), ak−1 (i, k) + ak−1 (k, j))
Cette récurrence est basée sur l'hypothèse que le circuit ne contient pas de circuit négatif: un chemin de
poids minimal ne passe pas deux fois par le même sommet; donc, si
k
est utilisé dans le chemin, il est utilisé
une seule fois.
Les valeurs qui nous intéressent sont les
En remarquant que
ak (i, k) = ak−1 (i, k)
a|S| (i, j): a|S| (i, j) = δ(i, j).
et ak (k, j) = ak−1 (k, j), on aboutit
à l'algo suivant:
//G graphe orienté pondéré sans circuit négatif
//P matrice des poids de G
Algo Floyd-Warshall{
//A_0=P
pour i de 1 à |S|
pour j de 1 à |S|
a[i][j]= P[i][j];
//fin init
pour k de 1 à |S| //calcul de a_k
pour i de 1 à |S|
pour j de 1 à |S|
si a[i][j]>a[i][k]+a[k][j]
alors a[i][j]=a[i][k]+a[k][j];
//pour tout i et j, a[i][j]=δ(i, j)
}
Le coût total est donc en
Exercice:
O(|S|3 ).
)
on ne s'est intéressé dans cette section qu'au calcul des poids minimaux, et non des chemins
correspondants. Enrichir les deux algorithmes précedents pour pouvoir aussi calculer une représentation ecace
de l'arbre des chemins de poids minimal.
4

Documents pareils