Graphs & Algorithmie : Recherches sur l’implémentation
des algorithmes liés à Dijkstra et au problème des K-plus
courts ch...
Table des matières
1 Structures de donnée et outils divers 2
1.1 Graphviz . . . . . . . . . . . . . . . . . . . . . . . . ...
Chapitre 1
Structures de donnée et outils divers
1.1 Graphviz
Graphviz est une suite d’utilitaires sous système Unix qui p...
Mais cet utilitaire prend tous son sens lors de tracés de graphes plus importants, lorsque le
tracé devient réellement fas...
1 #include <stdio . h>
2 #include <s t d l i b . h>
3 #include <s t r i n g . h>
4
5 // Structure de donnee permettant d ’...
53 parc_chaine−>poid = poid_arc ;
54 parc_chaine−>suiv = NULL;
55 i f ( prec == NULL) ∗ parc_tbl = parc_chaine ;
56 else p...
1.3 Structure de donnée liée au stockage du graphe
La structure de donnée ici présentée est celle mise en place dans le pr...
2 struct str_dijkstra
3 {
4 int noeud ;
5 int data ; // La donnee peut etre : distance , ou n_o du predecesseur
6 int verr...
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ème
Le cheminement dans les graphes, et plus précisément...
21 FinPour
22 FinTantQue
2.3 Commentaires sur l’implémentation
Pour implémenter cet algorithme, nous avons repris la struc...
26 p r i n t f ( "}n" ) ;
27
28 return 1;
29 }
2.4.2 Fonction d’affichage des tableaux liés à la structure de donnée
spécifiq...
22 {
23 f s c a n f ( src_graph , "%d %d" , &no_noeud,& nbr_successeurs ) ;
24 prec = NULL;
25
26 i f ( nbr_successeurs )
...
9 }
10
11 }
2.6 Fonctions utiles à l’algorithme de Dijkstra
2.6.1 Fonction retournant le poids de l’arc entre deux sommets...
8 i f (( distances+i )−>data < minimum && ( distances+i )−>verrou == 0)
9 {
10 minimum = ( distances+i )−>data ;
11 sommet...
1 void i n i t ( l i s t e ∗∗ deb , str_dijkstra ∗ predecesseurs , str_dijkstra ∗
distances , int nbr_noeud )
2 {
3 /∗
4 P...
12 temp = ( str_dijkstra ∗) c a l l o c ( nbr_noeud , sizeof ( str_dijkstra ) ) ;
13
14
15 int noeud_plus_proche = 0;
16 i...
8
9 FILE∗ src_graph=fopen ( argv [ 1 ] , " r " ) ;
10
11 l i s t e ∗∗deb = NULL;
12
13 str_dijkstra ∗ distances = NULL;
14...
Chapitre 3
Problème des K-plus courts chemins
3.1 Présentation du problème
Le but est d’implémenter un algorithme qui perm...
3.2 Analyse de l’algorithme des k-plus court chemins
Comme dit précédemment, l’algorithme des k-plus court chemins utilisé...
19 parc=parc−>suiv ;
20 }
21 return 1;
22
23 }
24 }
3.3.2 Fonction de création des nœuds des successeurs
Celte fonction à ...
42
43 return racine −>f i l s ; // on renvoie la l i s t e des f i l s du noeud pere
44
45 }
3.3.3 Fonction algorithme des...
Prochain SlideShare
Chargement dans…5
×

Dijkstra kshortest

282 vues

Publié le

Dijkstra

Publié dans : Ingénierie
0 commentaire
0 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

  • Soyez le premier à aimer ceci

Aucun téléchargement
Vues
Nombre de vues
282
Sur SlideShare
0
Issues des intégrations
0
Intégrations
4
Actions
Partages
0
Téléchargements
3
Commentaires
0
J’aime
0
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

