Lycée Carnot — 2016-2017 Option informatique MP/MP* TP 3 : Parcours de graphes et applications Dans ce TP, on étudie deux différents parcours de graphes à l’aide d’une interface générique, et quelquesunes de leurs applications : recherche des composantes connexes, tri topologique d’un graphe et détection de cycles dans un graphe. Pour traiter ce TP, il faut avoir fini les parties 2 et 3 du TP no 2, puisque nous utiliserons vos implémentations de graphe. 1 Piles et files On rappelle l’interface générique d’une file ou d’une pile que vous avez implémentées au TP no 1. type ’a t;; exception Empty;; exception Full;; value value value value value empty : unit -> ’a t;; is_empty : ’a t -> bool;; is_full : ’a t -> bool;; push : ’a -> ’a t -> unit;; pop : ’a t -> ’a ;; Si votre implémentation est douteuse, vous pouvez utiliser les modules C AML stack et queue, par exemple : let p = stack__new ();; stack__push 33 p;; stack__pop p;; let is_empty p = stack__length p = 0;; Attention : l’interface est légèrement différente de celle que l’on a vue en cours (et différente pour les piles et files). Le cas des files et piles vides est géré à l’aide d’une exception. Lisez bien la documentation des deux modules : http://caml.inria.fr/pub/docs/manual-caml-light/node15.html. 2 Parcours d’un graphe Q UESTION 1 Le pseudo-code du parcours en profondeur est donné par les algorithmes 1 et 2. Faire tourner ces deux algorithmes à la main sur quelques exemples simples pour être sûr de bien comprendre. Q UESTION 2 On considère tout d’abord que postvisiter ne fait rien et que previsiter s imprime à l’écran le sommet s (ici un entier). Implémenter les deux algorithmes en C AML. Vérifier que votre implémentation est bien indépendante de l’implémentation de graphe sous-jacente (cela doit fonctionner aussi bien avec une représentation par matrice d’adjacence ou par listes d’adjacence), que le graphe soit orienté ou non. Essayer sur plusieurs exemples. Q UESTION 3 Que se passe-t-il si on inverse les rôles de previsiter et postvisiter dans la question précédente ? Q UESTION 4 Comment obtenir en sortie la liste de tous les sommets dans l’ordre de leur première visite ? Implémenter cela en C AML. Q UESTION 5 exemples. Remplacer la pile par une file pour obtenir un parcours en largeur et le vérifier sur des http://carnot.cpge.info 1 Lycée Carnot — 2016-2017 Option informatique MP/MP* Algorithme 1 : explorer(G, s) : exploration en profondeur à partir d’un sommet. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Entrée : Graphe G = (S, A), sommet s ∈ S pile ← creer_pile_vide(); empiler(s, pile); tant que pile est non vide faire t ← depiler(pile); si t est marqué alors si t pas encore postvisité alors postvisiter(t); fin fin sinon prévisiter(t); marquer t; empiler(t, pile); pour tout voisin u de t faire si u n’est pas marqué alors empiler(u, pile); fin fin fin fin Algorithme 2 : parcours_pronfondeur(G) : parcours en profondeur d’un graphe. 1 2 3 4 5 3 Entrée : Graphe G = (S, A) pour s dans S faire si s n’est pas marqué alors explorer(G, s); fin fin Temps de premier et dernier passage Dans tout ce qui va suivre, on s’intéresse au parcours en profondeur, celui obtenu à l’aide d’une pile. Q UESTION 6 On considère une variable horloge initialisée à 0, qui est incrémentée à chaque fois que l’on prévisite ou que l’on postvisite un sommet. On utilise également deux tableaux pre et post, dans lesquels on met à jour pour chaque sommet son heure de prévisite (appelée aussi temps de premier passage) et de postvisite (temps de dernier passage). Pour s, s0 ∈ S, montrer que soit les intervalles [ pre[s], post[s]] et [ pre[s0 ], post[s0 ]] sont disjoints, soit l’un est inclus dans l’autre. Que réprésente l’intervalle [ pre[s], post[s]] ? Implémenter cela en C AML et essayer sur quelques exemples. 4 Composantes connexes d’un graphe non-orienté Q UESTION 7 On veut modifier l’algorithme pour obtenir les composantes connexes, sous la forme d’un tableau composantes où composantes[s] est l’identifiant (un entier unique pour chaque composante) de la composante connexe à laquelle appartient s. On va donc définir let previsiter s composantes id = composantes.(s) <- !id ;; http://carnot.cpge.info 2 Lycée Carnot — 2016-2017 Option informatique MP/MP* À quel endroit faut-il incrémenter la référence id pour que le parcours permette de calculer les composantes connexes ? Q UESTION 8 Écrire une fonction is_connected : graph -> bool qui vérifie si un graphe est connexe. Q UESTION 9 Écrire une fonction connected_components : graph -> graph list qui renvoie les sous-graphes correspondant aux composantes connexes. 5 Graphes orientés acycliques et tri topologique Définition 1. Un ordre topologique d’un graphe orienté G = (S, A) est un ordre total 2 sur les sommets tel que si (s, s0 ) ∈ A alors s ≺ s0 , c’est-à-dire compatible avec la relation d’ordre partielle donnée par les arcs. Q UESTION 10 Montrer qu’un graphe orienté qui admet un ordre topologique est acyclique. Q UESTION 11 Montrer que dans un graphe orienté acyclique G = (S, A), si (s, s0 ) ∈ A alors post[s] > post[s0 ]. Q UESTION 12 En déduire un algorithme qui détecte si un graphe est acyclique et l’implémenter. Q UESTION 13 À l’aide des temps de dernier passage, trouver un algorithme qui, pour un graphe acyclique, trie les sommets de ce graphe selon un ordre topologique et l’implémenter. Remarque 1. Il y a donc équivalence, pour un graphe orienté, entre être acyclique et admettre un ordre topologique. 6 Composantes fortement connexes d’un graphe orienté Soit G = (S, A) un graphe orienté. Q UESTION 14 Pourquoi ne peut-on pas simplement effectuer un parcours de graphe pour trouver les composantes (fortement) connexes, comme dans le cas non-orienté ? On définit le méta-graphe de G comme étant le graphe MG = (C f , A f ) où C f est l’ensemble des composantes fortement connexes de G et A f = {(c1 , c2 ) ∈ C2f , ∃s1 ∈ c1 , ∃s2 ∈ c2 , (s1 , s2 ) ∈ A}. Q UESTION 15 Montrer que MG est un graphe orienté acyclique. À quelle condition a-t-on G = MG ? Q UESTION 16 Soit c ∈ C f tel que d+ (c) = 0, on dit que c est un puits. Soit s ∈ c. Pourquoi la fonction explorer au départ de s dans G permet de trouver exactement c ? Ceci suggère un algorithme pour trouver les composantes fortement connexes : (1) choisir un sommet s appartenant à un puits de MG ; (2) utiliser le parcours en profondeur à partir de sommet pour construire sa composante connexe ; (3) supprimer cette composante du graphe initial ; (4) recommencer avec les sommets restants. Le problème, c’est que l’on ne voit pas bien a priori comment trouver un sommet appartenant à un puits de MG . En revanche, il est plus simple de trouver un sommet appartenant à une source de MG . Un sommet c est une source si d− (c) = 0. Q UESTION 17 Soient c1 , c2 ∈ C f , montrer que si (c1 , c2 ) ∈ A f alors max post[s1 ] > max post[s2 ] s1 ∈ c1 s2 ∈ c2 En déduire un algorithme permettant de trouver un sommet s appartenant à une source de M f . Q UESTION 18 Pour trouver un puits de MG , on peut utiliser le graphe transposé de G (cf. E XERCICE 15 du TP no 2). Pourquoi les composantes fortement connexes de G R sont les mêmes que celles de G ? Que vaut M f ( G R ) ? En déduire un algorithme pour trouver un sommet s appartenant à un puits de M f ( G ). Q UESTION 19 En déduire un algorithme pour trouver les composantes fortement connexes d’un graphe oriente et l’implémenter en C AML. Montrer que sa complexité est toujours linéaire (i.e. O(|S| + | A|)). http://carnot.cpge.info 3