dijkstra kshortest

22
Graphs & Algorithmie : Recherches sur l’implémentation des algorithmes liés à Dijkstra et au problème des K-plus courts chemins Arthur Van de Wiele Louis Thibon 25 Janvier 2011

Upload: -

Post on 18-Aug-2015

29 views

Category:

Engineering


6 download

TRANSCRIPT

Graphs & Algorithmie : Recherches sur l’implémentationdes algorithmes liés à Dijkstra et au problème des K-plus

courts chemins

Arthur Van de WieleLouis Thibon

25 Janvier 2011

Table des matières

1 Structures de donnée et outils divers 21.1 Graphviz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.2 Conversion d’une méthode de saisie simple vers graphviz . . . . . . . . . . . . . . . 31.3 Structure de donnée liée au stockage du graphe . . . . . . . . . . . . . . . . . . . . 61.4 Structure de donnée liée à l’algorithme de Dijkstra . . . . . . . . . . . . . . . . . . 61.5 Structure de donnée nécessaire à l’algorithme des k-plus courts chemins . . . . . . 7

2 Problème du plus court chemin 92.1 Présentation du problème . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92.2 Analyse de l’algorithme de Dijkstra . . . . . . . . . . . . . . . . . . . . . . . . . . . 92.3 Commentaires sur l’implémentation . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.4 Fonctions utiles à l’affichage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.4.1 Fonction nécessaire à l’obtention du code Graphviz . . . . . . . . . . . . . . 102.4.2 Fonction d’affichage des tableaux liés à la structure de donnée spécifique à

Dijkstra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112.5 Fonctions spécifiques au structures de données utilisées . . . . . . . . . . . . . . . . 11

2.5.1 Fonction de génération du graphe . . . . . . . . . . . . . . . . . . . . . . . . 112.5.2 Fonction d’affichage des tableaux liés à la structure de donnée spécifique à

Dijkstra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122.6 Fonctions utiles à l’algorithme de Dijkstra . . . . . . . . . . . . . . . . . . . . . . . 13

2.6.1 Fonction retournant le poids de l’arc entre deux sommets . . . . . . . . . . 132.6.2 Trouver la distances minimum depuis le tableau des distances . . . . . . . . 132.6.3 Fonction permettant de retrouver les successeurs d’un sommet . . . . . . . 142.6.4 Fonction de verrouillage des deux tableaux . . . . . . . . . . . . . . . . . . 142.6.5 Initialisation nécessaire à l’algorithme de Dijkstra . . . . . . . . . . . . . . . 142.6.6 L’algorithme en lui même . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152.6.7 La fonction main . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

3 Problème des K-plus courts chemins 183.1 Présentation du problème . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183.2 Analyse de l’algorithme des k-plus court chemins . . . . . . . . . . . . . . . . . . . 193.3 Commentaires sur l’implémentation . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

3.3.1 Fonction d’affichage de l’arbre . . . . . . . . . . . . . . . . . . . . . . . . . . 193.3.2 Fonction de création des nœuds des successeurs . . . . . . . . . . . . . . . . 203.3.3 Fonction algorithme des k plus court chemins . . . . . . . . . . . . . . . . . 21

1

Chapitre 1

Structures de donnée et outils divers

1.1 GraphvizGraphviz est une suite d’utilitaires sous système Unix qui permet de tracer le visuel d’un graphe

donné à partir d’un structure de donnée (langage) propre à Graphviz. Il est donc possible de créerun utilitaire simple pour convertir une structure de graphe simpliste en langage interprétable parun des programmes de Graphviz : “dot”.

Le langage interprétable par Graphviz se présente comme suite :

1 digraph G2 {3 edge [ c o l o r=purple , a r rows i z e =2] ;4 node [ c o l o r=pink , s t y l e=f i l l e d ] ;5 0 −> 1 [ l a b e l =1] ;6 0 −> 3 [ l a b e l =2] ;7 1 −> 2 [ l a b e l =3] ;8 1 −> 3 [ l a b e l =4] ;9 2 −> 3 [ l a b e l =5] ;

10 }

Ce qui permet d’obtenir le tracé suivant :

Figure 1.1 – Exemple de grape obtenu à l’aide des outils de Graphviz

2

Mais cet utilitaire prend tous son sens lors de tracés de graphes plus importants, lorsque letracé devient réellement fastidieux, comme dans cet exemple :

Figure 1.2 – Second exemple de graphe obtenu à l’aide des outils de Graphviz

1.2 Conversion d’une méthode de saisie simple vers graphvizLe langage qu’interprète Graphviz implique certaines redondances dans son écriture. Afin de

faciliter l’écriture d’un graphe, nous avons développé un petit utilitaire travaillant sur une méthodede saisie simple qui permet d’obtenir instantanément un code de type Graphviz.

Le méthode de saisie que nous avons souhaité utilisé se construit comme suit : Chaque ligne cor-respond à un sommet suivi du nombre et de la liste de ses successeurs avec l’affectation du poidsde l’arc entre eux. Pour plus de facilité, la première ligne doit comporter le nombre de sommetsdu graphe. Ce qui se traduit par l’écriture suivante pour le cas de la figure 1.1 :

