Trois moutons sauteurs

Trois moutons indifférenciés font une partie de « saute-moutons » sur un terrain comportant \(n\) emplacements numérotés par leurs indices allant de 0 à n - 1. Ils sont initialement placés tout à gauche du terrain. Deux moutons ne peuvent pas être sur le même emplacement.

À chaque instant, on représente la « configuration » des moutons par un triplet ordonné (a, b, c) : le mouton le plus à gauche est sur l'emplacement d'indice a, celui du milieu sur l'emplacement d'indice b et le plus à droite sur celui d'indice c. Toutes les configurations vérifient donc a < b < c. La configuration initiale est représentée par le triplet (0, 1, 2).

À chaque tour, un mouton peut :

  • se déplacer d'une case à droite ou à gauche sur un emplacement libre ;
  • faire un saut sur un emplacement libre. Pour réaliser le saut, un mouton doit tout d'abord choisir n'importe lequel de ses deux camarades. Il saute alors par dessus celui-ci et atterrit sur l'emplacement symétrique de son emplacement actuel par rapport au mouton choisi. Si par exemple, le mouton situé sur l'emplacement d'indice 0 saute par dessus celui placé à l'indice 2, il atterrit sur l'emplacement d'indice 4.

La figure ci-dessous représente les configurations successives de trois moutons sur une ligne de \(10\) emplacements.

Des moutons qui jouent à « saute-moutons »

La partie va bientôt commencer 🐑 🐑 🐑

En appliquant ces règles dans le cas d'une ligne comptant 5 emplacements, à la fin du premier tour les différentes configurations possibles sont :

  • (0, 1, 3) : le mouton en 2 s'est décalé de 2 à 3 ;
  • (1, 2, 4) : le mouton en 0 a sauté par-dessus de celui en 2 et se retrouve désormais en 4 ;
  • (0, 2, 3) : le mouton en 1 a sauté par-dessus de celui en 2 et se retrouve désormais en 3 ;

On imagine que les moutons jouent de façon à découvrir toutes les différentes configurations. On souhaite obtenir un dictionnaire qui associe à chaque configuration son coût, à savoir le nombre minimum de tours pour l'atteindre à partir de la position initiale.

1. Configurations voisines

La fonction configurations_voisines prend en paramètres un triplet d'entiers config ainsi qu'un entier n. Le triplet config représente la configuration prise par les moutons à un certain instant et l'entier n le nombre d'emplacements sur la ligne.

Cette fonction renvoie la liste des triplets représentant les configurations possibles au tour suivant. Cette liste pourra être renvoyée dans n'importe quel ordre.

On garantit que :

  • n est un entier de 3 à 32 ;

  • config = (a, b, c) est un triplet bien formé, c'est-à-dire avec 0 <= a < b < c < n.

Attention, la liste renvoyée ne devra contenir que des triplets bien formés à l'image du triplet config : 0 <= a < b < c < n.

Écrire la fonction configurations_voisines.

Exemples
>>> configurations_voisines((0, 1, 2), 10)
[(0, 1, 3), (1, 2, 4), (0, 2, 3)]
>>> configurations_voisines((7, 8, 9), 10)
[(6, 8, 9), (6, 7, 9), (5, 7, 8)]
>>> configurations_voisines((2, 4, 7), 10)
[(1, 4, 7), (3, 4, 7), (2, 3, 7), (2, 5, 7), (2, 4, 6), (2, 4, 8), (4, 6, 7), (0, 2, 7), (1, 2, 4)]

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
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 : 10/10