Dijkstra kshortest

  1. 1. 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
  2. 2. Table des matières 1 Structures de donnée et outils divers 2 1.1 Graphviz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.2 Conversion d’une méthode de saisie simple vers graphviz . . . . . . . . . . . . . . . 3 1.3 Structure de donnée liée au stockage du graphe . . . . . . . . . . . . . . . . . . . . 6 1.4 Structure de donnée liée à l’algorithme de Dijkstra . . . . . . . . . . . . . . . . . . 6 1.5 Structure de donnée nécessaire à l’algorithme des k-plus courts chemins . . . . . . 7 2 Problème du plus court chemin 9 2.1 Présentation du problème . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.2 Analyse de l’algorithme de Dijkstra . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.3 Commentaires sur l’implémentation . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.4 Fonctions utiles à l’affichage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.4.1 Fonction nécessaire à l’obtention du code Graphviz . . . . . . . . . . . . . . 10 2.4.2 Fonction d’affichage des tableaux liés à la structure de donnée spécifique à Dijkstra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.5 Fonctions spécifiques au structures de données utilisées . . . . . . . . . . . . . . . . 11 2.5.1 Fonction de génération du graphe . . . . . . . . . . . . . . . . . . . . . . . . 11 2.5.2 Fonction d’affichage des tableaux liés à la structure de donnée spécifique à Dijkstra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.6 Fonctions utiles à l’algorithme de Dijkstra . . . . . . . . . . . . . . . . . . . . . . . 13 2.6.1 Fonction retournant le poids de l’arc entre deux sommets . . . . . . . . . . 13 2.6.2 Trouver la distances minimum depuis le tableau des distances . . . . . . . . 13 2.6.3 Fonction permettant de retrouver les successeurs d’un sommet . . . . . . . 14 2.6.4 Fonction de verrouillage des deux tableaux . . . . . . . . . . . . . . . . . . 14 2.6.5 Initialisation nécessaire à l’algorithme de Dijkstra . . . . . . . . . . . . . . . 14 2.6.6 L’algorithme en lui même . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.6.7 La fonction main . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3 Problème des K-plus courts chemins 18 3.1 Présentation du problème . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.2 Analyse de l’algorithme des k-plus court chemins . . . . . . . . . . . . . . . . . . . 19 3.3 Commentaires sur l’implémentation . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.3.1 Fonction d’affichage de l’arbre . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.3.2 Fonction de création des nœuds des successeurs . . . . . . . . . . . . . . . . 20 3.3.3 Fonction algorithme des k plus court chemins . . . . . . . . . . . . . . . . . 21 1
  3. 3. Chapitre 1 Structures de donnée et outils divers 1.1 Graphviz Graphviz 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éer un utilitaire simple pour convertir une structure de graphe simpliste en langage interprétable par un des programmes de Graphviz : “dot”. Le langage interprétable par Graphviz se présente comme suite : 1 digraph G 2 { 3 edge [ color=purple , arrowsize =2]; 4 node [ color=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
  4. 4. Mais cet utilitaire prend tous son sens lors de tracés de graphes plus importants, lorsque le tracé 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 graphviz Le 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éthode de 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 poids de l’arc entre eux. Pour plus de facilité, la première ligne doit comporter le nombre de sommets du graphe. Ce qui se traduit par l’écriture suivante pour le cas de la figure 1.1 : 1 4 2 0 2 1 1 3 2 3 1 2 2 3 3 4 4 2 1 3 5 5 3 0 Le code source de ce programme est donné ci après. Les explications se trouvent à la suite du code. 3
  5. 5. 1 #include <stdio . h> 2 #include <s t d l i b . h> 3 #include <s t r i n g . h> 4 5 // Structure de donnee permettant d ’ obtenir des l i s t e s chainees 6 typedef struct l i s t e l i s t e ; 7 struct l i s t e 8 { 9 int noeud ; 10 int poid ; 11 l i s t e ∗ suiv ; 12 }; 13 14 int main ( int argc , char ∗argv [ ] ) 15 { 16 17 // Ouverture du f i c h i e r comportant l e code s i m p l i f i e du graphe . 18 FILE∗ src_graph=fopen ( argv [ 1 ] , " r " ) ; 19 20 // Afin de ne pas surcharger l e code , l e t e s t l i e a l ’ existence du parametre ou du f i c h i e r n ’ apparait pas . 21 22 // L ’ u t i l i s a t i o n des l i s t e s chainees et de l ’ a l l o c a t i o n memoire via des malloc permet d ’ optimiser l ’ espace memoire u t i l i s e pour stocker un graphe . Ceci permet egalement de t r a i t e r toute t a i l l e de graphe . 23 24 l i s t e ∗∗deb=NULL; 25 l i s t e ∗∗ parc_tbl=NULL; 26 27 l i s t e ∗parc_chaine=NULL; 28 l i s t e ∗ prec=NULL; 29 30 int nbr_noeud=0,no_noeud=0, nbr_successeurs =0; 31 int no_succ=0, poid_arc=0; 32 33 int i =0; 34 35 f s c a n f ( src_graph , "%d" , &nbr_noeud ) ; 36 37 deb = ( l i s t e ∗∗) malloc ( nbr_noeud ∗ sizeof ( l i s t e ∗ ) ) ; 38 parc_tbl=deb ; 39 40 for ( i =0; i<nbr_noeud ; i++) 41 { 42 43 f s c a n f ( src_graph , "%d %d" , &no_noeud,& nbr_successeurs ) ; 44 prec = NULL; 45 46 i f ( nbr_successeurs ) 47 { 48 while ( nbr_successeurs != 0) 49 { 50 parc_chaine = ( l i s t e ∗) malloc ( sizeof ( 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
  6. 6. 53 parc_chaine−>poid = poid_arc ; 54 parc_chaine−>suiv = NULL; 55 i f ( prec == NULL) ∗ parc_tbl = parc_chaine ; 56 else prec−>suiv = parc_chaine ; 57 prec = parc_chaine ; 58 nbr_successeurs −−; 59 } 60 } 61 else 62 { 63 ∗ parc_tbl = NULL; 64 } 65 parc_tbl++; 66 } 67 68 // Ci apres , la l e c t u r e du graphe donnant l e code necessaire au bon fonctionnement des u t i l i t a i r e s de graphviz . 69 70 // Cet algorithme revient a parcourir simplement la structure de donnee u t i l i s e e pour stocker l e graphe . Cette structure est presentee dans un autre paragraphe . 71 72 parc_tbl = deb ; 73 p r i n t f ( " digraph G {n" ) ; 74 p r i n t f ( "edge [ color=purple , arrowsize =2];n" ) ; 75 p r i n t f ( "node [ color=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−>suiv ; 83 } 84 parc_tbl++; 85 } 86 p r i n t f ( "}n" ) ; 87 88 f c l o s e ( src_graph ) ; 89 90 return 0; 91 } Le fonctionnement de ce programme est simple, il prend en paramètre (lors de l’appel de l’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 code gé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’appel né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 listes chainées qui sera détaillée dans la prochaine section. 5
  7. 7. 1.3 Structure de donnée liée au stockage du graphe La 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 celui des k-plus courts chemins. Pour faire simple, cette structure de donnée se présente sous la forme d’un tableau d’éléments de type liste correspondant à chaque sommet. Chacun de ces sommets est ensuite lié à tous ses successeurs 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ée pour y adapter tout type d’algorithme issu de la théorie des graphes. La structure de donnée est la suivante : 1 // Structure de donnee permettant d ’ obtenir l e s l i s t e s chainees 2 typedef struct l i s t e l i s t e ; 3 struct l i s t e 4 { 5 int noeud ; 6 int poid ; 7 l i s t e ∗ suiv ; 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. En effet, 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 Dijkstra Pour 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 structure suffit 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 str_dijkstra str_dijkstra ; 6
  8. 8. 2 struct str_dijkstra 3 { 4 int noeud ; 5 int data ; // La donnee peut etre : distance , ou n_o du predecesseur 6 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érification quand au bon fonctionnement de l’algorithme. 1.5 Structure de donnée nécessaire à l’algorithme des k-plus courts chemins Pour résoudre ce problème nous allons utiliser deux structures de données spécifiques. Une structure appelée "nœud", qui sera la structure de l’arbre. Cette structure contiendra la valeur du 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 noeud 2 { 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 pointeur vers un "nœud" de l’arbre, qui est le fils en question, ainsi qu’un pointeur vers le maillon suivant de la liste, le fils suivant. 1 struct l_Arbre 2 { 3 noeud∗ noeud_A ; 4 l_Arbre∗ suiv ; 5 }; C’est en combinant l’utilisation de ces deux structures que nous parviendrons à créer l’arbre représentant les chemins. Voici alors le schémas de la structure de l’arbre : 7
  9. 9. Figure 1.4 – Représentation des structures de donnée nécessaire à la création d’un arbre 8
  10. 10. Chapitre 2 Problème du plus court chemin 2.1 Présentation du problème Le 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 électriques ou réseaux d’information ou encore calcul d’itinéraire via des cartes, plusieurs algorithmes issus de la théorie des graphes permettent de répondre à ces problèmes mathématiques. Nous allons ici nous 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 courtes distances 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 Dijkstra Cet 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’algorithme comme suit : 1 Pour k de 1 a n , f a i r e : 2 distances [ k ] <−− cout ( s , k) ; 3 predecesseurs [ k ] <−− s ; 4 FinPour ; 5 M <−− Supprimer ( s , M) ; 6 7 TantQue (M non vide ) Faire : 8 i <−− LePlusProche (M) ; 9 Si ( distances [ i ] = i n f ) Alors retour ; 10 M <−− Supprimer ( i , M) ; 11 12 Pour k de 1 a d+( i ) Faire : 13 j <−− k−eme_successeur ( i ) ; 14 Si ( EstSupprime ( j , M) <> 1) 15 v <−− distances [ i ] + cout ( i , j ) ; 16 Si (v < distances [ j ] ) 17 Alors distances [ j ] <−− v ; 18 Alors predecesseurs [ j ] <−− i ; 19 FinSi 20 FinSi 9
  11. 11. 21 FinPour 22 FinTantQue 2.3 Commentaires sur l’implémentation Pour 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 de graphe. Il y a ensuite un certains nombre de fonction à développer pour faire tourner l’algorithme de Dijkstra. Certaines fonctionnalités trop souvent utilisées seront également isolée en tant que fonction 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épasser afin que les comparaisons fonctionnent. 1 #define INF 999999999 2.4 Fonctions utiles à l’affichage 2.4.1 Fonction nécessaire à l’obtention du code Graphviz Reprise 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 ne semble 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; 6 7 parc_tbl = deb ; 8 9 p r i n t f ( " digraph G {n" ) ; 10 p r i n t f ( "edge [ color=purple , arrowsize =2];n" ) ; 11 p r i n t f ( "node [ color=pink , s t y l e=f i l l e d ] ; n" ) ; 12 13 for ( i =0; i<nbr_noeud ; i++) 14 { 15 parc_chaine=∗parc_tbl ; 16 17 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−>suiv ; 21 } 22 23 parc_tbl++; 24 } 25 10
  12. 12. 26 p r i n t f ( "}n" ) ; 27 28 return 1; 29 } 2.4.2 Fonction d’affichage des tableaux liés à la structure de donnée spécifique à Dijkstra Cette petite fonction permet de vérifier que l’algorithme de Dijkstra effectue correctement le remplissage des tableaux en les affichant. 1 void aff_str_dijkstra ( str_dijkstra ∗ str , int nbr_noeud , char∗ mot) 2 { 3 4 int i =0; 5 6 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 } 10 11 } 2.5 Fonctions spécifiques au structures de données utilisées 2.5.1 Fonction de génération du graphe Comme 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 type tableau de listes chainées. 1 l i s t e ∗∗ generation_liste (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; 5 6 int i =0; 7 8 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; 12 13 f s c a n f ( src_graph , "%d" , &nbr_noeud ) ; 14 15 ∗noeuds = nbr_noeud ; 16 17 deb = ( l i s t e ∗∗) malloc ( nbr_noeud ∗ sizeof ( l i s t e ∗ ) ) ; 18 19 parc_tbl=deb ; 20 21 for ( i =0; i<nbr_noeud ; i++) 11
  13. 13. 22 { 23 f s c a n f ( src_graph , "%d %d" , &no_noeud,& nbr_successeurs ) ; 24 prec = NULL; 25 26 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 ∗) malloc ( sizeof ( l i s t e ) ) ; 32 33 f s c a n f ( src_graph , "%d %d" , &no_succ ,&poid_arc ) ; 34 35 parc_chaine−>noeud = no_succ ; 36 parc_chaine−>poid = poid_arc ; 37 parc_chaine−>suiv = NULL; 38 39 parc_chaine−>nbr_successeurs = nbr_successeurs_temp ; 40 41 i f ( prec == NULL) ∗ parc_tbl = parc_chaine ; 42 else prec−>suiv = parc_chaine ; 43 44 prec = parc_chaine ; 45 nbr_successeurs −−; 46 } 47 } 48 49 else 50 { 51 ∗ parc_tbl = NULL; 52 } 53 54 parc_tbl++; 55 } 56 57 return deb ; 58 } Cette fonction retourne le premier élément du tableau afin de pouvoir le parcourir dans les autres fonctions. 2.5.2 Fonction d’affichage des tableaux liés à la structure de donnée spécifique à Dijkstra Cette petite fonction permet de vérifier que l’algorithme de Dijkstra effectue correctement le remplissage des tableaux en les affichant. 1 void aff_str_dijkstra ( str_dijkstra ∗ str , int nbr_noeud , char∗ mot) 2 { 3 4 int i =0; 5 6 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
  14. 14. 9 } 10 11 } 2.6 Fonctions utiles à l’algorithme de Dijkstra 2.6.1 Fonction retournant le poids de l’arc entre deux sommets Cette 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é comme expliqué précédemment. 1 int get_poid ( l i s t e ∗∗ deb , int nbr_noeud , int sommet , int suivant ) 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 ; 6 7 parc_tbl = deb ; 8 9 for ( i =0; i<nbr_noeud ; i++) 10 { 11 parc_chaine=∗parc_tbl ; 12 13 while ( parc_chaine!=NULL) 14 { 15 i f ( i == sommet && parc_chaine−>noeud == suivant ) 16 { 17 poid = parc_chaine−>poid ; 18 return poid ; 19 } 20 21 parc_chaine=parc_chaine−>suiv ; 22 } 23 24 parc_tbl++; 25 } 26 27 return poid ; 28 } 2.6.2 Trouver la distances minimum depuis le tableau des distances Cette 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 find_dist_min ( str_dijkstra ∗ distances , int nbr_noeud ) 2 { 3 // Le premier noeud est toujours l e point source 4 int i = 0 , minimum = INF , sommet = −1; 5 6 for ( i = 0; i < nbr_noeud ; i++) 7 { 13
  15. 15. 8 i f (( distances+i )−>data < minimum && ( distances+i )−>verrou == 0) 9 { 10 minimum = ( distances+i )−>data ; 11 sommet = i ; 12 } 13 } 14 15 return sommet ; 16 } 2.6.3 Fonction permettant de retrouver les successeurs d’un sommet Cette 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 find_successeurs ( int sommet , l i s t e ∗∗deb , int nbr_noeud , str_dijkstra ∗ temp) 2 { 3 int i = 0; 4 l i s t e ∗parc_chaine = NULL; 5 6 parc_chaine=∗(deb+sommet) ; 7 8 while ( parc_chaine!=NULL) 9 { 10 temp [ parc_chaine−>noeud ] . data = 1; 11 parc_chaine=parc_chaine−>suiv ; 12 i ++; 13 } 14 15 return i ; 16 } 2.6.4 Fonction de verrouillage des deux tableaux Comme 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 , str_dijkstra ∗ distances , str_dijkstra ∗ predecesseurs ) 2 { 3 ( distances+sommet)−>verrou = 1; 4 ( predecesseurs+sommet)−>verrou = 1; 5 } 2.6.5 Initialisation nécessaire à l’algorithme de Dijkstra L’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 des prédécesseurs et verrouiller le premier élément, le sommet source. 14
  16. 16. 1 void i n i t ( l i s t e ∗∗ deb , str_dijkstra ∗ predecesseurs , str_dijkstra ∗ distances , int nbr_noeud ) 2 { 3 /∗ 4 Permet d ’ i n i t i a l i s e r correctement l e s deux 5 tableaux contenant l e s distances et l e s predecesseurs 6 en parcourant la l i s t e de l i s t e s chainees 7 ∗/ 8 9 l i s t e ∗parc_chaine = NULL; 10 11 int i =0, poid = INF ; // INF = 999999999 12 13 // I n i t i a l i s a t i o n des valeurs de verrou et des predecesseurs 14 for ( i = 0 ; i < nbr_noeud ; i ++) 15 { 16 ( distances+i )−>data = poid ; 17 ( predecesseurs+i )−>data = 0; 18 19 20 ( distances+i )−>verrou = 0; 21 ( predecesseurs+i )−>verrou = 0; 22 } 23 24 distances −> verrou = 1; // Verouillage du premier element 25 predecesseurs −> verrou = 1; // 26 27 parc_chaine=∗deb ; 28 29 // i n i t i a l i s a t i o n des valeurs des distances 30 while ( parc_chaine!=NULL) 31 { 32 distances [ parc_chaine−>noeud ] . data = parc_chaine−>poid ; 33 parc_chaine=parc_chaine−>suiv ; 34 } 35 } 2.6.6 L’algorithme en lui même Ci 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 algo ( l i s t e ∗∗ deb , str_dijkstra ∗ predecesseurs , str_dijkstra ∗ distances , int nbr_noeud ) 2 { 3 4 /∗ 5 Fonction recursive implementant l ’ algorithme de Dijkstra . 6 Cette fonction remplie correctement l e s deux tableaux 7 de distances et de predecesseurs . 8 Les noeux sans predecesseurs ont une distance a la source de 999999999. 9 ∗/ 10 11 str_dijkstra ∗temp = NULL; 15
  17. 17. 12 temp = ( str_dijkstra ∗) c a l l o c ( nbr_noeud , sizeof ( str_dijkstra ) ) ; 13 14 15 int noeud_plus_proche = 0; 16 int nbr_successeurs = 0; 17 int i = 0 , temp_int = 0; 18 19 noeud_plus_proche = find_dist_min ( distances , nbr_noeud ) ; 20 21 i f ( noeud_plus_proche == −1) 22 { 23 perror ( "nPas de p o s s i b i l i t e d ’ appliquer l ’ algo encore une f o i s " ) ; 24 return 1; 25 } 26 27 // Le tableau temp contient la data = 1 s i l e sommet correspondant a la position dans l e tableau est un predecesseur . 28 nbr_successeurs = find_successeurs ( noeud_plus_proche , deb , nbr_noeud , temp) ; 29 30 // Remplissage des tabelaux de distances et des predecesseurs . 31 for ( i = 0 ; i < nbr_noeud ; i++ ) 32 { 33 i f (( temp+i )−>data == 1 && ( distances+i )−>verrou == 0) 34 { 35 temp_int = ( distances+noeud_plus_proche )−>data + get_poid ( deb , nbr_noeud , noeud_plus_proche , i ) ; 36 37 ( predecesseurs+i )−>data = noeud_plus_proche ; 38 39 i f (( distances+i )−>data > temp_int ) ( distances+i )−>data = temp_int ; 40 } 41 } 42 43 // Verrouillage des noeux concernes . 44 v e r r o u i l l e r ( noeud_plus_proche , distances , predecesseurs ) ; 45 46 // Desallocation de la memoire . 47 f r e e (temp) ; 48 49 return algo ( deb , predecesseurs , distances , nbr_noeud ) ; 50 } 2.6.7 La fonction main Ci 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
  18. 18. 8 9 FILE∗ src_graph=fopen ( argv [ 1 ] , " r " ) ; 10 11 l i s t e ∗∗deb = NULL; 12 13 str_dijkstra ∗ distances = NULL; 14 str_dijkstra ∗ predecesseurs = NULL; 15 16 17 int nbr_noeud = 0; 18 19 deb = generation_liste ( src_graph , &nbr_noeud ) ; 20 21 distances = ( str_dijkstra ∗) c a l l o c ( nbr_noeud , sizeof ( str_dijkstra ) ) ; 22 predecesseurs = ( str_dijkstra ∗) c a l l o c ( nbr_noeud , sizeof ( str_dijkstra ) ) ; 23 24 i n i t ( deb , predecesseurs , distances , nbr_noeud ) ; 25 26 algo ( deb , predecesseurs , distances , nbr_noeud ) ; 27 28 p r i n t f ( "n" ) ; 29 aff_str_dijkstra ( distances , nbr_noeud , " distance " ) ; 30 p r i n t f ( "n" ) ; 31 32 aff_str_dijkstra ( predecesseurs , nbr_noeud , " predecesseur " ) ; 33 p r i n t f ( "n" ) ; 34 35 /∗ to_graphviz ( deb , nbr_noeud ) ; ∗/ 36 noeud∗ r=(noeud ∗) malloc ( sizeof ( noeud ) ) ; 37 38 r=algo_k ( r , deb , ( ( ∗ deb )−>nbr_successeurs ) ) ; 39 40 f c l o s e ( src_graph ) ; 41 42 // Desallocation de la memoire . 43 f r e e ( distances ) ; 44 f r e e ( predecesseurs ) ; 45 46 return 0; 47 } 17
  19. 19. Chapitre 3 Problème des K-plus courts chemins 3.1 Présentation du problème Le 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 stocker ces chemins dans un arbre qui contiendra les numéros des nœuds et le poids de l’arc avec le nœud parent. 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 le 4 est une branche "terminale", c’est à dire qu’elle ne pourra jamais mener à 3, elle ne représente donc pas un chemin. pour trouver les k plus court chemins il nous suffit de parcourir l’arbre et de compter la somme des poids de chaque branche. Chaque branche se terminant par une feuille 3 représente un chemin. 18
  20. 20. 3.2 Analyse de l’algorithme des k-plus court chemins Comme 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 plus court ( noeud i n i t i a l , noeud f i n a l ) 2 racine de l ’ arbre = noeud i n i t i a l 3 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 e 5 retourner noeud i n i t i a l 6 sinon f a i r e 7 chaque successeur devient 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 n d 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 sinon s i f i l s = noeud f i n a l f a i r e 12 retourner f i l s 13 f i n de 14 retourner noeud i n i t i a l 15 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 fonction des successeurs). Puis pour chacun des fils on répète l’opération, ainsi à la fin on obtient bien une structure semblable à celle présenté dans la partie structure de données utilisée. 3.3 Commentaires sur l’implémentation En 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ée les fils correspondants aux successeurs. Il manque donc la partie récursive de notre algorithme. 3.3.1 Fonction d’affichage de l’arbre Cette fonction à simplement pour but d’afficher l’arbre contenant tout les successeurs pour pouvoir visualiser les chemins. 1 int afficher_arbre_RGD ( noeud∗ racine ) 2 { 3 p r i n t f ( "Debut a f f i c h a g e n" ) ; 4 i f ( racine==NULL) 5 { 6 p r i n t f ( " racine null . n" ) ; 7 return 0; 8 } 9 else 10 { 11 l_Arbre∗ parc=(racine −>f i l s ) ; 12 // on a f f i c h e l e pere 13 p r i n t f ( "Sommet %d : " , racine −>valeur_sommet ) ; 14 p r i n t f ( "Poid_prec %d | " , racine −>poid_prec_noeurd ) ; 15 // on passe au f i l s 16 while ( parc−>noeud_A!=NULL ) 17 { 18 afficher_arbre_RGD ( parc−>noeud_A) ; 19
  21. 21. 19 parc=parc−>suiv ; 20 } 21 return 1; 22 23 } 24 } 3.3.2 Fonction de création des nœuds des successeurs Celte 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∗ creerSucc ( noeud∗ racine , l i s t e ∗∗ deb , int nbr_noeud ) 2 { 3 int i =0, j =0; 4 5 i f (∗ deb==NULL) // s i la l i s t e des successeurs est vide on ne cree rien 6 { 7 return NULL; 8 } 9 // on recupere l e nombre de successeurs 10 int nbr_successeurs =((∗deb )−>nbr_successeurs ) ; 11 p r i n t f ( " nbr_successeurs=%dn" , nbr_successeurs ) ; 12 13 racine −>f i l s = ( l_Arbre ∗) malloc ( sizeof ( l_Arbre ) ) ; 14 l_Arbre∗ parc= racine −>f i l s ; 15 l i s t e ∗ parc_l = ∗deb ; 16 17 while ( i<nbr_successeurs ) // pour chacun des successeurs 18 { 19 20 p r i n t f ( " f i l s %d n" , i ) ; 21 // on cree un noeud f i l s correspondant au successeur 22 noeud∗ temp = ( noeud ∗) malloc ( sizeof ( noeud ) ) ; 23 // on r e l i e ce noeud a son pere 24 parc−>noeud_A = temp ; 25 // on met l e s bonnes valeurs dans ce noeud 26 parc−>noeud_A−>valeur_sommet=parc_l−>noeud ; 27 parc−>noeud_A−>poid_prec_noeurd=parc_l−>poid ; 28 parc−>noeud_A−>pere=racine ; 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 ’ est pas l e dernier successeur 32 { 33 // on a l l o u e de la place pour l e noeud suivant 34 parc−>suiv=(l_Arbre ∗) malloc ( sizeof ( l_Arbre ) ) ; 35 } 36 // on passe au suivant 37 parc=parc−>suiv ; 38 parc_l=parc_l−>suiv ; 39 i ++; 40 } 41 parc=NULL; 20
  22. 22. 42 43 return racine −>f i l s ; // on renvoie la l i s t e des f i l s du noeud pere 44 45 } 3.3.3 Fonction algorithme des k plus court chemins Cette 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 ses fils. 1 noeud∗ algo_k ( noeud∗ racine , l i s t e ∗∗ deb , int nbr_noeud ) 2 { 3 4 int ret =0, i =0, nbr_succ=0; 5 l_Arbre∗ f i l s =(l_Arbre ∗) malloc ( sizeof ( l_Arbre ) ) ; 6 l_Arbre∗ tmp=(l_Arbre ∗) malloc ( sizeof ( l_Arbre ) ) ; 7 l i s t e ∗∗ parc_deb = deb ; 8 9 // 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 0 11 f i l s=creerSucc ( racine , parc_deb , nbr_noeud ) ; 12 13 nbr_succ=(∗parc_deb )−>nbr_successeurs ; 14 15 // pour chacun des successeurs 16 17 for ( i =0; i<nbr_succ ; i++) 18 { 19 // successeur i 20 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 successeurs . 23 tmp=creerSucc ( f i l s −>noeud_A , parc_deb , nbr_noeud ) ; 24 f i l s=f i l s −>suiv ; 25 } 26 27 p r i n t f ( "lancement a f f i c h a g e n" ) ; 28 29 // a f f i c h a g e de l ’ arbre 30 ret = afficher_arbre_RGD ( racine ) ; 31 p r i n t f ( "%dn" , ret ) ; 32 33 return racine ; 34 } 21

×