Télécharger la présentation
La présentation est en train de télécharger. S'il vous plaît, attendez
Publié parFélicité Parmentier Modifié depuis plus de 10 années
1
IFT-2000: Structures de données Les graphes Dominic Genest, 2009
2
Les graphes Un graphe est un ensemble de nœuds connectés ou non par des arêtes. Les nœuds ont souvent de linformation qui leur est associée, dont une partie sert à les identifier. Linformation propre à un nœud et qui sert à lidentifier sappelle son étiquette. Ces arêtes ont souvent des poids numériques, qui représente la plupart du temps une forme de coût attribué au passage dun nœud à un autre par cette arête. Par exemple, dans le cas où les nœuds dun graphe représenteraient des villes, le poids pourrait représenter la distance entre deux villes. Il est à noter quun nœud peut ne pas être connecté à aucun autre, mais quand même faire partie du graphe. Par exemple, dans le schéma ci-bas, il sagit bel et bien dun seul graphe, mais avec deux composantes connexes. A F G D B C E 2 3 8 3 9 8 2 Dominic Genest, 2009
3
Les graphes Un nœud isolé est un nœud qui nest connecté à aucun autre. Un puits est un nœud qui nest pas isolé mais duquel ne démarre aucune arête. Dans le graphe ci-bas, les nœuds D et G sont des puits. Une source est un nœud qui nest pas isolé mais auquel narrivent aucune arête. Dans le graphe ci-bas, seul le nœud A est une source. A F G D B C E 2 3 8 3 9 8 2 Dominic Genest, 2009
4
Options dun graphe Un graphe peut être orienté ou non. – Dans un graphe orienté, les arêtes ont des pointes de flèches et représentent la possibilité de passer dun nœud à un autre, dans une seule direction. Dans un graphe orienté, si on veut permettre le passage dans les deux sens, il faut avoir deux arêtes. – Dans un graphe non orienté, une arête na pas de pointe de flèche et sert donc toujours à représenter la permission de passer dun nœud à un autre, dans les deux sens. – Le choix dutiliser un graphe orienté ou non dépend du contexte. Un graphe peut être pondéré ou non. – Un graphe pondéré est un graphe dans lequel chaque arête a une information numérique qui lui est associée, et qui doit être considérée dans certains algorithmes comme par exemple ceux qui tentent de trouver le chemin le plus court entre deux nœuds donnés. – Dans certains contextes, les arêtes peuvent être considérées comme ayant toutes un poids égal, et donc il est inutile dattribuer une valeur numérique à chaque arête. Il sagira alors dun graphe non pondéré. Un graphe peut être étiqueté ou non. – Dans certains contextes, on peut ne vouloir aucune étiquette attribuée aux nœuds. La façon didentifier un nœud en particulier sera, dans ces cas, peut- être lindice dun tableau du programme ou bien une adresse-mémoire. On peut en fait considérer que tout graphe est finalement étiqueté, mais que ce sont dans ces cas des éléments de limplémentation qui feront office détiquette. Dominic Genest, 2009
5
Un graphe étiqueté, orienté et pondéré A F G D B C E 2 3 8 3 9 8 2 Dominic Genest, 2009
6
Un graphe étiqueté, orienté et non pondéré A F G D B C E Dominic Genest, 2009
7
Un graphe étiqueté, non orienté et pondéré A F G D B C E 2 3 8 3 9 8 2 Dominic Genest, 2009
8
Un graphe non étiqueté, non orienté et non pondéré Dominic Genest, 2009
9
Les arêtes dun nœud vers lui-même Dans la plupart des cas, surtout pour les graphes pondérés, une arête dun nœud vers lui-même, de coût zéro, est sous-entendue pour chacun des nœuds du graphe. A F G D B C E 2 3 8 3 9 8 2 0 0 0 0 0 0 0 Dominic Genest, 2009
10
Implantation dun graphe struct route; typedef struct // Un noeud { char nom[100]; // Létiquette du noeud struct route routes[1000]; int nb_routes; } Ville; typedef struct route // Une arête { Ville *destination; float longueur; // Le poids de larête } Route; typedef struct // Un graphe { Ville *villes; int nb_villes; } Pays; Exemple #1: Le graphe contient un tableau dynamique de nœuds Chaque nœud contient un tableau statique darêtes Chaque arête contient un pointeur vers un nœud et un poids. Chaque route est représentée par DEUX objets « route », lun dans chacune des deux villes quelle connecte. Dominic Genest, 2009
11
Implémentation de quelques fonctions typedef enum { OK, Erreur, VilleIntrouvable, VillePleine } CodeErreur; CodeErreur initPays(Pays *pays) { pays->nb_villes=0; pays->villes=0; return OK; } CodeErreur ajoute_ville(Pays *pays, const char *nom) { Ville *v = realloc(pays- >villes,sizeof(Ville)*(pays->nb_villes+1)); Ville *nouvelle_ville; if(!v) return Erreur; pays->villes = v; nouvelle_ville = &v[pays->nb_villes++]; strcpy(nouvelle_ville ->nom,nom); nouvelle_ville->nb_routes=0; return OK; } static Ville *trouve_ville(Pays *pays, const char *nom) { Ville *v; for(v=pays->villes;v!=&pays->villes[pays- >nb_villes] && strcmp(v->nom,nom);v++); return v!=&pays->villes[pays->nb_villes] ? v : 0; } CodeErreur ajoute_route(Pays *pays, const char *nom_ville_1, const char *nom_ville_2, float distance) { Ville *v1 = trouve_ville(pays,nom_ville_1), *v2 = trouve_ville(pays,nom_ville_2); Route *r1, *r2; if(!v1 || !v2) return VilleIntrouvable; if(v1->nb_routes==100 || v2- >nb_routes==100) return VillePleine; r1 = &v1->routes[v1->nb_routes++]; r2 = &v2->routes[v2->nb_routes++]; r1->longueur = distance; r2->longueur = distance; r1->destination = v2; r2->destination = v1; return OK; } Dominic Genest, 2009
12
Implantation dun graphe struct route; typedef struct ville // Un noeud { char nom[100]; // Létiquette du noeud struct route *premiere_route; ville *ville_suivante; } Ville; typedef struct route // Une arête { char nom_ville_destination[100]; float longueur; // Le poids de larête struct route *route_suivante; } Route; typedef struct // Un graphe { Ville *premiere_ville; } Pays; Exemple #2: Le graphe contient le point de départ dune liste chaînée de nœuds Chaque nœud contient un point de départ dune liste chaînée darêtes Chaque arête contient un identifiant de nœud et un poids. Dominic Genest, 2009
13
Matrice dadjacence dun graphe pondéré Pour un graphe, une fois les nœuds déterminés, la présence darêtes et leurs poids peuvent être entièrement spécifiés par une matrice. Cette matrice doit avoir autant de rangées et de colonnes que de nœuds (donc autant de cases que le nombre de nœuds au carré). Dans le cas dun graphe pondéré, chaque case de cette matrice contient le poids de larête qui permet de passer du nœud de la rangée en question au nœud de la colonne en question. Linexistence darête est représentée par la valeur infini (). Dominic Genest, 2009
14
Matrice dadjacence dun graphe pondéré A F G D B C E 2 3 8 3 9 8 2 ABCDEFG A0823 B029 C08 D0 E0 F30 G0 Dominic Genest, 2009
15
Matrice dadjacence dun graphe non pondéré Pour un graphe non pondéré, chaque case de la matrice dadjacence contient un 1 sil y a une arête qui permet de passer du nœud de la rangée en question au nœud de la colonne en question, ou un 0 sinon. Dominic Genest, 2009
16
Matrice dadjacence dun graphe non pondéré A F G D B C E ABCDEFG A1100011 B0111000 C0011000 D0001000 E0000100 F0100010 G0000001 Dominic Genest, 2009
17
Implémentation par matrice dadjacence Si peu de nœuds sont nécessaires, il peut savérer intéressant dimplémenter un graphe par sa matrice dadjacence: #define INFINI 9999999.0f typedef float Graphe[20][20]; void initGraphe(Graphe g) { int i,j; for(i=0;i<20;i++) for(j=0;j<20;j++) g[i][j]=0; } void ajoutArete(Graphe g, int source, int destination, float poids) { g[source][destination]=poids; } On peut quand même associer de linformation complémentaire aux nœuds et aux arêtes avec des structures auxiliaires, parallèlement à la matrice dadjacence. typedef struct { char nom[100]; float longueur; } Route; typedef Arete Graphe[20][20]; typedef struct { char nom_ville[100]; int nb_habitants; int annee_fondation; } Ville; Typedef struct { Route routes[20][20]; // Ceci est une matrice dadjacence Ville villes[20]; } Pays; Dominic Genest, 2009
18
Matrices dadjacences A F G D B C E ABCDEFG A1100011 B0111000 C0011000 D0001000 E0000100 F0100010 G0000001 Les puits, les sources et les nœuds isolés sont facilement identifiables dans une matrice dadjacence. Dominic Genest, 2009
19
Fermeture transitive dun graphe La fermeture transitive dun graphe est un autre graphe, construit à partir du premier. Il sagit dune copie du graphe, à laquelle on ajoute des arêtes. Une arête est ajoutée pour chaque chemin indirect qui existe dans le graphe initial. Ainsi, soit un graphe donné, sa fermeture transitive est un graphe qui contient, en plus de toutes les arêtes, dautres arêtes indiquant toutes les connexions indirectes du graphe. De plus, pour les graphes pondérés, le poids de chaque arête du nouveau graphe donne le coût du chemin le plus court entre les deux nœuds de larête en question. Et ces coûts remplacent même, sil y a lieu, les coûts des arêtes existantes dans le graphe initial. Dominic Genest, 2009
20
Fermeture transitive dun graphe pondéré A F G D B C E 2 3 8 3 9 8 2 A F G D B C E 2 3 5 3 9 8 2 7 5 12 14 Sa fermeture transitive Dominic Genest, 2009 Un graphe pondéré
21
Fermeture transitive dun graphe non pondéré A F G D B C E A F G D B C E Sa fermeture transitive Dominic Genest, 2009 Un graphe non pondéré
22
Matrice dadjacence dune fermeture transitive dun graphe pondéré ABCDEFG A0823 B029 C08 D0 E0 F30 G0 ABCDEFG A0571423 B029 C08 D0 E0 F35120 G0 La matrice dadjacence dun graphe pondéré La matrice dadjacence de sa fermeture transitive Dominic Genest, 2009
23
Matrice dadjacence dune fermeture transitive dun graphe non pondéré ABCDEFG A1100011 B0111000 C0011000 D0001000 E0000100 F0100010 G0000001 ABCDEFG A1111011 B0111000 C0011000 D0001000 E0000100 F0111010 G0000001 La matrice dadjacence dun graphe non pondéré La matrice dadjacence de sa fermeture transitive Dominic Genest, 2009
24
Algorithme de Warshall Pour construire la matrice dadjacence de la fermeture transitive dun graphe non pondéré à partir de sa matrice dadjacence, il faut employer lalgorithme de Warshall: – Faire une copie du graphe. – Dans la copie, pour chaque nœud k: Pour chaque paire de nœuds {i,j}: – Sil nexiste pas déjà une arête entre i et j, vérifier sil existe un chemin qui va de i à k, puis un qui va de k à j. Si cest le cas, ajouter une arête de i à j. Remarque: la vérification dexistence dune arête se fait à même la copie, et non pas dans le graphe initial. Ainsi, le résultat progresse en accumulant de nouveaux chemins indirects. Dominic Genest, 2009
25
Algorithme de Floyd-Warshall Pour construire la matrice dadjacence de la fermeture transitive dun graphe pondéré à partir de sa matrice dadjacence, il faut employer lalgorithme de Floyd- Warshall: – Faire une copie du graphe. – Dans la copie, pour chaque nœud k: Pour chaque paire de nœuds {i,j}: – Vérifier si le coût du chemin va de i à k, additionné à celui qui va de k à j, ne serait pas inférieur à celui précédemment trouvé pour aller de i à j. Si cest le cas, remplacer le coût de larête de i à j par cette somme. Remarque: dans le cas dun graphe pondéré, on peut considérer quil existe des arêtes en toute paire de nœuds, mais certaines avec un coût infini. Cela simplifie lénoncé de lalgorithme. Dominic Genest, 2009
26
Temps dexécution des algorithmes de Warshall et de Floyd-Warshall Ces deux algorithmes exécutent autant de comparaisons que le cube du nombre de nœuds du graphe en question. Le résultat de cet algorithme, cest-à-dire la fermeture transitive du graphe quon lui a donné en entrée, permet dobtenir instantanément le coût du meilleur chemin entre toutes les paires de nœuds possibles dun graphe. Un léger ajout à ces deux algorithmes permet de faire en sorte quils fournissent non seulement le coût du meilleur chemin entre chaque paire de nœuds, mais aussi la description de ces chemins. Dominic Genest, 2009
27
Algorithme de Floyd-Warshall modifié pour fournir la description des meilleurs chemins Faire une copie du graphe. Dans la copie, pour chaque nœud k: – Pour chaque paire de nœuds {i,j}: Vérifier si le coût du chemin va de i à k, additionné à celui qui va de k à j, ne serait pas inférieur à celui précédemment trouvé pour aller de i à j. Si cest le cas, remplacer le coût de larête de i à j par cette somme et prendre en note létiquette de k à côté de la case « i,j ». Les étiquettes k notées dans chaque case correspondent alors au « meilleur précédent » pour chaque paire de noeud. Pour obtenir le plus court chemin dun nœud à un autre, il suffit alors de partir du dernier nœud, et de remonter jusquau début, de case en case, via ces étiquettes « k » notées à côté de chaque case, puis dinverser la liste de nœuds parcourus par ce processus. Dominic Genest, 2009
28
Matrice dadjacence dune fermeture transitive dun graphe pondéré ABCDEFG A05 (F)7 (B)14 (B)2 (A)3 (A) B02 (B)9 (B) C08 (C) D0 E0 F3 (F)5 (B)12 (B)0 G0 Résultat de Floyd- Warshall modifié pour fournir la description des meilleurs chemins: Dominic Genest, 2009 A F G D B C E 2 3 8 3 9 8 2
29
Plus court chemin entre deux noeuds Sil sagit dun graphe non pondéré, on peut aussi obtenir la description des chemins les plus courts en modifiant lalgorithme de Warshall de la même façon. Sil sagit dun graphe pondéré mais quon ce quon veut, cest le chemin le plus court en terme de nombre détapes plutôt que de coût total, alors il suffit dutiliser lalgorithme de Warshall plutôt que Floyd-Warshall même si cest un graphe pondéré. – Par exemple, on pourrait vouloir découvrir le trajet en avion impliquant le moins descales possibles, quitte à ce quil dure plus longtemps. Toutefois, ces algorithmes sont souvent inappropriés pour la situation la plus courante, cest-à-dire celle où on veut le plus court chemin entre deux nœuds bien particuliers, plutôt que davoir tous les plus courts chemins entre toutes les paires de nœuds possibles. Si on détermine deux nœuds et quon ne sintéresse quau plus court chemin dans un graphe entre ces deux nœuds-là, il faut utiliser lun ou lautre des algorithmes suivants: – Sil sagit dun graphe non pondéré ou si cest le nombre darêtes quon veut minimiser: parcours en largeur. – Sil sagit dun graphe pondéré dont tous les poids sont positifs: lalgorithme de Dijkstra. – Sil sagit dun graphe pondéré mais qui possède des arêtes avec des poids négatifs: lalgorithme de Bellman-Ford. – Sil sagit dun graphe avec beaucoup de nœuds, mais dont la résolution peut être simplifiée par une heuristique (par exemple, un graphe euclidien représentant un labyrinthe avec de grandes salles qui sont une grille de noeuds): lalgorithme A*. Dominic Genest, 2009
30
Le parcours en profondeur 1.Initialiser (mettre à « faux ») une marque (une valeur booléenne) associée à chaque nœud. 2.Empiler et marquer le nœud de départ. 3.Tant et aussi longtemps que la pile nest pas vide: a)Dépiler un nœud, et lui faire le traitement voulu pour le parcours (par exemple, afficher sa valeur à lécran). b)Pour chacun de ses voisins qui nest pas marqué: i.Le marquer. ii.Lempiler. Remarque: Ce parcours nest pas approprié pour retrouver le chemin le plus court entre deux nœuds donnés. Il est cependant très facile à implanter par une fonction récursive. Dominic Genest, 2009
31
Parcours en profondeur Dominic Genest, 2009 A F G D B C E A Nœud de départ Pile
32
Parcours en profondeur Dominic Genest, 2009 A F G D B C E Nœud de départ Pile Dépilé: A
33
Parcours en profondeur Dominic Genest, 2009 A F G D B C E G ; B ; F Nœud de départ Pile Dépilé: A
34
Parcours en profondeur Dominic Genest, 2009 A F G D B C E G ; B Nœud de départ Pile Dépilé: F
35
Parcours en profondeur Dominic Genest, 2009 A F G D B C E G Nœud de départ Pile Dépilé: B
36
Parcours en profondeur Dominic Genest, 2009 A F G D B C E G ; C ; D Nœud de départ Pile Dépilé: B
37
Parcours en profondeur Dominic Genest, 2009 A F G D B C E G ; C Nœud de départ Pile Dépilé: D
38
Parcours en profondeur Dominic Genest, 2009 A F G D B C E G Nœud de départ Pile Dépilé: C
39
Parcours en profondeur Dominic Genest, 2009 A F G D B C E Nœud de départ Pile Dépilé: G
40
Parcours en profondeur Dominic Genest, 2009 A F G D B C E Nœud de départ Pile FIN
41
Le parcours en largeur 1.Initialiser (mettre à « faux ») une marque (une valeur booléenne) associée à chaque nœud. 2.Enfiler et marquer (mettre sa marque à « vrai ») le nœud de départ. 3.Tant et aussi longtemps que la file nest pas vide: a)Défiler le prochain nœud. b)Pour chaque voisin qui nest pas marqué: i.Le marquer ii.Lenfiler. Remarque: Sil sagit dun parcours fait pour retrouver un chemin comportant le plus petit nombre détapes entre deux nœuds donnés, alors on peut sarrêter dès quon atteint le nœud darrivée. Aussi, si on veut la description de ce chemin, il faut mémoriser le meilleur précédent pour chaque nœud, un peu de la même façon que la modification qui a été faite à lalgorithme de Warshall. Dominic Genest, 2009
42
Parcours en largeur Dominic Genest, 2009 A F G D B C E A Nœud de départ File ()
43
Parcours en largeur Dominic Genest, 2009 A F G D B C E Nœud de départ File () Défilé: A
44
Parcours en largeur Dominic Genest, 2009 A F G D B C E G ; B ; F Nœud de départ File () (A) () (A) Défilé: A
45
Parcours en largeur Dominic Genest, 2009 A F G D B C E B ; F Nœud de départ File () (A) () (A) Défilé: G
46
Parcours en largeur Dominic Genest, 2009 A F G D B C E F Nœud de départ File () (A) () (A) Défilé: B
47
Parcours en largeur Dominic Genest, 2009 A F G D B C E F ; C ; D Nœud de départ File () (A) () (B) (A) Défilé: B
48
Parcours en largeur Dominic Genest, 2009 A F G D B C E C ; D Nœud de départ File () (A) () (B) (A) Défilé: F
49
Parcours en largeur Dominic Genest, 2009 A F G D B C E D Nœud de départ File () (A) () (B) (A) Défilé: C
50
Parcours en largeur Dominic Genest, 2009 A F G D B C E Nœud de départ File () (A) () (B) (A) Défilé: D
51
Parcours en largeur Dominic Genest, 2009 A F G D D B C E Nœud de départ File () (A) () (B) (A) Meilleur chemin entre A et D: D
52
Parcours en largeur Dominic Genest, 2009 A F G D D B C E Nœud de départ File () (A) () (B) (A) Meilleur chemin entre A et D: D
53
Parcours en largeur Dominic Genest, 2009 A F G D B B C E Nœud de départ File () (A) () (B) (A) Meilleur chemin entre A et D: B D
54
Parcours en largeur Dominic Genest, 2009 A F G D B B C E Nœud de départ File () (A) () (B) (A) Meilleur chemin entre A et D: B D
55
Parcours en largeur Dominic Genest, 2009 A A F G D B C E Nœud de départ File () (A) () (B) (A) Meilleur chemin entre A et D: A B D
56
Lalgorithme de Dijkstra 1.Initialiser toutes les marques à « faux », puis tous les meilleurs coûts à « infini ». 2.Mettre le meilleur coût du nœud de départ à zéro. 3.Tant et aussi longtemps quil reste des nœuds qui ne sont pas marqués: a)Choisir le nœud x, parmi ceux qui ne sont pas marqués, qui a le plus petit « meilleur coût ». b)Marquer ce nœud x. c)Mettre à jour les meilleurs coûts et meilleurs précédents de chacun de ses voisins, en considérant le chemin que procure x en tant quintermédiaire. 4.Procéder comme dhabitude pour retrouver le meilleur chemin, cest-à-dire à laide des valeurs « meilleurs précédent », à partir du nœud darrivée. Remarque: il ne sagit pas dun parcours. Dominic Genest, 2009
57
Lalgorithme de Bellman-Ford 1.Initialiser tous les meilleurs coûts à « infini » (il ny a pas de marque pour cet algorithme-ci). 2.Mettre le meilleur coût du nœud de départ à zéro. 3.Répéter autant de fois quil y a de nœuds, moins 1: a)Pour chaque arête: i.Mettre à jour le meilleur coût et le meilleur précédent du nœud au bout de larête, en considérant le chemin que procure le nœud au départ de larête en tant quintermédiaire. 4.Répéter encore ça une autre fois, et si jamais ça change quelque chose aux meilleurs coûts, cest signe quil y a un cycle négatif et donc quil nexiste pas de solution. Si cette nième répétition ne change rien aux meilleurs coûts, alors la solution est celle retrouvée par la méthode habituelle utilisant les meilleurs précédents à partir de la fin. Remarque: il ne sagit pas dun parcours. Dominic Genest, 2009
58
Lalgorithme A* Il sagit exactement de la même chose que Dijkstra, sauf que lorsquon élit le nœud aillant le plus petit « meilleur coût », on additionne chaque « meilleur coût » à une estimation du coût de ce nœud vers le nœud darrivée (exemple, distance à vol doiseau). Cet algorithme est la base de bien des algorithmes en intelligence artificielle. Dominic Genest, 2009
Présentations similaires
© 2024 SlidePlayer.fr Inc.
All rights reserved.