Université Paris Diderot – Paris 7 L3 Informatique Algorithmique Année 2009-2010, 1er semestre TD n◦4 - Correction Parcours de graphes Exercice 1 Y’a qu’à... Exercice 2 Non ! Par exemple, le graphe de sommets {a, b, c, d, e} et d’arêtes {(ab), (ac), (bd), (be), (cd), (ce)}. L’arbre d’arêtes {(ab), (ac), (bd), (ce)} ne peut pas être obtenu par un parcours en largeur, mais est bien un arbre de plus courts chemins partant de a. Exercice 3 Y’a qu’à... Exercice 4 Le premier problème est qu’on fait un parcours sans relance. Ainsi s’il y a un circuit inaccessible depuis le sommet de départ, on ne le détectera pas. Un problème plus sérieux est illustré en faisant tourner l’algorithme sur le graphe de la figure 1. Vous avez vu en cours qu’un arc retour dans un parcours en profondeur permet de détecter un cycle. L’algorithme proposé ici n’utilise que deux couleurs pour marquer les noeuds, ainsi un arc transverse ou un arc avant peut être pris pour un arc de retour et signaler un faux circuit. Fig. 1 – Un graphe dirigé Une solution pourrait être de modifier l’algorithme pour distinguer les arcs retour des arcs transverses et en avant. Mais, est-ce suffisant ? Est-ce que notre parcours en profondeur va systématiquement rencontrer un arc retour si le graphe contient un cycle ? Oui, mais pour cela il faut démontrer le théorème suivant. Théorème 1 Dans un graphe orienté il existe un circuit si et seulement si dans chaque parcours en profondeur il existe un arc de retour. Ainsi, si un parcours en profondeur ne trouve pas d’arc retour, c’est effectivement qu’il n’y a pas de circuit. 1 L’algorithme correct est alors : 1 2 3 4 5 Procédure Cherche_Circuit(G) // G = (S, A) graphe orienté pour chaque s ∈ S faire Marquage[s] := blanc ; Père[s] := nil ; 7 pour chaque s ∈ S faire si CC(G,s) alors terminer 8 afficher("pas de circuit") ; 6 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Fonction CC(G, s) // G = (S, A) graphe orienté Marquage[s] := gris ; pour chaque x ∈ Voisinage(s,G) faire si Marquage[x] == blanc alors Père[x] := s ; si CC(G, x) alors renvoyer vrai ; sinon si Marquage[x] == gris alors afficher(x) ; y := s ; tant que y 6= x faire afficher(y) ; y := Père[y] ; renvoyer vrai; 24 25 26 Marquage[s] := noir ; renvoyer faux; Cet algorithme se termine pour les mêmes raisons que l’algorithme de parcours en profondeur avec la même complexité. Preuve du théorème. L’implication (⇐) a été montré en cours : s’il y a un arc de retour (x, y) alors le chemin de y vers x dans l’arbre de parcours et l’arc (x, y) forment le circuit. Pour montrer la réciproque (⇒), supposons qu’il existe un circuit C dans G et nommons v le premier sommet de ce circuit qui est visité pendant le parcours en profondeur. Nommons p le prédécésseur de v sur ce circuit. En utilisant la notion de date de début et de date de fin utilisée dans le cours1 , à l’instant début[v], tous les sommets du circuit sont blancs, ou autrement dit, pour chaque sommet x du circuit différent de v : début[v] < début[x]. Il suffit alors de montrer qu’à l’instant f in[p] le sommet V est encore gris. Autrement dit, il suffit de montrer f in[p] < f in[v]. Par la propriété des intervalles de traitement2 et l’inégalité précédente, chacun des sommets x de C est dans un des deux cas suivants : 1. soit début[v] < début[x] < f in[x] < f in[v] 2. soit début[v] < f in[v] < début[x] < f in[x] Si le sommet p est dans le cas 1 le résultat est immédiat. Si ce sommet est dans le cas 2, montrons qu’il y a une contradiction. En effet, cela signifirait que le sommet p qui est accessible depuis v n’a pas été visité à la fin de l’exploration de v, ce qui est contraire à l’intuition du parcours en profondeur. 1 rappel 2 voir : début et f in donnent les dates du début et de la fin de traitement des sommets cours 2 Plus précisement, examinons la situation à l’instant f in[v]. À cet instant d’après la propriété des intervalles, un sommet x du circuit C est soit noir (cas 1), soit blanc (cas 2). Par hypothèse, p est blanc, et considérons le premier prédecesseur de p sur C qui soit noir (ce prédécesseur existe, car v est noir), nommons le s. Le successeur de s sur C est blanc. Or, on vérifie aisément sur la définition de l’algorithme, qu’à tout instant le successeur d’un noeud noir est noir. Le successeur de s sur C ne peut être blanc et noir à la fois. Contradiction. Exercice 5 On fait une modification du parcours en largeur. En effet, il trouve des plus courts chemins, donc il y a lieu d’espérer qu’il trouve aussi des plus courts circuits. Cet algorithme est suivi d’une preuve pour se convaincre de cette affirmation. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Procédure Cherche_Circuit(G, s) // G = (S, A) graphe orienté pour chaque x ∈ S faire Marquage[x] := faux ; Père[x] := nil ; F := File_Vide ; Enfiler(s,F) ; Marquage[s] := vrai ; Profondeur[s] := 0 ; tant que F non vide faire x := Defiler(F) ; pour chaque y ∈ Voisinage(x, G) faire si non(Marquage[y]) alors Enfiler(y,F) ; Marquage[y] := vrai ; Père[y] := x ; Profondeur[y] := Profondeur[x] + 1; sinon si y = s alors tant que x 6= nil faire afficher(x) ; x := Père[x] ; renvoyer vrai ; Preuve : – Terminaison. On étudie les exécutions possibles de l’algorithme. Si une exécution entre dans le sinon de la ligne 18, alors l’algorithme termine (en renvoyant vrai). Si au contraire elle n’entre jamais dans ce sinon, alors elle se déroule exactement comme un parcours en profondeur, et donc termine aussi. On conclut que l’algorithme termine. – Preuve que l’algorithme trouvera un circuit s’il y en a un passant par s. On suppose qu’il existe au moins un circuit passant par s. On considère, parmi les circuits de longueur minimale passant par s, les sommets de plus grande profondeur en partant de s. Parmi ceux-là, l’un d’eux sera le premier atteint par le parcours en largeur effectué par l’algorithme. Pour ce sommet, on entre dans le sinon de la ligne 18, et l’algorithme renvoie vrai. Jusqu’à ce qu’on entre dans le sinon de la ligne 18, les invariants du cours sur le parcours en largeur sont vérifiés. En particulier, les sommets dont la distance depuis s est strictement inférieure ont déjà été visités, et l’arbre obtenu est un arbre des plus courts chemins depuis s. – Minimalité d’un circuit trouvé Ainsi, au moment où on rentre dans le sinon, on trouve donc un circuit sur s. Et on sait que tout sommet x dont la distance à s est strictement inférieure a été visité précédemment. Puisqu’on n’est entré dans le sinon de la ligne 18 à la visite de x, c’est que x n’est pas lié a s. On en déduit que le cycle passant par s trouvé par l’algorithme est de longueur minimale. 3 Exercice 6 Tout graphe non orienté et connexe G vérifie une et une seule des conditions suivantes : – soit G est un arbre, – soit G contient un cycle. Or d’après le cours, un graphe non orienté connexe G = (S, A) est un arbre si et seulement si |A| = |S|−1. Pour tester si un tel graphe possède un cycle, il suffit donc de tester si |A| > |S| − 1. Cet algorithme ne permet cependant pas d’exhiber un cycle dans le graphe. Exercice 7 Un pseudo-code possible du parcours en profondeur avec relance. On n’utilise pas de marques : les dates remplacent. Un début à -1 signifie un sommet non encore atteint, un début ≥ 0 mais une fin de -1 signifie un sommet en cours de visite. Variables globales: sommet x,y; entier temps = 0, d[V], f[V] tableaux initialises à -1 Pour x de 1 a n Si marquage[x] = faux PP(x) Procedure PP(sommet v) d[v] = temps++ pour chaque voisin si d[y] == -1 Afficher PP(y) sinon si f[v] Afficher sinon si f[y] Afficher sinon Afficher f[v] = temps++ y de v faire (v,y) est un arc de parcours == -1 (v,y) est un arc de retour (aussi appelé en arrière) < d[v] (v,y) est un arc transversal (v,y) est un arc en avant 4