1 42 0 2 1 1 3 23 1 2 2 3 3 44 2 1 3 55 3 0

Le code source de ce programme est donné ci après. Les explications se trouvent à la suite ducode.

3

1 #include <s td i o . h>2 #include <s t d l i b . h>3 #include <s t r i n g . h>45 // S t ruc tu re de donnee permettant d ’ o b t en i r des l i s t e s cha inees6 typedef struct l i s t e l i s t e ;7 struct l i s t e8 {9 int noeud ;

10 int poid ;11 l i s t e ∗ su iv ;12 } ;1314 int main ( int argc , char ∗argv [ ] )15 {1617 // Ouverture du f i c h i e r comportant l e code s im p l i f i e du graphe .18 FILE∗ src_graph=fopen ( argv [ 1 ] , " r " ) ;1920 // Afin de ne pas surcharger l e code , l e t e s t l i e a l ’ e x i s t e n c e du

parametre ou du f i c h i e r n ’ appara i t pas .2122 // L ’ u t i l i s a t i o n des l i s t e s cha inees e t de l ’ a l l o c a t i o n memoire v ia

des mal loc permet d ’ op t imi se r l ’ espace memoire u t i l i s e pours t o c k e r un graphe . Ceci permet egalement de t r a i t e r t ou t e t a i l l ede graphe .

2324 l i s t e ∗∗deb=NULL;25 l i s t e ∗∗ parc_tbl=NULL;2627 l i s t e ∗parc_chaine=NULL;28 l i s t e ∗prec=NULL;2930 int nbr_noeud=0,no_noeud=0, nbr_successeurs=0;31 int no_succ=0, poid_arc=0;3233 int i =0;3435 f s c a n f ( src_graph , "%d" , &nbr_noeud ) ;3637 deb = ( l i s t e ∗∗) mal loc ( nbr_noeud ∗ s izeof ( l i s t e ∗ ) ) ;38 parc_tbl=deb ;3940 for ( i =0; i<nbr_noeud ; i++)41 {4243 f s c a n f ( src_graph , "%d %d" , &no_noeud,&nbr_successeurs ) ;44 prec = NULL;4546 i f ( nbr_successeurs )47 {48 while ( nbr_successeurs != 0)49 {50 parc_chaine = ( l i s t e ∗) mal loc ( s izeof ( l i s t e ) ) ;51 f s c a n f ( src_graph , "%d %d" , &no_succ ,&poid_arc ) ;52 parc_chaine−>noeud = no_succ ;

4

53 parc_chaine−>poid = poid_arc ;54 parc_chaine−>su iv = NULL;55 i f ( prec == NULL) ∗parc_tbl = parc_chaine ;56 else prec−>su iv = parc_chaine ;57 prec = parc_chaine ;58 nbr_successeurs−−;59 }60 }61 else62 {63 ∗parc_tbl = NULL;64 }65 parc_tbl++;66 }6768 // Ci apres , l a l e c t u r e du graphe donnant l e code nece s sa i r e au bon

fonct ionnement des u t i l i t a i r e s de g raphv i z .6970 // Cet a l gor i thme r e v i e n t a parcour i r simplement l a s t r u c t u r e de

donnee u t i l i s e e pour s t o c k e r l e graphe . Cet te s t r u c t u r e e s tp re sen t ee dans un autre paragraphe .

7172 parc_tbl = deb ;73 p r i n t f ( " digraph G {\n" ) ;74 p r i n t f ( " edge [ c o l o r=purple , a r rows i z e =2] ;\n" ) ;75 p r i n t f ( "node [ c o l o r=pink , s t y l e=f i l l e d ] ; \ n" ) ;76 for ( i =0; i<nbr_noeud ; i++)77 {78 parc_chaine=∗parc_tbl ;79 while ( parc_chaine !=NULL)80 {81 p r i n t f ( "%d −> %d [ l a b e l=%d ] ; \ n" , i , parc_chaine−>noeud ,

parc_chaine−>poid ) ;82 parc_chaine=parc_chaine−>su iv ;83 }84 parc_tbl++;85 }86 p r i n t f ( "}\n" ) ;8788 f c l o s e ( src_graph ) ;8990 return 0 ;91 }

Le fonctionnement de ce programme est simple, il prend en paramètre (lors de l’appel del’exécutable) un nom de fichier (lien relatif) et en effectue la traduction vers le langage Graphviz.Afin de ne pas surcharger ce programme, nous avons préféré effectuer l’affichage en console du codegénéré, plutôt que le le faire passer via des tubes et des sous-process directement à l’utilitaire ’dot’.De ce fait, afin d’obtenir un graphique sous forme d’image (ici, de type png) la commande d’appelnécessite d’utiliser un pipe en console afin de router directement les code généré vers l’utilitaire :

1 . / conv graph1 . txt | dot −Tpng −o t e s t . png

Ce programme utilise une structure de données pour le stockage du graphe basée sur les listeschainées qui sera détaillée dans la prochaine section.

