Les graphes et l’algorithme de Dijkstra

# Chapitre 10 Les graphes et l’algorithme de Dijkstra

Soit un graphe ayant n sommets numérotés de 0 à n−1. Certains de ces sommets sont reliés par des arêtes de longueur données.
L’algorithme de Dijkstra permet de trouver le chemin de distance minimale qui relie le sommet de numéro `deb` aux autres sommets.
Cet algorithme procéde de proche en proche en se servant de la remarque :
si par exemple, le chemin le plus court pour aller du sommet 0 au sommet 2 passe par le sommet 4, le début de ce chemin est aussi le chemin le plus court pour aller du sommet 0 au sommet 4.

## 10.1 L’algorithme sur un exemple

Soient n=4 et le graphe de matrice `A` :

A= | ⎛ ⎜ ⎜ ⎜ ⎝ |
0 | 27 | 11 | 23
27 | 0 | 14 | 1
11 | 14 | 0 | 10
23 | 1 | 10 | 0
| ⎞ ⎟ ⎟ ⎟ ⎠

Je veux partir du sommet 0 pour aller au sommet 1 (resp 2,3) par le plus court chemin.
J’initialise :
`dist` à `[0,27,11,23]` (c’est à dire à la ligne 0 de `A`) `afaire` à `[1,2,3]` et
`sprec` à `[-1,0,0,0]` (`sprec[0]=-1` car on part du sommet 0 et pour `j!=0`, `sprec[j]=0` car `dist` est la distance provisoire minimum pour aller de 0 à `j`). Les 2 valeurs `dist[0]` et `sprec[0]` sont définitives car le sommet 0 est ici le départ. Je sais aussi que le chemin le plus court du sommet 0 au sommet 1 (resp 2,3) ne repassera pas par le sommet 0.
Première étape
Je cherche le minimum des valeurs de `dist` pour les sommets dont les numéros sont dans `afaire=[1,2,3]` c’est à dire le minimum de `[27,11,23]`.
Le minimum 11 de `[27,11,23]` est réalisé pour le sommet 2 (car `dist[2]=11`). Je supprime le numéro 2 de la liste `afaire` qui devient `afaire=[1,3]` car je connais maintenant le plus court chemin pour aller de 0 à 2, il est direct et a pour longueur 11 je ne change donc pas la valeur de `dist[2]` ni celle de `sprec[2]` : ces 2 valeurs sont maintenant définitives.
On a donc encore `dist=[0,27,11,23]` et `sprec=[-1,0,0,0]` où les valeurs d’indice 0 et 2 sont définitives.
Maintenant, le chemin provisoirement le plus court allant du sommet 0 au sommet 1 (resp du sommet 0 au sommet 3) est :

- soit le chemin direct allant de 0 à 2, suivi par le chemin le plus court allant de 2 à 1 (resp de 2 à 3),
- soit le chemin le plus court allant de 0 à 1 (resp de 0 à 3) sans passer pas par 2.

L’étape suivante consiste donc à comparer le chemin le plus court de 0 à 2 de longueur `dist[2]`, suivi par le chemin direct de 2 à 1 de longueur `A[2,1]` (resp 3 de longueur `A[2,3]`) avec le chemin le plus court provisoire qui va de 0 à 1 `dist[1]` (respde 0 à 3 `dist[3]`).
Je compare donc `27=dist[1]` à 11+14=25 (11=`dist[2]`=longueur du chemin minimum allant de 0 à 2 et `14=A[2,1]`=longueur du chemin direct allant de 2 à 1). On a 25<27 donc je modifie `dist` en `[0,25,11,23]` et `sprec` en `[-1,2,0,0]` puisque 25 est la longueur du chemin qui passe par 2.
Je compare donc `23=dist[3]` à 11+10=21 (11=longueur du chemin minimum allant de 0 à 2 et `10=A[2,3]`=longueur du chemin direct allant de 2 à 3). On a 21<23 donc je modifie `dist` en `[0,25,11,21]` et `sprec` en `[-1,2,0,2]` puisque 21 est la longueur du chemin qui passe par 2.
Donc maintenant `dist=[0,25,11,21]` et `sprec=[-1,2,0,2]`
Deuxième étape
Je cherche le minimum des valeurs de `dist` pour les sommets de numéros `afaire=[1,3]` c’est à dire le minimum de `[25,21]`.
Le minimum 21 de `[25,21]` est réalisé pour le sommet 3 car `dist[3]=21`. Je supprime le numéro 3 de la liste `afaire` qui devient `afaire=[1]` car je connais maintenant le plus court chemin pour aller de 0 à 3, il est de longueur 21 et il passe par 2 car `sprec[3]=2`.
Je cherche enfin le plus court chemin pour aller de 0 à 1 en empruntant :

