Vous êtes sur la page 1sur 10

Principes de l'algorithme de Dijkstra

Publi en 1959, l'algorithme de Dijkstra est une alternative celui de Floyd, alternative plus complexe, mais galement beaucoup plus rapide. En fait, le rsultat fourni par Dijkstra n'est pas tout fait le mme que celui fourni par Floyd : on se fixe un sommet source, et Dijkstra donne tous les chemins les plus courts de ce sommet source chacun des autres sommets et ce en O(aln n) . Ainsi, pour le mme rsultat, Floyd tourne en O(n3) tandis que Dijkstra tourne en O(a2ln n) o a est le nombre d'artes du graphe (suppos connexe, rappelons-le), donc O(n).

Description Implmentation

Description de l'algorithme
L'algorithme de Dijkstra est un algorithme de type glouton : chaque nouvelle tape, on traite un nouveau sommet. Reste dfinir le choix du sommet traiter, et le traitement lui infliger, bref l'algorithme... Tout au long du calcul, on va donc maintenir deux ensembles :

C, l'ensemble des sommets qui restent visiter ; au dpart C=S-{source} D, l'ensemble des sommets pour lesquels on connat dj leur plus petite distance la source ; au dpart, D={source}.

L'algorithme se termine bien videmment lorsque C est vide. Pour chaque sommet s dans D, on conservera dans un tableau distances le poids du plus court chemin jusqu' la source, et dans un tableau parcours le sommet p qui le prcde dans un plus court chemin de la source s. Ainsi, pour retrouver un chemin le plus court, il suffira de remonter de prdcesseur en prdcesseur jusqu' la source, ce qui pourra se faire grce un unique appel rcursif (beaucoup moins coteux que dans le cas de Floyd...).

Initialisation
Au dbut de l'algorithme, le chemin le plus court connu entre la source et chacun des sommets est le chemin direct, avec une arte de poids infini s'il n'y a pas de liaison entre les deux sommets. On initialise donc le tableau distances par les poids des artes reliant la source chacun des sommets, et le tableau parcours par source pour tous les sommets.

ime tape
On suppose avoir dj trait i sommets, parcours et distances contiennent respectivement les poids et le prdcesseur des plus courts chemins pour chacun des sommets dj traits. Soit s le sommet de C ralisant le minimum de distances[s]. On supprime s de C et on l'ajoute D. Reste mettre jour les tableaux distances et parcours pour les sommets t relis

directement s par une arte comme suit : si distances[s] + F(s,t) < distances[t], alors on remplace distances[t] par distances[s] + F(s,t) et parcours[t] par s... et c'est tout !

(n-2)me tape
Au dpart, il y a (n-1) sommets visiter, mais comme on le verra ci-aprs, la dernire tape est inutile puisqu'elle n'apporte rien. Ainsi, ds la (n-2)me tape, distances et parcours contiennent toute l'information ncessaire pour trouver des plus courts chemins de la source chacun des autres sommets (car alors D=S):

distances[s] est le poids du plus court chemin de la source s parcours[t] est le prdcesseur de s dans un plus court chemin de la source s

Preuve de l'algorithme
Il reste prouver que cet algorithme fonctionne bel et bien, ce qui premire vue n'est pas franchement une vidence. La rcurrence effectuer repose sur les deux hypothses suivantes :

Les tableaux distances et parcours permettent d'obtenir pour tous les points s de D un plus court chemin qui mne de la source s Les tableaux distances et parcours permettent d'obtenir pour tous les points t de C un plus court chemin qui mne de la source t en ne passant que par des points de D.

Ces deux hypothses sont bien vrifies l'initialisation des tableaux, si elles le sont encore l'arrive, comme D=S , la deuxime hypothse fournit la preuve de l'algorithme. Supposons donc ces hypothses vrifies une tape donne de l'algorithme. Soit s le sommet de C qui minimise le tableau distances. Montrons que les deux hypothses sont encore valables pour DU{s} et C-{s} .