5

1.3 Structure de donnée liée au stockage du grapheLa structure de donnée ici présentée est celle mise en place dans le programme de conversion

précédent. Elle sera également utilisé lors de l’implémentation de l’algorithme de Djikstra et celuides k-plus courts chemins.

Pour faire simple, cette structure de donnée se présente sous la forme d’un tableau d’élémentsde type liste correspondant à chaque sommet. Chacun de ces sommets est ensuite lié à tous sessuccesseurs via une liste chainée. Cela permet non seulement de donner un sens à un arc, maiségalement de pouvoir tirer profit de la simplicité de représentation de cette structure de donnéepour y adapter tout type d’algorithme issu de la théorie des graphes.

La structure de donnée est la suivante :

1 // S t ruc tu re de donnee permettant d ’ o b t en i r l e s l i s t e s cha inees2 typedef struct l i s t e l i s t e ;3 struct l i s t e4 {5 int noeud ;6 int poid ;7 l i s t e ∗ su iv ;8 } ;

On peut ainsi représenter le stockage du graphe pris en exemple précédemment comme suit :

Figure 1.3 – Représentation du stockage du graphe via des listes chainées

En outre, l’utilisation de cette structure permet de parcourir le graphe de manière intuitive. Eneffet, on connait immédiatement tous les successeurs de chaque sommet, et les poids qui les relient.

1.4 Structure de donnée liée à l’algorithme de DijkstraPour l’algorithme de Dijkstra, deux structures sont nécessaire, l’une pour créer le tableau des

distances, l’autre pour créer le tableau des prédécesseurs. Dans la pratique, une seule structuresuffit car on peut référencer la distance ou le prédécesseur comme une simple donnée de type int.Comme le préconise l’algorithme de Dijkstra, cette structure doit également comporter un verrou,afin de ne pas écraser des données déjà traitées.

La structure de donnée est la suivante :

1 typedef struct s t r_d i j k s t r a s t r_d i j k s t r a ;

6

