Pour chercher les circuits, il est plus simple de faire un parcours en profondeur. C'est pourquoi nous privilégierons les parcours en profondeur récursifs.
Dans cette partie on considérera qu'un circuit est un chemin commençant et se terminant par le même sommet et ne contenant pas deux fois un autre sommet.
Par exemple si on a un circuit 0 -> 1 -> 2 -> 3 -> 0 et un circuit 2 -> 4 -> 5 -> 2, alors le chemin suivant, qui est valide, n'est pas un circuit : 0 -> 1 -> 2 -> 4 -> 5 -> 2 -> 3 -> 0.
Pour rappel, les graphes suivants sont déjà définis :
Exercice 16 : existence d'un circuit en partant d'un sommet
Compléter le code de la fonctionexiste_circuit_rec qui prend en paramètres un graphe sous forme de dictionnaire d'adjacence graphe, depart un sommet de graphe, et qui renvoie un booléen indiquant s'il y a un circuit en partant de depart.
La fonction a également un paramètre optionnel, etat, qui vaut None par défaut et sinon qui est un dictionnaire d'états des sommets de graphe
La fonction existe_circuit_rec parcourt le graphe en profondeur en partant de depart. Si on trouve un sommet qui est en attente, alors c'est qu'il y a un circuit.
Pour tester cette fonction, on peut regarder graphe2 dans lequel chaque sommet mène à un circuit, graphe4 où il n'y a aucun circuit et graphe5 où il y a un circuit si on part de 1, 2, 3 ou 4 mais pas des autres sommets.
On rappelle que lorsqu'on fait un parcours en profondeur, tous les sommets en attente forment un chemin depuis le premier sommet depart jusqu'au sommet courant. Si au cours de l'exploration on arrive sur un sommet en attente, c'est qu'on a trouvé un circuit.
Si on trouve un sommet traité, c'est qu'il ne mène pas à un circuit, sinon on aurait déjà renvoyé True. Ce n'est donc pas la peine de continuer à explorer à partir de là.
###(Dés-)Active le code après la ligne # Tests (insensible à la casse) (Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Exercice 17 : existence d'un circuit dans un graphe
Écrire le code de la fonction existe_circuit_profondeur qui prend en paramètre un graphe sous forme de dictionnaire d'adjacence graphe et qui renvoie un booléen s'il existe un circuit dans graphe.
La fonction existe_circuit_profondeur teste tous les sommets non encore vus en vérifiant s'ils mènent à un circuit ou pas.
Nous allons maintenant travailler à chercher les circuits présents dans les graphes. Les circuits seront représentés par des listes, comme les chemins, sauf que le permier élément et le dernier sont égaux.
Par exemple dans le graphe 2 il y a 2 circuits qui peuvent être représentés ainsi :
Toutes les représentations de chaque circuit sont équivalentes.
Nous n'avons pas le cas dans les exemples, mais le plus petit circuit possible correspond à un sommet qui aurait un arc pointant sur lui-même. On obtiendrait alors un circuit [sommet,sommet], où sommet est un sommet du graphe.
Nous allons commencer par écrire plusieurs fonctions qui nous servirons pour la suite.
Exercice 18 : extraire un circuit à la fin d'un chemin
Écrire le code de la fonction extrait_circuit qui prend en paramètre une liste chemin correspondant à un chemin et qui renvoie la fin de la liste correspondant à un circuit. On suppose que le chemin donné contient bien un circuit.
Il faut parcourir le chemin et commercer à rajouter des éléments dès qu'on est arrivé à un sommet égal au dernier de chemin. Quand on a commencé à rajouter des sommets, il faut continuer jusqu'à la fin.
###(Dés-)Active le code après la ligne # Tests (insensible à la casse) (Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Écrire le code de la fonction circuit_valide qui prend en paramètres un dictionnaire graphe représentant un graphe et circuit, une liste de sommets, et qui renvoie un booléen indiquant si circuit correspond bien à un circuit de graphe.
Exercice 20 : trouver un circuit accessible à partir d'un sommet
Écrire le code de la fonction trouve_circuit_rec qui prend en paramètre un dictionnaire graphe représentant un graphe et un sommet depart, et qui renvoie un circuit de graphe accessible depuis depart ou la liste vide s'il n'y en a pas. Un circuit peut ne pas être accessible depuis n'importe quel sommet.
Il y a également deux paramètres optionnels : etat, correspondant à l'état des sommets et chemin qui est la liste des sommets permettant d'aller à depart.
Par exemple dans le graphe 5, le circuit n'est pas accessible en partant de 0.
Si on n'a pas défini etat et chemin, on les initialise.
On rajoute le depart au chemin.
Si on est sur un sommet en attente, c'est que le chemin se termine par un circuit. On renvoie donc le circuit extrait de chemin.
Sinon on met le sommet à en attente puis on parcourt chacun de ses voisins.
Si le voisin n'a pas été traité, on regarde s'il mène à un circuit.
Si c'est le cas, on renvoie vrai.
Si à la fin on n'a pas trouvé de circuit, on marque le sommet comme traité et on l'enlève de la fin du chemin. On renvoie alors une lite vide.
Selon la façon dont le parcours est réalisé vous pouvez ne pas forcément obtenir le même circuit ou au moins obtenir un circuit partant d'un autre sommet.
###(Dés-)Active le code après la ligne # Tests (insensible à la casse) (Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Exercice 21 : trouver un circuit quelconque dans un graphe
Écrire le code de la fonction trouve_circuit_profondeur qui prend en paramètre un dictionnaire graphe correspondant à un graphe et qui renvoie un circuit qu'il contient ou la liste vide s'il n'y en a pas.
Afin de pouvoir tester les résultats du dernier exercice de cette partie, il faut définir des fonctions permettant de comparer des circuits et des listes de circuits.
Exercice 22 : équivalence de deux circuits
Écrire le code de la fonction circuits_equivalents qui prend en paramètres deux listes qui correspondent à des circuits et qui renvoit un booléen indiquant si ce sont deux représentations du même circuit. On ne vérifiera pas si les circuits sont valides ou pas.
On rappelle que [1, 2, 3, 4, 1] est équivalent à [2, 3, 4, 1, 2] ou à [3, 4, 1, 2, 3].
Par contre, ils ne sont pas équivalents à [1, 4, 3, 2, 1].
Pour que deux circuits soient équivalents, il faut déjà qu'ils aient la même longueur.
Ensuite, puisque le dernier élément d'un circuit correspond au premier, on peut l'ignorer dans les deux listes.
On commence par chercher l'indice du premier élément de circuit1 qui correspond au premier de circuit2.
S'il n'existe pas, alors les deux circuits ne sont pas équivalents.
Sinon, on parcourt les éléments de circuit2 (sauf le dernier) tout en vérifiant qu'il y a bien une correspondance avec les éléments de circuit1. Une fois arrivé à l'avant-dernier élément de circuit1, il faut bien penser à revenir au premier pour pouvoir continuer.
Indication pour les indices de circuit1
Si i1 est l'indice du premier élément de circuit1 égal à circuit2[0], alors i1+i2 est l'indice de l'élément de circuit1 qui doit être égal avec celui de circuit2 d'indice i2.
Sauf qu'il bien faire attention parce que i1+i2 peut ne pas être un indice de circuit1...
###(Dés-)Active le code après la ligne # Tests (insensible à la casse) (Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Exercice 23 : vérifier si un circuit est dans une liste de circuits
Écrire le code de la fonction appartient_circuit qui prend en paramètre une liste circuit correspondant à un circuit et une liste de listes liste_circuits, et qui renvoie un booléen indiquant si circuit est équivalent à un circuit contenu dans liste_circuits.
Exercice 24 : vérifier l'équivalence de listes de circuits
Écrire le code de la fonction listes_equivalentes qui prend en paramètres deux listes de listes correspondant à des circuits liste_circuits1 et liste_circuits2, et qui renvoie un booléen indiquant si les deux listes contiennent les mêmes circuits.
Il est conseillé d'utiliser appartient_circuits.
Puisque cette fontion va nous servir à comparer les circuits trouvés avec les circuits attendus, il se peut que le même circuit apparaisse plusieurs fois dans une des listes. On supposera donc qu'une des deux listes ne contient pas plusieurs circuits équivalents.
Il faut que les listes de circuits soient de même taille.
Ensuite il faut s'assurer que tous les circuits dans la première liste correspondent à un circuit dans la deuxième et réciproquement.
Si on posait l'hypothèse qu'une des listes ne contient des circuits duppliqués, il suffirait de vérifier la taille et que tous les circuits d'une liste sont inclus dans l'autre pour pouvoir conclure.
###(Dés-)Active le code après la ligne # Tests (insensible à la casse) (Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Exercice 25 : trouver tous les circuits passant par un sommet donné
Écrire le code de la fonction trouve_circuits_rec qui prend en paramètre un dictionnaire graphe correspondant à un graphe, un sommet depart et une liste circuits, et qui rajoute à circuits tous les circuits de graphe passant par depart. La représentation des circuits trouvés doivent tous commencer par depart.
Il y a également deux paramètres supplémentaires etat et chemin qui sont initialisés à None par défaut :
etat est un dictionnaire correspondant aux états des sommets ;
chemin est la liste des sommets ayant permi d'arriver au sommet actuel. On supose que depart est le dernier élément de chemin.
Contrairement à trouve_circuit_rec, on n'accepte pas les circuits qui sont accessibles depuis depart mais qui n'y reviennent pas.
Pour les tests, puisque les circuits ne peuvent pas être trouvés dans le même ordre mais qu'on veut qu'ils commencent tous par le départ choisi, on n'utilisera pas listes_equivalentes.
Astuce pour tester plus facilement la fonction
Si vous rajoutez returncircuits à la fin de la fonction, vous pouvez tester la fonction en une seule ligne :
Si chemin ou etat sont à None, on leur donne une valeur initiale. Pour chemin, on y met le sommet actuel.
Si le sommet est en attente, alors c'est qu'on vient de trouver un circuit.
Si le circuit commence au sommet actuel, c'est que le circuit passe bien par depart.
On rajoute une copie du chemin à la liste des circuits.
Sinon, on marque le sommet actuel comme étant en attente.
On parcourt alors chaque voisin. Si le voisin n'a pas été marqué comme traité :
on le rajoute au chemin ;
on rajoute tous les circuits qu'on peut trouver en passant par l'arc depart -> voisin à l'aide d'un appel récursif ;
on le retire du chemin pour pouvoir prendre un autre chemin.
On repasse le sommet courant à "pas vu" pour pouvoir y revenir par un autre chemin.
Aucun sommet n'est marqué comme "traité" dans cette fonction, mais ils le seront dans la fonction suivante. Cela permettra d'éviter de prendre deux fois des circuits déjà vus.
###(Dés-)Active le code après la ligne # Tests (insensible à la casse) (Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Exercice 26 : trouver tous les circuits d'un graphe
Écrire le code de la fonction trouve_tous_circuits qui prend en paramètre un dictionnaire correspondant à un circuit et renvoyant la liste de tous les circuits de ce graphe.
Selon l'ordre de parcours, les résutats peuvent ne pas être dans le même ordre.
Explications de l'algorithme
On parcourt chaque sommet, on cherche les circuits qu'il contient et on le marque comme traité pour ne plus le revisiter en regardant les sommets suivants.
Normalement au début de la recherche des circuits passant par un sommet, tous les autres sommets sont marqués soit à "non vu", soit à "traité".
###(Dés-)Active le code après la ligne # Tests (insensible à la casse) (Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
###(Dés-)Active le code après la ligne # Tests (insensible à la casse) (Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5
.128013rnq.[;f"kpuS}wlhd4sPv/ab{t&73]ée:5=(m1_cy)g2 oi
8,6010j0y0s0p0N0h0l0L0G0h0p0l0l0B0a0s0N0c0a000O010l0d0D0D0p030H000e0M0h0d0*0M04010o0;0?0^0`0/0j0N0n0L0@030b0j0M0g040(0E0a0L0l0s0H0h0y0(1f0J0N040z0E060u0y0D0L0E1E1o0.0S1c0!0$0&0(0i0N0J0i0h1S0i0s0-010V0q1t1N0%0'0a1R1T1V1T0s1#1t1Z0s03001!0i0!0}0l0c0p1l0a0K1o0#1'0(090X0y040p0D0y1Z1|1~1&1P0a271t2a2c0-020L0m030M0c0M0l0N11040L0T1`03030y0G2y142f041@0o0s0i2L1=1@2Q1!0j2h1(1V04292v1Z1b1d242i2X2Z2$1Z0c2E1@2J2L2/0:1}2z2&1(040M030@0h0-0E2I2?1K2J2V0(2`2|0p2~000K321~342?360a382}0-0v3e2K0/352^372{3l000k3o3g2g3s3j3u3a0-0A3y3q3h3B3k3E000R3H2=3A1O2_3D3b0u3P3r3S3t393b0P3y142,0y2L2$2P0j2R2U3B0G0M0T2#1c1@3(2.333&3;0T3{3R250a0b0-0T093y0L3Q2@3Z3j090-1=0M0d0n0y0F0s4i0l0F0G0N030G0d2x0l3&3Y430,000C4y3J4d040-0J030p0c0i0y4E422i4B0I0z3H0L4W4a4z2i4H000U0p0s494b3i0M0-0B4(4Z1(4B0r4P4c434#0l0M0?0U4?3i4B4U15334Y4F430l22000t050d0M0s081}0Z0n0d5b5d5f4.562i4500092{5o4Q2_0-4`4|4'533f555w0(1j0-1y5v4@4!4I4K4M4O5C2K4)3B4B0f4V4X5T4G0-4r4t4v0s4x5R005E5L1(4+004-5)5+500-070w5X4W5Z435r5t035K3i4#0T1}035B2/5=3B5H005J5;5|5M004J4L4N4~5U0-522/0O4X6s6a4d5r0N486f4/370-4%683|6A0a4B076m5!00650^6E3f6g4:0-5_6z5p5-4,5:696R0(580-5l5e5g0%0L5j6'5n5)6#6H6o5`6t6s6:4#4h4j4l5$4u4w0F2E0G6K4A0-4D6/6G4#6j5P754R0-0Q623K460y666P5S6G4B7h6V5F3j5#4s705'7e6S007r6!7a6C0V7n415,0(4S6?6@5{7E4$7G7z7K5@7S7u6M7l6O7V4B6U7D6W5G4,7i4d6%5a5c6(1=0W0s0x6-086?6:5r2E0s0d03137s7J7W6~5&5(6q143~3)3'2-143+140s3-8f1?8f0p1$8a0o3+0/8o3^8q0T0V0X0l00
# Tests
(insensible à la casse)(Ctrl+I)