- soit le chemin minimum allant de 0 à 2 de longueur 11 (car `dist[2]=11`), puis le chemin direct allant de 2 à 1 de longueur `14=A[2,1]` (donc un chemin de longueur 11+14=25=`dist[1]`),
- soit le chemin minimum qui va de 0 à 3 de longueur 21 (car `dist[3]=21`), puis le chemin direct allant de 3 à 1 de longueur 1=A[3,1] (donc un chemin de longueur 21+1=22).

Je compare donc 25 à 22. On a 22<25 donc je modifie `dist` en `[0,22,11,21]` et `sprec` en `[-1,3,0,2]`.
Donc maintenant `dist=[0,22,11,21]` et `sprec=[-1,3,0,2]`
Troisième étape
Il reste à chercher le minimum de `[22]` est obtenu pour le sommet de numéro 1, numéro que l’on supprime de la liste `afaire` qui devient vide.
Le résultat final est donc :
`dist=[0,22,11,21]` et `sprec=[-1,3,0,2]`

## 10.2 Déscription de l’algorithme de Dijkstra

Soit `A` la matrice donnant la longueur des arêtes i.e. `A[j,k]` est la longueur de l’arête reliant le sommet `j` au sommet `k` avec la convention de mettre `A[j,k]=inf=`+∞ quand il n’y a pas d’arête qui relie le sommet `j` au sommet `k`.
Soientt `dist` un vecteur donnant les distances provisoires reliant le sommet de numéro `deb` aux autres sommets et `sprec[j]` le numéro du sommet précedent `j` par lequel on doit passer pour avoir la distance minimale provisoire.
Par exemple si `n=5` et si en fin de programme `sprec=[3,2,0,-1,2]` cela veut dire que l’on part du sommet 3 car `sprec[3]=-1`.
Si on cherche le plus court chemin pour aller du sommet 3 au sommet `j=4` le chemin sera `3,???,4`. Mais comme `sprec[4]=2` le chemin sera `3,??,2,4` puis, comme `sprec[2]=0` et `sprec[0]=3` le chemin sera `3,0,2,4`.
Par contre le chemin minimum pour aller du sommet `3` à `0` sera direct de `3` à `0` puique `sprec[0]=3`.
`afaire` est la liste des indices restant à faire.
Initialisation :
Au début `dist=A[deb]` (`A[deb]` est la ligne d’indice `deb` de `A`) et
`sprec[deb]=-1` et `sprec[j]=deb` pour `j!:=deb`.
`afaire` est la liste des indices dans laquelle on a enlevé `deb` I.E. une liste de longueur `n-1`.
Etapes suivantes :
On cherche le minimum `m` des distances provisoires `dist` reliant le sommet `deb` aux sommets dont les numéros sont dans `afaire` et on note `jm` le numéro du sommet réalisant ce minimum. On supprime `jm` de la liste `afaire`.
On compare ensuite, pour tous les numéros des sommets restant afaire, la longueur `autredist` des chemins qui passent par `jm` à la valeur provisoire `dist` et si pour le sommet de numéro `k=afaire[j]` le chemin qui passe par `jm` est plus court on modifie `dist[k]` et on modifie `sprec[k]` qui vaut alors `jm`.
On recommence jusqu’a épuisement de la liste `afaire` , c’est à dire que l’on fait cela `n-1` fois.
Remarque
Attention aux indices !!!!
`afaire` est la liste des indices ou numéros des sommets restant à traiter et il ne faut pas confondre le numéro des sommets et les indices qu’ils ont dans la liste `afaire` i.e. ne pas confondre `k=afaire[j]` avec `j` (si `afaire=[2,0,1]` le sommet 0 a pour indice 1 dans `afaire`).

## 10.3 Le programme

    dijkstra(A,deb):={
    local j,k,n,na,dist,sprec,distaf,afaire,
          m,jm,autredist,jma;
    // initialisation
    n:=size(A);
    dist:=A[deb];
    sprec:=[deb $n];
    sprec[deb]:=-1;
    n:=n-1;
    //afaire liste des indices restant a faire
    afaire:=suppress([j$(j=0..n)],deb);
    na:=size(afaire)-1;
    pour k de 0 jusque n-1 faire
    //le sommet jm realise la dist m minimum de distaf
    //jma est l'indice de m dans la liste distaf
    //jm est l'indice de m dans la liste afaire
    distaf:=[dist[afaire[j]]$(j=0..na)];
    m:=min(distaf);
    //jma indice du minimum m dans afaire
    jma:=member(m,distaf)-1;
    //jm indice du minimum m dans dist
    jm:=afaire[jma];
    //fin prematuree
    si m==inf alors return dist,sprec; fsi;
    afaire:=suppress(afaire,jma);
    na:=na-1;
      pour j de 0 jusque na faire
         autredist:=m+A[jm,afaire[j]];
         si autredist<dist[afaire[j]] alors
            dist[afaire[j]]:=autredist;
            sprec[afaire[j]]:=jm;
         fsi;
      fpour;
    fpour;
    retourne dist,sprec;
    }:;