2 struct s t r_d i j k s t r a3 {4 int noeud ;5 int data ; // La donnee peut e t r e : d i s tance , ou n_o du predeces seur6 int verrou ;7 } ;

Ce verrou est présent dans les deux tableau, ce qui constitue une redondance puisque chaqueélément des deux tableaux sont verrouillés en même temps. Mais cela constitue aussi une vérificationquand au bon fonctionnement de l’algorithme.

1.5 Structure de donnée nécessaire à l’algorithme des k-pluscourts chemins

Pour résoudre ce problème nous allons utiliser deux structures de données spécifiques. Unestructure appelée "nœud", qui sera la structure de l’arbre. Cette structure contiendra la valeurdu sommet mis dans l’arbre ainsi que le poids de l’arc avec le sommet précédent (dans l’arbre)."nœud" contiendra également un pointeur vers son nœud père et, le nombre de fils pouvant varier,une liste de fils.

1 struct noeud2 {3 int valeur_sommet ;4 int poid_prec_noeurd ;5 l_Arbre∗ f i l s ;6 noeud∗ pere ;7 } ;

La liste de fils constitue la deuxième structure utilisée. Cette structure contient un pointeurvers un "nœud" de l’arbre, qui est le fils en question, ainsi qu’un pointeur vers le maillon suivantde la liste, le fils suivant.

1 struct l_Arbre2 {3 noeud∗ noeud_A ;4 l_Arbre∗ su iv ;5 } ;

C’est en combinant l’utilisation de ces deux structures que nous parviendrons à créer l’arbrereprésentant les chemins. Voici alors le schémas de la structure de l’arbre :

7

Figure 1.4 – Représentation des structures de donnée nécessaire à la création d’un arbre

8

Chapitre 2

Problème du plus court chemin

2.1 Présentation du problèmeLe cheminement dans les graphes, et plus précisément la recherche du plus court chemin, est un

problème récurrent dans l’étude des graphes. Ces applications sont multiples : réseaux électriquesou réseaux d’information ou encore calcul d’itinéraire via des cartes, plusieurs algorithmes issusde la théorie des graphes permettent de répondre à ces problèmes mathématiques. Nous allons icinous intéresser à l’algorithme de Dijkstra puis tenter de l’implémenter en langage C.

L’algorithme de Dijkstra utilise deux tableaux, l’un de prédécesseurs, l’autre des plus courtesdistances entre deux points quelconques. Avec l’application successive de l’algorithme de Dijkstra,on finit par obtenir le tableau du plus court chemin reliant la source à tous les autres sommets.

Cela revient donc à effectuer un parcours ordonné du graphe en mettant à jour certaines in-formation dans les deux tableaux selon un certain nombre de règles.

2.2 Analyse de l’algorithme de DijkstraCet algorithme effectue l’initialisation de ses deux tableaux puis les remplie au fur et à mesure de

son évolution dans le graphe et selon des contraintes bien définies. On peut repésenter l’algorithmecomme suit :

1 Pour k de 1 a n , f a i r e :2 d i s t an c e s [ k ] <−− cout ( s , k ) ;3 pr edec e s s eu r s [ k ] <−− s ;4 FinPour ;5 M <−− Supprimer ( s , M) ;67 TantQue (M non vide ) Fa i re :8 i <−− LePlusProche (M) ;9 Si ( d i s t an c e s [ i ] = \ i n f ) Alors r e tour ;

10 M <−− Supprimer ( i , M) ;1112 Pour k de 1 a d+( i ) Fa i re :13 j <−− k−eme_successeur ( i ) ;14 Si ( EstSupprime ( j , M) <> 1)15 v <−− d i s t an c e s [ i ] + cout ( i , j ) ;16 Si ( v < d i s t an c e s [ j ] )17 Alors d i s t an c e s [ j ] <−− v ;18 Alors p r ede c e s s eu r s [ j ] <−− i ;19 FinSi20 FinSi

9

21 FinPour22 FinTantQue

2.3 Commentaires sur l’implémentationPour implémenter cet algorithme, nous avons repris la structure de stockage de graphe vue dans

le programme de conversion de langage, c’est à dire un tableau de listes chainées. Il est donc néces-saire de ré-implémenter sous forme de fonctions les algorithmes de génération de cette structure degraphe. Il y a ensuite un certains nombre de fonction à développer pour faire tourner l’algorithmede Dijkstra. Certaines fonctionnalités trop souvent utilisées seront également isolée en tant quefonction a part entière pour plus de clarté.

Pour permettre a l’algorithme de Dijkstra de fonctionner, il est nécessaire de quantifier les dis-tances infinies. Nous avons donc choisi une distance maximale qu’aucun chemin ne doit dépasserafin que les comparaisons fonctionnent.

1 #define INF 999999999

2.4 Fonctions utiles à l’affichage

2.4.1 Fonction nécessaire à l’obtention du code GraphvizReprise de l’utilitaire de conversion, cette petite fonction permet de rendre compte du graphe

avec lequel on travail. Tout étant détaillé dans la partie correspondante au convertisseur, il nesemble pas nécessaire d’expliquer plus en détail son fonctionnement.

1 int to_graphviz ( l i s t e ∗∗deb , int nbr_noeud )2 {3 l i s t e ∗∗ parc_tbl=NULL;4 l i s t e ∗parc_chaine=NULL;5 int i =0;67 parc_tbl = deb ;89 p r i n t f ( " digraph G {\n" ) ;

10 p r i n t f ( " edge [ c o l o r=purple , a r rows i z e =2] ;\n" ) ;11 p r i n t f ( "node [ c o l o r=pink , s t y l e=f i l l e d ] ; \ n" ) ;1213 for ( i =0; i<nbr_noeud ; i++)14 {15 parc_chaine=∗parc_tbl ;1617 while ( parc_chaine !=NULL)18 {19 p r i n t f ( "%d −> %d [ l a b e l=%d ] ; \ n" , i , parc_chaine−>noeud ,

parc_chaine−>poid ) ;20 parc_chaine=parc_chaine−>su iv ;21 }2223 parc_tbl++;24 }25

10

26 p r i n t f ( "}\n" ) ;2728 return 1 ;29 }

2.4.2 Fonction d’affichage des tableaux liés à la structure de donnéespécifique à Dijkstra

Cette petite fonction permet de vérifier que l’algorithme de Dijkstra effectue correctement leremplissage des tableaux en les affichant.

1 void a f f_s t r_d i j k s t r a ( s t r_d i j k s t r a ∗ s t r , int nbr_noeud , char∗ mot)2 {34 int i =0;56 for ( i = 0 ; i < nbr_noeud ; i ++)7 {8 p r i n t f ( "Noeud %d a pour %s %d du sommet %d . ( verrou : %d) \n" , i ,

mot , ( s t r+i )−>data , ( s t r+i )−>noeud , ( s t r+i )−>verrou ) ;9 }

1011 }

2.5 Fonctions spécifiques au structures de données utilisées

2.5.1 Fonction de génération du grapheComme pour le passage à Graphviz, cette fonction est issue du convertisseur précédemment

traité. Elle prend en paramètre le fichier de l’utilisateur, et génère la structure de graphe de typetableau de listes chainées.

1 l i s t e ∗∗ g en e r a t i on_ l i s t e (FILE∗ src_graph , int∗ noeuds )2 {3 int nbr_noeud=0,no_noeud=0, nbr_successeurs=0;4 int nbr_successeurs_temp= 0 , no_succ=0, poid_arc=0;56 int i =0;78 l i s t e ∗∗deb=NULL;9 l i s t e ∗∗ parc_tbl=NULL;

10 l i s t e ∗parc_chaine=NULL;11 l i s t e ∗prec=NULL;1213 f s c a n f ( src_graph , "%d" , &nbr_noeud ) ;1415 ∗noeuds = nbr_noeud ;1617 deb = ( l i s t e ∗∗) mal loc ( nbr_noeud ∗ s izeof ( l i s t e ∗ ) ) ;1819 parc_tbl=deb ;2021 for ( i =0; i<nbr_noeud ; i++)

11

22 {23 f s c a n f ( src_graph , "%d %d" , &no_noeud,&nbr_successeurs ) ;24 prec = NULL;2526 i f ( nbr_successeurs )27 {28 nbr_successeurs_temp = nbr_successeurs ;29 while ( nbr_successeurs != 0)30 {31 parc_chaine = ( l i s t e ∗) mal loc ( s izeof ( l i s t e ) ) ;3233 f s c a n f ( src_graph , "%d %d" , &no_succ ,&poid_arc ) ;3435 parc_chaine−>noeud = no_succ ;36 parc_chaine−>poid = poid_arc ;37 parc_chaine−>su iv = NULL;3839 parc_chaine−>nbr_successeurs = nbr_successeurs_temp ;4041 i f ( prec == NULL) ∗parc_tbl = parc_chaine ;42 else prec−>su iv = parc_chaine ;4344 prec = parc_chaine ;45 nbr_successeurs−−;46 }47 }4849 else50 {51 ∗parc_tbl = NULL;52 }5354 parc_tbl++;55 }5657 return deb ;58 }

Cette fonction retourne le premier élément du tableau afin de pouvoir le parcourir dans lesautres fonctions.

2.5.2 Fonction d’affichage des tableaux liés à la structure de donnéespécifique à Dijkstra

Cette petite fonction permet de vérifier que l’algorithme de Dijkstra effectue correctement leremplissage des tableaux en les affichant.

1 void a f f_s t r_d i j k s t r a ( s t r_d i j k s t r a ∗ s t r , int nbr_noeud , char∗ mot)2 {34 int i =0;56 for ( i = 0 ; i < nbr_noeud ; i ++)7 {8 p r i n t f ( "Noeud %d a pour %s %d du sommet %d . ( verrou : %d) \n" , i ,

mot , ( s t r+i )−>data , ( s t r+i )−>noeud , ( s t r+i )−>verrou ) ;

12

9 }1011 }

2.6 Fonctions utiles à l’algorithme de Dijkstra

2.6.1 Fonction retournant le poids de l’arc entre deux sommetsCette fonction permet de connaitre le poids de l’arc entre deux sommets. Si ces deux sommets

ne sont pas directement reliés par un arc, cette fonction renvoie la valeur de l’infini quantifié commeexpliqué précédemment.

1 int get_poid ( l i s t e ∗∗ deb , int nbr_noeud , int sommet , int su ivant )2 {3 l i s t e ∗∗ parc_tbl = NULL;4 l i s t e ∗parc_chaine = NULL;5 int i =0, poid = INF ;67 parc_tbl = deb ;89 for ( i =0; i<nbr_noeud ; i++)

10 {11 parc_chaine=∗parc_tbl ;1213 while ( parc_chaine !=NULL)14 {15 i f ( i == sommet && parc_chaine−>noeud == suivant )16 {17 poid = parc_chaine−>poid ;18 return poid ;19 }2021 parc_chaine=parc_chaine−>su iv ;22 }2324 parc_tbl++;25 }2627 return poid ;28 }

2.6.2 Trouver la distances minimum depuis le tableau des distancesCette fonction permet à l’algorithme de Dijkstra de travailler sur le tableau des distances, et

renvoie le sommet correspondant à la distance la plus petite qui n’a pas déjà été verrouillée.

1 int f ind_dist_min ( s t r_d i j k s t r a ∗ d i s tance s , int nbr_noeud )2 {3 // Le premier noeud e s t t ou j our s l e po in t source4 int i = 0 , minimum = INF , sommet = −1;56 for ( i = 0 ; i < nbr_noeud ; i++)7 {

13

8 i f ( ( d i s t an c e s+i )−>data < minimum && ( d i s t an c e s+i )−>verrou == 0)9 {

10 minimum = ( d i s t an c e s+i )−>data ;11 sommet = i ;12 }13 }1415 return sommet ;16 }