Soit p=parcours[s] . D'aprs la premire hypothse, il existe un plus court chemin c allant de la source p et ne passant que par des points de D. Montrons que cU(p,s) (en trait plein et pais sur le dessin prcdent) est un chemin minimal de la source s. Par l'absurde, si ce

n'tait pas le cas, on pourrait trouver un autre chemin plus court qui entre ncessairement dans l'ensemble C par un certain sommet t (sinon on contredit la deuxime hypothse) diffrent de s (en pointills pais sur le dessin). Or, d'aprs le choix de s, distances[s] < distances[t] , donc comme toutes les artes sont de poids positifs, ce nouveau chemin passant par t aura un poids suprieur ou gal au poids de cU(p,s) . Ainsi, cU(p,s) est bien un chemin minimal de la source s, la premire hypothse sera bien vrifie au dbut de l'tape suivante. Pour dmontrer la seconde hypothse applique DU{s} , considrons un sommet u de C distinct de s. De deux choses l'une : soit on peut trouver un plus court chemin de la source u en ne passant que par des points de DU{s}, , soit il n'y a rien modifier et c'est fini. Si on peut effectivement trouver un plus court chemin, il passe par s d'aprs la deuxime hypothse applique D. Il reste voir que cU(p,s)U(s,u) (en pointills fins sur le dessin) est bien un plus court chemin de la source u en ne passant que par des sommets de DU{s} . Considrons donc un tel chemin, passant par s. S'il retourne dans D, il faut ncessairement qu'il ressorte de D en un certain sommet v. Mais v a t incorpor D avant s, donc il existe un chemin de la source v plus court que cU(p,s) , et a fortiori plus court que cU(p,s)U(s,u) , ce qui voudrait dire que notre chemin ne passe pas par s. Ainsi, ce chemin ne retourne pas dans D et est donc bien de la forme escompte. La mise jour des tableaux parcours et distances ralise les modifications ncessaires pour les seuls sommets o il peut y avoir lieu de modifier quelque chose, c'est--dire ceux directement relis s. Ouf ! L'algorithme fonctionne bien, tant mieux ! On remarque aussi que la dernire tape (la (n-1)me) ) est inutile, puisque, comme il ne reste qu'un seul sommet t dans C, un plus court chemin ne passant que par des sommets de D=S-{t} est un plus court chemin de la source t.

Evaluation
Schmatiquement, on pourrait reprsenter l'algorithme de Dijkstra de la manire suivante :

Pour i=1 n-2 Faire s = Extraire_Minimum(C) Pour tous les sommets (de C) relis s // (Pour toutes les artes partant de s) Si (il le faut) Mettre_a_jour(distances,parcours) Suivant i

La boucle intrieure devrait tre uniquement effectue sur des sommets de C. En pratique, la distinction entre les extrmits des artes appartenant C et les autres ne peut se faire sans test. Comme de toute faon le test (il le faut) renvoie "faux" pour les sommets de D (sommets dj traits), on peut tendre la boucle tous les sommets du graphe relis s sans perte de temps. La mise jour des tableaux se fait en temps constant tout comme le test (il le faut), l'extraction du minimum en O(ln n) . La boucle intrieure est excute en tout et pour tout a fois o a est le nombre d'artes du graphe. Le graphe est connexe, donc n=O(a) . Bref, un algorithme en O(a nln n) , c'est merveilleux... mais ce n'est pas le rsultat annonc ! Cherchons l'erreur... Elle tient "simplement" au fait que la fonction d'extraction du minimum attend aussi en argument la relation d'ordre qui existe sur ce tas, i.e. s=Extraire_Minimum(C,inf) . Mais cette fonction inf dpend de la distance minimale du

sommet considr la source, distance variant d'tape en tape en fonction du tableau distances ! L'algorithme ainsi crit est donc faux... Il faut ainsi, aprs chaque modification de la relation d'ordre, s'assurer que le tas reste tas, ce qui peut se faire l'aide d'une simple percolation. En effet, considrons un sommet t pour lequel il est ncessaire de modifier les tableaux distances et parcours. t ne peut que se "rapprocher" de la source, donc il ne peut que remonter dans le tas. Concrtement, on obtient l'algorithme suivant :
Pour i=1 n Faire s=Extraire_Minimun(C,inf) Pour tous les sommets t relis s // ( = Pour toutes les artes partant de s) Si (il le faut) Mettre_a_jour(distances,parcours) Percolation(t) Fin Si Suivant i