.128013s3o_8;bcdufvg?/0ly n7apS!.r1-me,(P2=4:+twki9][5h*)é6050j0F0O0w0R0r0b0t0i0r0w0b0b0K010O0R0x010406050b0k0E0E0w0B0s040y0d0r0k0^0d0u050p0 1113150}0x041e1l051o0p1o1q1l0}0j0R0m0-0/0;0?0W0R0n0W0r1E0W0O0{050(0h0r0F1z0:0=011D1F1H1F0O1N1P1L0O0h0d0j151M0B1m0O0W0-180b0x0w0u0?0J011R1B010l0*0F0u0w0E0F1L1?1^1}1T201P23250{0a0t0I0B0d0x0d0b0R1b0u0t0$1;0B0B0F0i2q1e280u1m0p1/2D0O1-1,1.0j2a0?1H0u222n1L1w1y0.1S2N0R2P0u1)1x1L0x2w1m2B2D2+0~1@2r2V1~2!0B120r0{0t0C2A2/0|2.292;1T2?2^2`0J2}1^2 2B2M01340w2_040t0c382C0}3b320?3e3g0t0L3k3a2/3c3q2`0V3u3m3w3o3d0d2@3f2`0!3B302:1A333G353h0v3L3n3O3p3Q3I3h0f3U3D3W3F3H3r0S3$313(3y040C0q3-3N2W3)3R0C2|1f2~3C3.3_3:0C373~39403^2=3Y3g0C3j463l3M3x4b0{0C3t4f3v414a3*4k3A4n484i4r3;3K4u4h3E433T4A3V424j3;3#4F3%4H4x0C3,4L4p3P4x0J3?4R494T3R0J3}2+4v4C4I0J454%4B3/4*4e4-4G4q4!4m4=4M4@3Z0J4t4`4S3X4U4z504Y524!4E554w4!4K5a4)4U4Q5e4/4x0c4W5i4N3R0c4$3 4.5o3Z0c4,5s4?4Z5v4;5y4{5A3g0c4_5D513`5v4 5J565L5G545O5b5v595T5f5p5d5X5j5p5h5#5u3g0L5m5)4|5+5r475t5/0{0L5x5=5z573Z0L5C5{5E5}5+5I615K3:0L5N665P685S6b5U5+5W6f5Y5~5!6j5$5~5(6n5*0{0V5-6r5@040V5;4g5|5Q6t5`6B626D6y602C1n2)1e2T2G0j2K3c0i1)261m6P1p6N2-4n056U0$2*6H0Q0{0$0l3u5?1T0P2`6:6C0u0l0{6U6`1E0k0B0w2q0b0e0m0d0R2o2P0b6^6H0`040H7c676|1c200n7h5P7e0G3u0t6;3p0{1d6$6C7e0Y0M3B0t7D7s6_0{0w7n3c7p7r7t3d0{0h7J3E7L4n7F6H0u6|7M6C0d0{0K7Z7W7j6~7m4u7E7V7i0476782Y0F7b7U7N7#047%7`7y0{0U0T3B067.7N0i0C0{030t1P0,0j0Z0i3f0w0n7^7C7E7N6,040R6/7 7)047I8t5K7|0z7~2+7/5P0E0R0{6v6L80047B7-7.8n7G7;77798l7x6H7|0A7R3/7H0x0x220j8Z3_7e0H7g8V7:8w8C7{0{0D7(5K8F4k8*1~7T8=8Q7Q8/7o0{7q8x6c7Y937K0{0Y0Y8m7D8o0{8r8_988v9k3c7|0N9n3E8{3;9r3(8z8B2~8D3x7P8}1T7e8M4%8O8P8u7=8T7_2-7!0{8Y9a4C8#8%0u8)9S3(8,8.9O8u8;9z8?049q973c9t6A6*5K8 9)919D0?9?399A9T040i9_017z9e8N9I7N7X8R7?7aa18Xa1a80w8$8(a19!ae7Haj959v429C9-3E7|8^as3(9/an0496908ua09Y8+9ca49H9J5K8p9jawaq0492aC8y0{9,aS8E8G9uaO1~9xap2=999$9=0{9G3 9I9g8Q9L7@9N2~9*9Ra*9lag9V9Xa{9b7f9#a^8Q9(397N9{2C9}8!aQa%1T9pbe0?ayaF8~aoa!33a)b47daH9f8Oa70{a=abbkbf9Qal8vah9Waz8-bCb68JbraAbh7Obdbz9`bmaW9B9 bNaubNbjb07Sbsa5bb3_aM8sbT9~aEb,9waUbYaY9:b(a#0{8AbNa87wb!9Za,bt86a;8Sa?acbBbQbOa}aic9akc9afazaB9@8uaRbqa+bMbn7ubVcp01bgcsbZcm94049d84c36H888a8c7^0t0b0w1aa@4785aK5PcE04038b0wcIcK0O1QcK2s7^0 8e1Q0h0t0obtbv8qbW7$b=0{6F9;5P7|0XbCcl9|9*avb/aPbJ0|cCaL9ib+cj7:0Rc:04020r0O0gb|bpb78Ka-47a/b^boa99Mc704a`cxbUcbbFcd0{bHcfarb aGcod1a(c/dAdHd99lb.dwb#czaIa.cP3c8p0F1Hd8c~8QdPd#8W0{dedgdidKdIbAdddfdhcsb}azdm3ldoc.bx8UdFb_dubIbEa dQc0b2c|chd-d%bKcncid(daazcA4ucOa:cD89cScUcW1acZ0kc#0;0kc(0-c+c-8Qdbcs7|9yegaXc?dtc{dDcrd/0?bXd@amb%9h8qd!bac.eDeOctd*d=d-b~e6dGd`d4a/d}c5bye0d:dvdk9%e4bGb3e^7:c}edcyefeXd$eaeRd.e*bldScBdV3EcRcT0tc*cJes0tc!0$eweycVc,eTeCdceGf36H9tc@9*eLe=cqd3dpeP8@d-e e-emd6eVc=048I3h9*d+0gfufQfsdLe,elbuc4aad f8e?e3a~e{bCeZf(bRdMeHdxf5e!a8ecc^b1ej4%fZ87eofffhcXetevc%c#ezfqaJfK9lf.f=atc;cvaYfy9P04fAf/bOf`fEe#04d0dNbUfIf 6Cb*d-gefvaTd;d,f6e)e}cyfYd|f#dsc9adeMdye5gJb1dCfBcaf@gvb-gZgfbcgCf{dRf}3 gyen8a8b0iercYfkeufmg72tfpeB8ug)greFfNgkd)gmbIdcgug%aPf`g.fLaNe!9tfPh1e$dgfUgra8g)b8c1b%d59ld~cMf09oc8gXafe`dLgWgohog$gDa|hHfVckeidTcNht6Tg1g;g?g5g`exg8c*gadUgcbUh09*hm7NfxeKe9eEfGf6hchQ3EgAf6fDfRe%f6hpdlc2fcbchvdte@hx9~gSf,gRhKhn9iibc.fIhqfafr6+0{dYhfg!axaYhih{hlgBdcfSd-d3ihgLe.gNc6gPhzhF9UccgXcehAiddLf2hL8:ie9^dLg,hPi1b)0{2w0O70gIhIbUi34A0p6(0F2D2(i-6O1x6Q2G2I2E1(1*2G0w1Oi:0p6P0}j00%0)0+04.
2. Coût des positions

La fonction couts prend en paramètre un entier n donnant le nombre d'emplacements sur la ligne.

Cette fonction renvoie le dictionnaire associant à chaque configuration que peuvent prendre les moutons, le nombre minimum de tours nécessaires pour l'atteindre à partir de la position initiale.

Une version correcte de la fonction configurations_voisines de la question précédente est déjà chargée en mémoire.

Écrire cette fonction.

Exemples
>>> couts(4)
{(0, 1, 2): 0, (0, 1, 3): 1, (0, 2, 3): 1, (1, 2, 3): 2}
>>> couts(5)
{(0, 1, 2): 0, (0, 1, 3): 1, (1, 2, 4): 1, (0, 2, 3): 1, (0, 1, 4): 2, (1, 2, 3): 2, (0, 2, 4): 2, (1, 3, 4): 2, (2, 3, 4): 2, (0, 3, 4): 2}
Aide

On peut modéliser le problème à l'aide d'un graphe dans lequel les sommets sont les différentes configurations possibles. Deux configurations voisines sont reliées par une arête.

On fournit ci-dessous deux représentations de la situation pour \(n=5\).

Le graphe comportant toutes les arêtes est complexe. On en a représenté ici une version simplifiée comportant tous les sommets mais en supprimant de nombreuses arêtes de façon à ce qu'il n'existe qu'un unique chemin entre la configuration initiale et chaque configuration accessible.1

Arbre couvrant

Afin de construire le graphe précédent, on a pris soin de ne pas repasser par un sommet correspondant à une configuration déjà rencontrée (en pointillés ci-dessous).

Plusieurs configurations accessibles ne sont pas représentées afin d'alléger la figure.

Graphe

Ces représentations fournissent une approche permettant de résoudre le problème : le dictionnaire souhaité peut être obtenu à l'aide d'un parcours en largeur du graphe des configurations.

On pourra utiliser une file et enfiler, à chaque étape du parcours, le couple (configuration, distance) précisant la configuration à envisager et sa distance à la configuration initiale.

La classe File décrite ci-dessous est à ce titre déjà chargée dans l'éditeur.

La classe File
from collections import deque


class File:
    """Classe définissant une structure de file"""

    def __init__(self):
        self.valeurs = deque([])

    def est_vide(self):
        """Renvoie le booléen True si la file est vide, False sinon"""
        return len(self.valeurs) == 0

    def enfile(self, x):
        """Place x à la queue de la file"""
        self.valeurs.appendleft(x)

    def defile(self):
        """Retire et renvoie l'élément placé à la tête de la file.
        Provoque une erreur si la file est vide
        """
        if self.est_vide():
            raise ValueError("La file est vide")
        return self.valeurs.pop()

    def __repr__(self):
        """Convertit la file en une chaîne de caractères"""
        if self.est_vide():
            return "∅"
        return f"(queue) {' -> '.join(str(x) for x in self.valeurs)} (tête)"

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
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 : 10/10

.128013s3o_;8bcdufvgU/0lyàq n7apS!.r1Fûmeh,(P2=4:+Ctwki9][5R)é6050j0I0T0y0W0r0b0v0i0r0y0b0b0O010T0W0z010406050b0k0H0H0y0D0s040A0d0r0k0|0d0w0v020y0H0z0f0v0#0I160D0u0k0I0b050p13151719110z041x1E051H0p1H1J1E110j0W0m0;0?0^0`0J0W0n0J0r1X0J0T0 050,0h0r0I1S0@0_011W1Y1!1Y0T1*1,1(0T0h0d0j191)0D1F0T0J0;1c0b0z0y0w0`0N011.1U010l0.0I0w1k0I1(292b2g1:2j1,2m0H2o040a0v0M0D0d0z0d0b0W1f1h0*270D0D0I0i2J1x2q0w1F0p252V0T2322240j2s0`1!0w2l2G1(1P1R0=1/2)0W2+0w1 1Q1(0z2O1F2T2V30122a1h2;2h2_0D160r0 0v0E2S3410332r361:383a3c0N3f2b3h2T2(013m0y3b040v0c3q2U113t3k0`3w3y0v0P3C3s343u3I3c0!3M3E3O3G3v0d393x3c0(3T3i351T3l3Y3n3z0x3%3F3*3H3,3!3z0g3:3V3=3X3Z3J0X3{3j3}3Q040E0q423)2=3~3-0E3e1y3g3U434b450E3p4g3r4i4a373@3y0E3B4o3D3(3P4t0 0E3L4x2V2}0I2V2/2Y0j2$3u0i1 2y0)1Q1F4H2 3g3M054P0*4W4j2h0V0 0*0l4Y3;4b0U3c4-3|4k0l0 4P1e1w4F4z3W0~040L4=4%3l0 0w534r1:500$0Q3T0v5f0v4~440 1,0b0e4`0T4|305h4.2h0d0 0O3M5s4?370h4*0W2Q583u500L0$5e5g5i4k0 0y0e2Z0-0T0I0D5y5N5u5w5X5t55040F2k5G4 0 5J5L5f5Y5%0*2a0D0T5#5A1:5v045x4F5z540`5I5+3}0H0W0 484}5$630 0K5`6201674C654b506f605;0`6j044n326c015b5/61593H4_0d1e6g6B015}5 5r6q6i68046a30065g6A3P5P5R0D5T5V6l5Z040C6#5%2l2u0I6)6d51526b5{6C045?175_6=6h6n6G6V045o6.6x0 0$5K4F6S5M6w0w5k1v5n6E5p73500Z737c6^0I5@6{6v6?74040Y6 3W6J7w5j717g6z6M4)040U1W1,7z4b0d4:042_7q3g6U3W7m5Q5S2I6!6|6H5}6(7Z701v0T0e0m0W0*7i5-5c6z6T6M7m4P0w2j0n7/046o6L7b6D6F6p6w7y847s7U6X6Z5W7%7x0 7$7r6h7m4+5*8d3}5I776R6T7?6w7F0l3Y7K376D7`1X7+0d0W2H2+8x5|7N2@8H6@7_7{0k6Y2J5m0m8D8F1v7}6;8h6H7^1g7{7}7 7R7@567}7;788r8:7S3}7F0W4,878i8z7{8C8E2@6-8`7!7N7P8L6I8J5792705l7f4{7}5d8/8;9i8+047V6Y7X8c8Z3u7#7l0 6+8l9q5,6:9t7B8A0n8~8V8(968#83807s5}0R966s4f9x8n758p4h9i9j81049c5o5q4X6w7j9A8N8B8T8 8G8m6m0 7v9a8e5~9H827Q3r8=7L0 9N9@666O9Q9V7a7s7F2O0T8P999K8{9Z7e9#3%0p4!4I1G2~1x4K1x0T4Mar2!2W1~202Y0y1+am0p4K1D4$6H2O0H0e0l0y0V0I0e0J0c0 1p1r1t1v0v9g321K3h1E0o2+0v0y1e2O0v0b1c1e0W1g0va:1!0b1-0ka(130i0i1v2Ha=2La^5U0b0K0;0J0y0ia{1-4P2N1v2F0w0j2b0T0v0ta?350d0Gbl0+0v0j1g0ibmbo1-0%0,0z1-0j0k0v5@4`0D0:2l0v0?0D0n0I8P7$1N4S2:3}1=1Z1#1%aG3u6,2w2y2A0A0i0D0}bl0M0s251g4Y4V3(314Xalb$3W7F4+737N5h9:374^7B9ec55a5-9Aad9%7s5baYa65:9Y9!7g9$9}6M86ae8!5C6^5E9|2U6M8o7D9Y6_5^96cr8*9(ccca6r6O6Qcf6}6e9Oa49Ga24b6s6ucO6H6y9hck889{cF5!cU2h6scN4p79c$afbaab0I0rc)9_c+cb047kcK3v4*7o6`7}9?8qa7afcmc99R9;c~9AcDcxb~9S7uc`6KcHc%c8dhc:9~4(0 7H9wdnc=2Q1uc_c|0`5}0BdmcpcIc~0Yci4p9Xdo131Qbkdl967jd6cj8r9k72dC6Ia0dG2Uds1:9P7=d86H8u8wd!7m2F5maA0icF989`9ldzc^9fd,8:7E0 8v9pdx8!0 d?9E90d`0 8Kd;e92G2Ia;0w8S8U90cocydI8YcY70ead^cTcs70ce3rcz75dL3D9W8;e3048^d|ea9-9Fd!7M560ddhd)dDd{egag5maid050eE10eGeG9kdP0mdRd09sd07U0zbDbi8X9AeMen9/dc2hc!d7e)eHclahcn7}c e~5%e{9.91f96/dVdH9Lc*ey7Tc(c#dX9Yc?dAdSeXe,e.f1eU01a90+acd|da7h781xb}anaCaE1I040S0+5Ua-a/2J0va(0w0%a 0^7XbH0@bu1-6,b7c^1,0v7)b7a.0k0:a 5^0-ela)292P5pb70z1d0:2H1lf+bnfWb.a,2F8Pbof!0DbJbL1hbObQbS1G3haD0+0-0/04.

  1. il s'agit d'un arbre couvrant du graphe. Il en existe d'autres dans lesquels les chemins entre deux configurations peuvent différer.