2.6.3 Fonction permettant de retrouver les successeurs d’un sommetCette fonction travail sur un tableau temporaire et y stock tous les successeurs d’un sommet

donné. Elle retourne en outre le nombre de successeurs.

1 int f i nd_succe s s eu r s ( int sommet , l i s t e ∗∗deb , int nbr_noeud ,s t r_d i j k s t r a ∗ temp)

2 {3 int i = 0 ;4 l i s t e ∗parc_chaine = NULL;56 parc_chaine=∗(deb+sommet ) ;78 while ( parc_chaine !=NULL)9 {

10 temp [ parc_chaine−>noeud ] . data = 1 ;11 parc_chaine=parc_chaine−>su iv ;12 i++;13 }1415 return i ;16 }

2.6.4 Fonction de verrouillage des deux tableauxComme expliqué précédemment, les deux tableau se verrouillent systématiquement ensemble.

Il est donc plus simple de créer une fonction à cet égard.

1 void v e r r o u i l l e r ( int sommet , s t r_d i j k s t r a ∗ d i s tance s , s t r_d i j k s t r a ∗pr edec e s s eu r s )

2 {3 ( d i s t an c e s+sommet )−>verrou = 1 ;4 ( p r ede c e s s eu r s+sommet )−>verrou = 1 ;5 }