Reprenons le calcul prcdent : sans la boucle intrieure, l'algorithme tourne en O(nln n) . La boucle intrieure est excute a fois. On a donc au maximum a percolations, chacune en O(ln n) . Par consquent, l'algorithme final tourne en O((n+a)ln n) , ou, puisque n=O(a) , en O(aln n).

Implmentation de l'algorithme de Dijkstra


Implmentation de l'algorithme de Dijkstra
Rsultat renvoy par l'algorithme
Comme nous l'avons expliqu dans la partie prcdente, l'algorithme de Dijkstra renvoie deux tableaux : distances et parcours. D'o la ncessit de crer une structure approprie en C++ :
struct Res { int taille ; // taille du graphe calcul Sommet source ; // source des chemins considrs int * distances ; // distances[i] est la distance totale de la source i int * parcours ; // parcours[i] est le sommet prcdent i dans le parcours de la source i Res() ; // constructeur par dfaut ~Res() ; // destructeur void Affiche(FILE * f = stdout) ; // Affiche le rsultat (sue l'cran ou dans un fichier) } ;

L'algorithme en lui-mme
Pas de remarque particulire faire sur l'implmentation de l'algorithme de Dijkstra en luimme. Elle ne fait que suivre les tapes prcdemment mentionnes en s'appuyant sur les structures de donnes prsentes... L est tout le secret !

Res * Graphe::Djikstra(int source) { Res * resultat = new Res ; int * distances = new int [taille] ; Sommet * parcours = new int [taille] ; Sommet * C = new int [taille-1] ; MatriceDouble * m =matricialisation() ; for (int k=0 ; k<taille ; k++) { distances[k]=m->rep[source][k] ; parcours[k]=source ; } // Initialisation de C for (Sommet i=0, j=0 ; i<taille ; i++) { if (i!=source) { C[j]=i ; j++ ; } } Tas * T = new Tas(C, taille-1, (fptr) inf_str, distances, vrai) ; T->Tri() ; for (i=0 ; i<taille -2; i++) { Sommet mini = T->ExtrMinimum() ; for (Rayons * l = m->adresse[mini]->sommet.liste ; l!=NULL ; l=l->suivant) { Sommet s = l->fleche.extrem ; if (inf_str(somme(m->rep[mini][s] , distances[mini]),distances[s])) { distances[s]=somme(m->rep[mini][s] , distances[mini]) ; parcours[s] = mini ; T->Percolation (T->Inverse(s)) ; } } } delete m ; delete T ; delete [] C ; resultat->distances = distances ; resultat->parcours = parcours ; resultat->taille = taille ; resultat->source = source ; return resultat ; }

Petite interface
Qu'attendons-nous pour tester l'algorithme sur quelques graphes de notre cru ? Une petite interface conviviale ? Qu' cela ne tienne, dfinissons un objet menu :

class Menu { char * nom ; // Titre du menu int taille ; // Nombre d'options propos char * * composants ; // Tableau contenant le nom des options proposes char * acces ; // tableau contenant les lettres qui permettent de choisir une option donne static int est_dans(char, char *) ; // teste si char est dans char * et renvoie sa position dans char * s'il existe public : Menu(char *, int, char * *, char *) ; // Constructeur paramtr (nom, taille, tableau de composantes, table d'accs) ~Menu() ; // destructeur void Affiche() ; // Affichage du menu int Reponse () ; // Attente de la rponse } ;

Dans notre cas, le menu propos est le suivant :


*** Menu principal *** [G] [A] [C] [S] [T] [E] [Q] : : : : : : : creation interactive d'un Graphe Afficher un graphe Charger un graphe en format texte Sauver un graphe en format texte Trouver les plus courts chemins Enregistrer le resultat Quitter

Votre choix ?

La cration interactive d'un graphe se comprend sans problme ; il faut juste dcider au dpart si on souhaite obtenir un graphe symtrique ou non (i.e. "des routes avec ou sans sens interdit"). Trouver les plus courts chemins lance bien entendu l'algorithme de Dijkstra une fois le sommet source prcis. Pour des raisons de commodit de programmation lors de l'appel rcursif ncessaire pour trouver un chemin le plus court proprement parler, les chemins les plus courts sont affichs dans l'ordre inverse, c'est--dire qu'il faut simplement les lire de droite gauche...Il reste dire un mot sur les formats de fichiers utiliss pour reprsenter les graphes... Se reporter la partie suivante pour quelques exemples concrets :
Taille : n Sommet : n Extremite : i Poids : pi ... Extremite : j

Poids : pj ... Sommet : n-1 ... ... Sommet : 0 ... Extremite : k Poids : pk

L'ordre des sommets, tout comme l'ordre des artes, importe peu pourvu que les sommets apparaissent tous une et une seule fois (le programme de lecture commence par lire la taille du graphe et recherche alors les n sommets correspondants).

Deux petits exemples


Reprenons l'exemple du graphe S={d,a,o,u,s,t} , en renumrotant les sommets de 0 5 et en supprimant l'arte (d,d) (le plus court chemin de d lui-mme est ncessairement 0). Certes, ce graphe n'est pas connexe, mais tant qu'on ne s'occupe pas des chemins qui n'existent pas, l'algorithme fonctionne merveille...

Taille : 6 Sommet : 5 Extremite : Poids : 5 Sommet : 4 Extremite : Poids : 3 Extremite : Poids : 4 Sommet : 3 Extremite : Poids : 1 Sommet : 2 Extremite : Poids : 3 Sommet : 1 Extremite : Poids : 7 Extremite : Poids : 1 Sommet : 0

4 5 2 4 3 3 2

Extremite : 1 Poids : 2

Lanons Dijkstra sur le sommet 0 :


Source : 0 Parcours a l'envers 1 0 Distance : 2 Parcours a l'envers 2 1 0 Distance : 3 Parcours a l'envers 3 2 1 0 Distance : 6 Parcours a l'envers 4 3 2 1 0 Distance : 7 Parcours a l'envers 5 4 3 2 1 0 Distance : 10

de 0 a 1 : de 0 a 2 : de 0 a 3 : de 0 a 4 : de 0 a 5 :

Pour les amateurs, un graphe un peu plus gros, et connexe cette fois :

Taille : 8 Sommet : 7 Extremite : 4 Poids : 2 Extremite : 2 Poids : 3 Extremite : 0

Poids : 3 Sommet : 6 Extremite : Poids : 1 Extremite : Poids : 4 Extremite : Poids : 1 Sommet : 5 Extremite : Poids : 2 Sommet : 4 Extremite : Poids : 5 Extremite : Poids : 1 Extremite : Poids : 2 Sommet : 3 Extremite : Poids : 7 Extremite : Poids : 3 Sommet : 2 Extremite : Poids : 4 Sommet : 1 Extremite : Poids : 1 Sommet : 0 Extremite : Poids : 3 Extremite : Poids : 3 Extremite : Poids : 8 Extremite : Poids : 1

5 3 0 6 7 5 3 6 7 4 7 7 4 2 1

C'est parti pour un petit Dijkstra appliqu au sommet 6...


Source : 6 Parcours a 0 6 Distance : Parcours a 1 0 6 Distance : Parcours a 2 7 1 0 6 Distance : Parcours a 3 6 Distance : Parcours a 4 0 6 Distance : Parcours a 5 6 Distance :

l'envers de 6 a 0 : 1 l'envers de 6 a 1 : 2 l'envers de 6 a 2 : 6 l'envers de 6 a 3 : 4 l'envers de 6 a 4 : 4 l'envers de 6 a 5 : 1

Parcours a l'envers de 6 a 7 : 7 1 0 6 Distance : 3