On tape :
`M:=[[0,27,11,23],[27,0,14,1],[11,14,0,10],[23,1,10,0]]`
`dijkstra(M,0)`
On obtient (cf la section précédente) :
`[0,22,11,21],[-1,3,0,2]`
On tape :
`A:=[[0,1,6,7],[1,0,4,3],[6,4,0,1],[7,3,1,0]]` `dijkstra(A,2)`
On obtient :
`[5,4,0,1],[1,2,-1,2]`
cela veut dire par exemple pour aller de 2 à 0 la distance minimale est 5 et le chemin est 2,1,0.
On tape :
`B:=[[0,1,6,7],[1,0,4,2],[6,4,0,1],[7,2,1,0]]` `dijkstra(B,0)`
On obtient :
`[0,1,4,3],[-1,0,3,1]`
cela veut dire par exemple pour aller de 0 à 2 la distance minimale est 4 et le chemin est 0,1,3,2.

## 10.4 Le chemin le plus court d’un sommet à un autre

On tape en utilisant le programme précédent :

    dijkstra2(A,deb,fin):={
    local dist,sprec,long,chemin,j;
    dist,sprec:=dijkstra(A,deb);
    long:=dist[fin];
    j:=sprec[fin];
    chemin:=fin;
    tantque j!=-1 faire
    chemin:=j,chemin;
    j:=sprec[j];
    ftantque;
    retourne long,[chemin];
    }:;

ou bien en arrétant le programme dès que l’on a atteint le sommet `fin`, on tape :

    dijkstra3(A,deb,fin):={
    local j,k,n,na,dist,sprec,distaf,afaire,m,
          jm,autred,jma,long,chemin;
    n:=size(A);
    //dist:=[inf$ n];dist[deb]:=0;
    dist:=A[deb];
    sprec:=[deb $n];
    sprec[deb]:=-1;
    n:=n-1;
    //afaire liste des indices restant a faire
    afaire:=suppress([j$(j=0..n)],deb);
    na:=n-1;
    pour k de 0 jusque n-1 faire
       //minimum des distances dist[afaire[j]]
       distaf:=[dist[afaire[j]]$(j=0..na)];
       m:=min(distaf);
       //jma indice du minimum m dans afaire
       jma:=member(m,distaf)-1;
       //jm indice du minimum m dans dist
       jm:=afaire[jma];
       si m==inf alors return dist,sprec; fsi;
       si jm==fin alors
          long:=dist[fin];
          chemin:=jm;
          j:=sprec[jm];
          tantque j!=-1 faire
            chemin:=j,chemin;
            j:=sprec[j];
          ftantque;
          retourne long,[chemin];
       fsi;
       afaire:=suppress(afaire,jma);
       na:=na-1;
       pour j de 0 jusque na faire
          autred:=m+A[jm,afaire[j]];
          si autred<dist[afaire[j]] alors
             dist[afaire[j]]:=autred;
             sprec[afaire[j]]:=jm;
          fsi;
       fpour;
    fpour;
    }:;

On tape :
`M:=[[0,27,11,23],[27,0,14,1],[11,14,0,10],[23,1,10,0]]`
`dijkstra3(M,0,1)`
On obtient (cf la section précédente) :
`22,[0,2,3,1]`
`dijkstra3(M,0,2)`
On obtient (cf la section précédente) :
`11,[0,2]`
`dijkstra3(M,0,3)`
On obtient (cf la section précédente) :
`21,[0,2,3]`
On tape :
`A:=[[0,1,6,7],[1,0,4,3],[6,4,0,1],[7,3,1,0]]`
`dijkstra2(A,2,0)` ou `dijkstra3(A,2,0)`
On obtient : `5,[2,1,0]`
On tape :
`B:=[[0,1,6,7],[1,0,4,2],[6,4,0,1],[7,2,1,0]]`
`dijkstra2(B,0,2)` ou `dijkstra3(B,0,2)`
On obtient :
`4,[0,1,3,2]`
On tape :
`dijkstra2(B,2,0)` ou `dijkstra3(B,2,0)`
On obtient :
`4,[2,3,1,0]`
Exemple avec une matrice créee aléatoirement
On tape :
`MR:=randmatrix(5,5,’alea(50)+1’)`
`M:=MR+tran(MR)`
`pour j de 0 jusque 4 faire M[j,j]=<0; fpour;`
`M`
On obtient :
`[[0,47,91,57,60],[47,0,58,18,50],[91,58,0,22,74],`
`[57,18,22,0,70],[60,50,74,70,0]]`

⎛ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ |
0 | 47 | 91 | 57 | 60
47 | 0 | 58 | 18 | 50
91 | 58 | 0 | 22 | 74
57 | 18 | 22 | 0 | 70
60 | 50 | 74 | 70 | 0
| ⎞ ⎟ ⎟ ⎟ ⎟ ⎟ ⎠

On tape :
`dijkstra(M,0)`
On obtient :
`[0,47,79,57,60],[-1,0,3,0,0]`
On tape :
`dijkstra2(M,0,2)`
On obtient :
`79,[0,3,2]`