2.6.5 Initialisation nécessaire à l’algorithme de DijkstraL’algorithme de Dijkstra travaille sur deux tableau, comme explicité précédemment, mais il

faut au préalable préparer ces tableaux. Initialiser le tableau des distances ainsi que le tableau desprédécesseurs et verrouiller le premier élément, le sommet source.

14

1 void i n i t ( l i s t e ∗∗ deb , s t r_d i j k s t r a ∗ predece s s eur s , s t r_d i j k s t r a ∗d i s tance s , int nbr_noeud )

2 {3 /∗4 Permet d ’ i n i t i a l i s e r correctement l e s deux5 t a b l e aux contenant l e s d i s t an c e s e t l e s p redece s s eur s6 en parcourant l a l i s t e de l i s t e s cha inees7 ∗/89 l i s t e ∗parc_chaine = NULL;

1011 int i =0, poid = INF ; // INF = 9999999991213 // I n i t i a l i s a t i o n des va l e u r s de verrou e t des predece s s eur s14 for ( i = 0 ; i < nbr_noeud ; i ++)15 {16 ( d i s t an c e s+i )−>data = poid ;17 ( p r ede c e s s eu r s+i )−>data = 0 ;181920 ( d i s t an c e s+i )−>verrou = 0 ;21 ( p r ede c e s s eu r s+i )−>verrou = 0 ;22 }2324 d i s t an c e s −> verrou = 1 ; // Verou i l l a g e du premier e lement25 pr edec e s s eu r s −> verrou = 1 ; //2627 parc_chaine=∗deb ;2829 // i n i t i a l i s a t i o n des va l e u r s des d i s t anc e s30 while ( parc_chaine !=NULL)31 {32 d i s t an c e s [ parc_chaine−>noeud ] . data = parc_chaine−>poid ;33 parc_chaine=parc_chaine−>su iv ;34 }35 }

2.6.6 L’algorithme en lui mêmeCi après le code de l’algorithme de Dijkstra comme défini plus haut, en utilisant la structure

de stockage du graphe donnée.

1 int a lgo ( l i s t e ∗∗ deb , s t r_d i j k s t r a ∗ predece s s eur s , s t r_d i j k s t r a ∗d i s tance s , int nbr_noeud )

2 {34 /∗5 Fonction r e cu r s i v e implementant l ’ a l gor i thme de D i j k s t r a .6 Cette f onc t i on rempl ie correctement l e s deux t a b l e aux7 de d i s t anc e s e t de predece s s eur s .8 Les noeux sans predece s s eur s ont une d i s t ance a l a source de

999999999.9 ∗/

1011 s t r_d i j k s t r a ∗temp = NULL;

15

12 temp = ( s t r_d i j k s t r a ∗) c a l l o c ( nbr_noeud , s izeof ( s t r_d i j k s t r a ) ) ;131415 int noeud_plus_proche = 0 ;16 int nbr_successeurs = 0 ;17 int i = 0 , temp_int = 0 ;1819 noeud_plus_proche = find_dist_min ( d i s tance s , nbr_noeud ) ;2021 i f ( noeud_plus_proche == −1)22 {23 per ro r ( "\nPas de p o s s i b i l i t e d ’ app l i que r l ’ a lgo encore une f o i s " ) ;24 return 1 ;25 }2627 // Le t a b l e au temp con t i en t l a data = 1 s i l e sommet correspondant a

l a p o s i t i o n dans l e t a b l e au e s t un predeces seur .28 nbr_successeurs = f ind_succe s s eu r s ( noeud_plus_proche , deb , nbr_noeud ,

temp) ;2930 // Remplissage des t a b e l aux de d i s t anc e s e t des p redece s s eur s .31 for ( i = 0 ; i < nbr_noeud ; i++ )32 {33 i f ( ( temp+i )−>data == 1 && ( d i s t an c e s+i )−>verrou == 0)34 {35 temp_int = ( d i s t an c e s+noeud_plus_proche )−>data + get_poid ( deb ,

nbr_noeud , noeud_plus_proche , i ) ;3637 ( p r ede c e s s eu r s+i )−>data = noeud_plus_proche ;3839 i f ( ( d i s t an c e s+i )−>data > temp_int ) ( d i s t an c e s+i )−>data =

temp_int ;40 }41 }4243 // Ver rou i l l a g e des noeux concernes .44 v e r r o u i l l e r ( noeud_plus_proche , d i s tance s , p r ede c e s s eu r s ) ;4546 // Desa l l o ca t i on de l a memoire .47 f r e e ( temp) ;4849 return a lgo ( deb , predece s s eur s , d i s tance s , nbr_noeud ) ;50 }

2.6.7 La fonction mainCi après, la fonction main() donne l’ordre d’appel des différentes fonctions.

1 int main ( int argc , char ∗argv [ ] )2 {3 i f ( argv [ 1 ] == NULL)4 {5 p r i n t f ( "Error : no input f i l e . \ n" ) ;6 return 1 ;7 }

16

89 FILE∗ src_graph=fopen ( argv [ 1 ] , " r " ) ;

1011 l i s t e ∗∗deb = NULL;1213 s t r_d i j k s t r a ∗ d i s t an c e s = NULL;14 s t r_d i j k s t r a ∗ pr ede c e s s eu r s = NULL;151617 int nbr_noeud = 0 ;1819 deb = gene r a t i on_ l i s t e ( src_graph , &nbr_noeud ) ;2021 d i s t an c e s = ( s t r_d i j k s t r a ∗) c a l l o c ( nbr_noeud , s izeof ( s t r_d i j k s t r a ) ) ;22 pr edec e s s eu r s = ( s t r_d i j k s t r a ∗) c a l l o c ( nbr_noeud , s izeof ( s t r_d i j k s t r a )

) ;2324 i n i t ( deb , p redece s s eur s , d i s tance s , nbr_noeud ) ;2526 a lgo ( deb , predece s s eur s , d i s tance s , nbr_noeud ) ;2728 p r i n t f ( "\n" ) ;29 a f f_s t r_d i j k s t r a ( d i s tance s , nbr_noeud , " d i s t anc e " ) ;30 p r i n t f ( "\n" ) ;3132 a f f_s t r_d i j k s t r a ( predece s s eur s , nbr_noeud , " pr edece s s eu r " ) ;33 p r i n t f ( "\n" ) ;3435 /∗ to_graphviz ( deb , nbr_noeud ) ; ∗/36 noeud∗ r=(noeud ∗) mal loc ( s izeof ( noeud ) ) ;3738 r=algo_k ( r , deb , ( ( ∗ deb )−>nbr_successeurs ) ) ;3940 f c l o s e ( src_graph ) ;4142 // Desa l l o ca t i on de l a memoire .43 f r e e ( d i s t an c e s ) ;44 f r e e ( p r ede c e s s eu r s ) ;4546 return 0 ;47 }

17

Chapitre 3

Problème des K-plus courts chemins

3.1 Présentation du problèmeLe but est d’implémenter un algorithme qui permettra d’afficher tous les chemins d’un nœud de

départ à nœud d’arrivée dans un graphe. Pour résoudre ce problème nous avons décider de stockerces chemins dans un arbre qui contiendra les numéros des nœuds et le poids de l’arc avec le nœudparent.

Figure 3.1 – Schéma du graphe considéré

Ainsi ce graphe donnerai :

Figure 3.2 – Arbre du graphe considéré

Cet arbre représente les chemins possibles pour aller de 0 à 3. La branche qui se termine par le4 est une branche "terminale", c’est à dire qu’elle ne pourra jamais mener à 3, elle ne représentedonc pas un chemin. pour trouver les k plus court chemins il nous suffit de parcourir l’arbre et decompter la somme des poids de chaque branche. Chaque branche se terminant par une feuille 3représente un chemin.

18

3.2 Analyse de l’algorithme des k-plus court cheminsComme dit précédemment, l’algorithme des k-plus court chemins utilisé est un algorithme qui

créé un arbre contenant la liste de tout les chemins possibles d’un point à un autre.

L’algorithme théorique se définit comme suit :

1 Algorithme k p lus court ( noeud i n i t i a l , noeud f i n a l )2 r a c i n e de l ’ arbre = noeud i n i t i a l3 de 0 a nombre de noeud f a i r e :4 s i nombre de f i l s = 0 f a i r e5 r e tourne r noeud i n i t i a l6 s inon f a i r e7 chaque suc c e s s eu r dev i ent un f i l s .8 pour chaque f i l s f a i r e :9 s i f i l s d i f f e r e nd de noeud f i n a l f a i r e

10 f i l s = Algorithme k plus court ( f i l s , noeud f i n a l )11 s inon s i f i l s = noeud f i n a l f a i r e12 r e tourne r f i l s13 f i n de14 r e tourne r noeud i n i t i a l15 f i n Algorithme k plus court

L’algorithme consiste à créer, de manière récursive, la liste de fils du nœud actuel (en fonctiondes successeurs). Puis pour chacun des fils on répète l’opération, ainsi à la fin on obtient bien unestructure semblable à celle présenté dans la partie structure de données utilisée.

3.3 Commentaires sur l’implémentationEn ce qui concerne l’implémentation de l’algorithme nous n’avons pas réussit à aboutir malgré

plusieurs essais et reprises du code. Nous avons cependant réussit à implémenter la partie qui créeles fils correspondants aux successeurs. Il manque donc la partie récursive de notre algorithme.

3.3.1 Fonction d’affichage de l’arbreCette fonction à simplement pour but d’afficher l’arbre contenant tout les successeurs pour

pouvoir visualiser les chemins.

1 int afficher_arbre_RGD ( noeud∗ r a c i n e )2 {3 p r i n t f ( "Debut a f f i c h a g e \n" ) ;4 i f ( r a c i n e==NULL)5 {6 p r i n t f ( " r a c i n e nu l l . \n" ) ;7 return 0 ;8 }9 else

10 {11 l_Arbre∗ parc=(rac ine−>f i l s ) ;12 // on a f f i c h e l e pere13 p r i n t f ( "Sommet %d : " , rac ine−>valeur_sommet ) ;14 p r i n t f ( "Poid_prec %d | " , rac ine−>poid_prec_noeurd ) ;15 // on passe au f i l s16 while ( parc−>noeud_A!=NULL )17 {18 afficher_arbre_RGD ( parc−>noeud_A) ;

19

19 parc=parc−>su iv ;20 }21 return 1 ;2223 }24 }

3.3.2 Fonction de création des nœuds des successeursCelte fonction à pour but de créer la liste des nœuds fils contenant les successeurs du nœud

racine envoyé en paramètre. Elle retourne un pointeur vers cette liste de fils.

1 l_Arbre∗ c ree rSucc ( noeud∗ rac ine , l i s t e ∗∗ deb , int nbr_noeud )2 {3 int i =0, j =0;45 i f (∗deb==NULL) // s i l a l i s t e des succe s s eur s e s t v i de on ne cree

r i en6 {7 return NULL;8 }9 // on recupere l e nombre de succe s s eur s

10 int nbr_successeurs =((∗deb )−>nbr_successeurs ) ;11 p r i n t f ( " nbr_successeurs=%d\n" , nbr_successeurs ) ;1213 rac ine−>f i l s = ( l_Arbre ∗) mal loc ( s izeof ( l_Arbre ) ) ;14 l_Arbre∗ parc= rac ine−>f i l s ;15 l i s t e ∗ parc_l = ∗deb ;1617 while ( i<nbr_successeurs ) // pour chacun des succe s s eur s18 {1920 p r i n t f ( " f i l s %d \n" , i ) ;21 // on cree un noeud f i l s correspondant au succe s seur22 noeud∗ temp = ( noeud ∗) mal loc ( s izeof ( noeud ) ) ;23 // on r e l i e ce noeud a son pere24 parc−>noeud_A = temp ;25 // on met l e s bonnes va l e u r s dans ce noeud26 parc−>noeud_A−>valeur_sommet=parc_l−>noeud ;27 parc−>noeud_A−>poid_prec_noeurd=parc_l−>poid ;28 parc−>noeud_A−>pere=rac i n e ;29 parc−>noeud_A−>f i l s=NULL;30 p r i n t f ( "%d , %d \n" , parc−>noeud_A−>valeur_sommet , parc−>noeud_A−>

poid_prec_noeurd ) ;31 i f ( i !=nbr_successeurs ) // s i ce n ’ e s t pas l e dern i e r succes seur32 {33 // on a l l o u e de l a p l ace pour l e noeud su i van t34 parc−>su iv=(l_Arbre ∗) mal loc ( s izeof ( l_Arbre ) ) ;35 }36 // on passe au su i van t37 parc=parc−>su iv ;38 parc_l=parc_l−>su iv ;39 i++;40 }41 parc=NULL;

20

4243 return rac ine−>f i l s ; // on renvo ie l a l i s t e des f i l s du noeud pere4445 }

3.3.3 Fonction algorithme des k plus court cheminsCette fonction à normalement pour but de dérouler l’algorithme. Mais dans notre cas comme

l’implémentation n’as rien donnée elle ne déroulera l’algorithme que sur le nœud de départ et sesfils.

1 noeud∗ algo_k ( noeud∗ rac ine , l i s t e ∗∗ deb , int nbr_noeud )2 {34 int r e t =0, i =0, nbr_succ=0;5 l_Arbre∗ f i l s =(l_Arbre ∗) mal loc ( s izeof ( l_Arbre ) ) ;6 l_Arbre∗ tmp=(l_Arbre ∗) mal loc ( s izeof ( l_Arbre ) ) ;7 l i s t e ∗∗ parc_deb = deb ;89 // i n i t i a l i s a t i o n : sommet 0

10 // on cree l e s f i l s du somet 011 f i l s=cree rSucc ( rac ine , parc_deb , nbr_noeud ) ;1213 nbr_succ=(∗parc_deb )−>nbr_successeurs ;1415 // pour chacun des succe s s eur s1617 for ( i =0; i<nbr_succ ; i++)18 {19 // succes seur i20 p r i n t f ( "\n Sommet : %d \n " , f i l s −>noeud_A−>valeur_sommet ) ;21 parc_deb= ( deb + f i l s −>noeud_A−>valeur_sommet ) ;22 // on cree l e s f i l s de chacun des sommet correspondant au

succe s s eur s .23 tmp=cree rSucc ( f i l s −>noeud_A , parc_deb , nbr_noeud ) ;24 f i l s=f i l s −>su iv ;25 }2627 p r i n t f ( " lancement a f f i c h a g e \n" ) ;2829 // a f f i c h a g e de l ’ arbre30 r e t = afficher_arbre_RGD ( ra c i n e ) ;31 p r i n t f ( "%d\n" , r e t ) ;3233 return r a c i n e ;34 }

21