La rumeur qui court...

On modélise la circulation de rumeurs dans un groupe de personnes à l'aide d'un graphe orienté comme ci-dessous.

flowchart LR
    C(Camille) --> R(Romane)
    C <--> M(Marion)
    M --> R
    C --> P(Paul)
    R --> N(Nicolas)
    R --> A(Antoine)
    R <--> P
    P --> A
    A <--> N
    N --> C
    S(Stéphane) --> A

L'arête "Romane → Antoine" signifie que Romane est en relation avec Antoine et l'informe des rumeurs. Cette relation n'est pas symétrique : Antoine n'informe pas Romane des rumeurs (observer l'orientation de la flèche).

L'arête "Antoine ↔ Nicolas" signifie quant à elle que Antoine et Nicolas s'informent l'un l'autre des rumeurs. La relation va donc dans les deux directions.

Lorsqu'une personne apprend une rumeur elle s'empresse de la raconter à toutes ses relations.

Ce graphe est représenté en machine par un dictionnaire dans lequel :

  • les clés sont les chaînes de caractères correspondant aux noms des personnes du groupe,

  • les valeurs associées sont des listes de chaînes de caractères représentant les noms des personnes avec qui elles sont en relation (et donc les personnes à qui elles transmettent les rumeurs).

Le graphe dessiné plus haut est ainsi représenté par le dictionnaire suivant :

🐍 Script Python
graphe = {
    'Camille':  ['Romane', 'Marion', 'Paul'],
    'Romane':   ['Nicolas', 'Antoine', 'Paul'],
    'Marion':   ['Camille', 'Romane'],
    'Paul':     ['Antoine', 'Romane'],
    'Antoine':  ['Nicolas'],
    'Nicolas':  ['Camille', 'Antoine'],
    'Stéphane': ['Antoine']
    }

Lors de sa circulation une rumeur est déformée... Connaissant la structure du graphe et l'origine de la rumeur (la première personne informée), on cherche à savoir combien de fois celle-ci a été racontée avant d'atteindre une personne donnée.

Par exemple, si la rumeur part de Romane, elle sera racontée 1 seule fois avant qu'Antoine ne l'apprenne, 3 fois avant que Marion ne l'apprenne.

Écrire la fonction distance :

  • qui prend en paramètres le graphe sous forme d'un dictionnaire (graphe), le nom de la personne à l'origine de la rumeur (origine) ainsi que le nom d'une personne (destination),

  • renvoie le nombre de fois minimal où la rumeur a été racontée entre l'origine et la destination.

Si la rumeur n'atteint pas la destination, la fonction renverra None.

Aide (1)

On pourra garder trace des personnes au courant de la rumeur à l'aide d'un dictionnaire {personne: booléen}.

Aide (2)

On pourra utiliser un parcours particulier du graphe. À ce titre, on fournit une la classe File dont on donne l'interface ci-dessous.

Interface de la classe File
  • f = File() : crée une file vide et l'affecte à la variable f;

  • f.est_vide() : renvoie le booléen indiquant si la file f est vide ;

  • f.enfile(x) : enfile l'élément x dans la file f;
  • f.defile() : défile un élément de la file f si elle n'est pas vide et le renvoie. Provoque une erreur si la file est vide.
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 __str__(self):
        """Convertit le file en une chaîne de caractères"""
        return f"{list(self.valeurs)}"
Aide (3)

On pourra gérer les personnes à qui transmettre la rumeur et du nombre de fois où celle-ci à été racontée en enfilant des tuples. Par exemple : file.enfile(('Romane', 0)).

Exemples

On utilise le graphe ci-dessous :

🐍 Console Python
>>> graphe = {
...     "Camille":  ["Romane", "Marion", "Paul"],
...     "Romane":   ["Nicolas", "Antoine", "Paul"],
...     "Marion":   ["Camille", "Romane"],
...     "Paul":     ["Antoine", "Romane"],
...     "Antoine":  ["Nicolas"],
...     "Nicolas":  ["Camille", "Antoine"],
...     "Stéphane": ["Antoine"]
...     }

On a alors :

🐍 Console Python
>>> distance(graphe, 'Romane', 'Romane')
0
>>> distance(graphe, 'Romane', 'Antoine')
1
>>> distance(graphe, 'Romane', 'Marion')
3
>>> distance(graphe, 'Romane', 'Stéphane') is None
True

###(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

.128013lS]et-dA5f18umaèg,_F/R=in{ 6)yàqPhcLN[(bs.p;r4j'C90"ovT+w73Ok:é }2030b08090j0s050J0)0D050j0J0J0r0U090s0L0U020v030J0h0i0i0j0N0y02060V050h0 0V0t0)000j0i0L0M0)0q08190N0A0h080J030p16181a1c140L02031H1A1K0p1H140b0s0W0@0_0{0}0C0s0l0C051Y0C0912030/0I05081T0`0|0U1X1Z1#1Z091*1,1(090N1I090C0@1f0J0L0j0t0}0+0U1.1V0U0e0;080t1n081(24262b1:2e1,2h0i2j02040)0B0N0V0L0V0J0s1i1k0-220N0N080D2E1A2l0t1I0p202Q1}1 1~1)0b2n0}1#0t2g2B1(1Q1S0^1/2!0s2$0t0V2)1(0L2J1I2O2Q2`15251k2+2c2:0N1905120)0f2N2~132}2m301:3234360+39263b2O2Z0U3g0j35020)0#3k2P143n3e0}3q3s0)0O3w3m2~3o3C360d3G3y3I3A3p0V333r360w3N3c2 1U3f3S3h3t0!3X3z3!3B3$3U3t0g3)3P3+3R3T3D0S3;3d3?3K020f0T3{3Z2,3@3%0f381B3a3O3|443~0f3j493l4b43313-3s0f3v4h2P1L2^1A2)2T0b1 2Y3Q0D2;2t0,1R1I2@082_3a3G034B0-4J4c2c0%120-0e3G0)3Y3J0e4T0s0J0/0t0D084L3*4411020H4+3=4d120l0N0j0L0C4*4q4P4k1:4.0m4W4Y3Q0t123S1Y2.4}2|4,2c52545e3f4T1y0 2h2M4~553?4.0x0'3N0)5w4X5i3B122p5c3a5y4=2c0V120r5h5G5j020o2f4;4Q51120H0x5v5x5q4?020j0h0n4B0h4_0t095L5S0}5I025K4~5F5.0U4.0u5R505A020L080N0J1j2$5|3o4.5u5?5Z4R0D120o3r0J5D3l5@5}0U4S020e3S5-6l575 61630t656a5z0U0V0Z122.6r3J4@4_4{6i4r6A4.0*5X5w6b5N5C663Q5:0K6V3}122g6U5p6N5U4:6'5M5~590l5b6Z4-12536z6,0U0i0s12416+5^5s5W4~0v5x6k3o6n0Z1X1,6G6W6D022:5,6^5^6t6%5d6_6X6;316#4%0n0W0s0-7p5T4/5t6Q757656120j2L1x057x0}5g7i6s120t0I0n0.4`1y7c3?5:5=2`7D6!6o5Q6 6l7o7(6H024U7'7m705U722`747C6R6A6t5$5'0V5)267h7:6l4.0G7K3p7F7H087J7+3Q4.077W447Y8h6c120X0N1x7B756S0}6n0s4V7N7,7G097I8k1:7Y7Z5E8s887-5l2.0j5o836712697@7_7_8H6n2J8A0N0t8C5~7Q7S0/601z738T7#446n6p0N8#8I0W0V4$6F8x7d6E8!8{7$4^4`4|8785877|8a8c8O8e12078R4a8,8r6A8u8w7!8H6C7P0V828G7{7F5%5(5*9q3l8H958d7$8@8_8~9a5r9c9e4i9g8T8H7k7/4K6A7*9G5!6$9P9y6(4/6*9T7q029D2C9F9Q6_7M9l9s7f7R7T8)8=5:0Y8=6{12489#7y0x7?9f5Y9i128X5)9)6j8V6d020F1j6L3b0p4N4I4sai0p4v1A094xan2W2R0j1+ak4v1G4 3o2J0i0n0e0j0%080n0C0#121s1u1w1y0)9J4r1N3b1H0$1k1h0;4$1-0h1k250N9v0?2g0)0_0N0l085)0K0)0E1-4%0V0D0%0ja.0)0-0?63180.0?0z0)7u2C09610)5lbb0e0e2K8A0(0)b5050Q0:2G0b0Qa#1-5C0)0H0i2;0k1,4X1t5 aA0sba2g1}0(0m0)4H6{ba630N0 1AbB0x6YaSax0R0C0jaN0)b12s09bj0)1}0:b90Nbb4%bb1o0;bi0j0W2Kb#0j0)0U1Q4%264)2abk0Q6.5bb#7 0?0_0)8:2sa~bq1k8A0L1,a;0c2.2CbJ2A5)a+c40N5a2$0)1jbb0tbt4M4C3o1=1!1$1'ay9b4/9658ct6/6y9}7L6?9_6|026~cQ5_12a09yag4C02bU1O1J02a?bK1aa(225+2.1xb)810)b!cabtbc2:0t0a7u2G0.c_c?blcx0tbmc-0?b72Dbicaa 5m8M0scx69c)0h053b1#02cx0b0(btbzb$d3b5b*2DbFb'0Jb{b~4'4)b(d6cNc60Hbz2:0i0I2Jce0(8(1y0x6@dq14dqcxbw1Q2e1-bz0I0V1f0(a*1/a_0sb?0h0)dPa~d@dxb'0b26c91-1Q2Ldj6x0:dSa bK6v64aO5$22805+dY0sdpeicxb94%a@0s0@1-d_bcdf8K5ne4bu5$aN050@0`cw1k2J0t9Dd)dF4$dH08dX1AdZ03dq2Ceqd^0Qbc25e0eC0JbJcx0j0P7 b9b#aO9'cmdacy4BdSdc090(b4a+b{c|1k2.0D0N0(2s5+c^dfeLc04}eQ0pdnaT1depbza%c/6hb)616{cvdE0tb03S0 1-2@f18M5)d.bJ4)ca2C0l0t8vbEd4eJa~evdid7eWb/097 e(5)0?eY0)ed9vc^2Gca8o2sa:a=0Q0(c~0Dbi0-190tbo1-eG8@0ybacB2G3QcE1@1%2k6_0%aaaccP4Kc$4Oc(af0-0/0;0J02.

###(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

.128013lS]et-dA5f18umaèg,_F/R=in{ 6)yàqPhcLN[(bs.p;r4j'C90"ovT+w73Ok:é }2030b08090j0s050J0)0D050j0J0J0r0U090s0L0U020v030J0h0i0i0j0N0y02060V050h0 0V0t0)000j0i0L0M0)0q08190N0A0h080J030p16181a1c140L02031H1A1K0p1H140b0s0W0@0_0{0}0C0s0l0C051Y0C0912030/0I05081T0`0|0U1X1Z1#1Z091*1,1(090N1I090C0@1f0J0L0j0t0}0+0U1.1V0U0e0;080t1n081(24262b1:2e1,2h0i2j02040)0B0N0V0L0V0J0s1i1k0-220N0N080D2E1A2l0t1I0p202Q1}1 1~1)0b2n0}1#0t2g2B1(1Q1S0^1/2!0s2$0t0V2)1(0L2J1I2O2Q2`15251k2+2c2:0N1905120)0f2N2~132}2m301:3234360+39263b2O2Z0U3g0j35020)0#3k2P143n3e0}3q3s0)0O3w3m2~3o3C360d3G3y3I3A3p0V333r360w3N3c2 1U3f3S3h3t0!3X3z3!3B3$3U3t0g3)3P3+3R3T3D0S3;3d3?3K020f0T3{3Z2,3@3%0f381B3a3O3|443~0f3j493l4b43313-3s0f3v4h2P1L2^1A2)2T0b1 2Y3Q0D2;2t0,1R1I2@082_3a3G034B0-4J4c2c0%120-0e3G0)3Y3J0e4T0s0J0/0t0D084L3*4411020H4+3=4d120l0N0j0L0C4*4q4P4k1:4.0m4W4Y3Q0t123S1Y2.4}2|4,2c52545e3f4T1y0 2h2M4~553?4.0x0'3N0)5w4X5i3B122p5c3a5y4=2c0V120r5h5G5j020o2f4;4Q51120H0x5v5x5q4?020j0h0n4B0h4_0t095L5S0}5I025K4~5F5.0U4.0u5R505A020L080N0J1j2$5|3o4.5u5?5Z4R0D120o3r0J5D3l5@5}0U4S020e3S5-6l575 61630t656a5z0U0V0Z122.6r3J4@4_4{6i4r6A4.0*5X5w6b5N5C663Q5:0K6V3}122g6U5p6N5U4:6'5M5~590l5b6Z4-12536z6,0U0i0s12416+5^5s5W4~0v5x6k3o6n0Z1X1,6G6W6D022:5,6^5^6t6%5d6_6X6;316#4%0n0W0s0-7p5T4/5t6Q757656120j2L1x057x0}5g7i6s120t0I0n0.4`1y7c3?5:5=2`7D6!6o5Q6 6l7o7(6H024U7'7m705U722`747C6R6A6t5$5'0V5)267h7:6l4.0G7K3p7F7H087J7+3Q4.077W447Y8h6c120X0N1x7B756S0}6n0s4V7N7,7G097I8k1:7Y7Z5E8s887-5l2.0j5o836712697@7_7_8H6n2J8A0N0t8C5~7Q7S0/601z738T7#446n6p0N8#8I0W0V4$6F8x7d6E8!8{7$4^4`4|8785877|8a8c8O8e12078R4a8,8r6A8u8w7!8H6C7P0V828G7{7F5%5(5*9q3l8H958d7$8@8_8~9a5r9c9e4i9g8T8H7k7/4K6A7*9G5!6$9P9y6(4/6*9T7q029D2C9F9Q6_7M9l9s7f7R7T8)8=5:0Y8=6{12489#7y0x7?9f5Y9i128X5)9)6j8V6d020F1j6L3b0p4N4I4sai0p4v1A094xan2W2R0j1+ak4v1G4 3o2J0i0n0e0j0%080n0C0#121s1u1w1y0)9J4r1N3b1H0$1k1h0;4$1-0h1k250N9v0?2g0)0_0N0l085)0K0)0E1-4%0V0D0%0ja.0)0-0?63180.0?0z0)7u2C09610)5lbb0e0e2K8A0(0)b5050Q0:2G0b0Qa#1-5C0)0H0i2;0k1,4X1t5 aA0sba2g1}0(0m0)4H6{ba630N0 1AbB0x6YaSax0R0C0jaN0)b12s09bj0)1}0:b90Nbb4%bb1o0;bi0j0W2Kb#0j0)0U1Q4%264)2abk0Q6.5bb#7 0?0_0)8:2sa~bq1k8A0L1,a;0c2.2CbJ2A5)a+c40N5a2$0)1jbb0tbt4M4C3o1=1!1$1'ay9b4/9658ct6/6y9}7L6?9_6|026~cQ5_12a09yag4C02bU1O1J02a?bK1aa(225+2.1xb)810)b!cabtbc2:0t0a7u2G0.c_c?blcx0tbmc-0?b72Dbicaa 5m8M0scx69c)0h053b1#02cx0b0(btbzb$d3b5b*2DbFb'0Jb{b~4'4)b(d6cNc60Hbz2:0i0I2Jce0(8(1y0x6@dq14dqcxbw1Q2e1-bz0I0V1f0(a*1/a_0sb?0h0)dPa~d@dxb'0b26c91-1Q2Ldj6x0:dSa bK6v64aO5$22805+dY0sdpeicxb94%a@0s0@1-d_bcdf8K5ne4bu5$aN050@0`cw1k2J0t9Dd)dF4$dH08dX1AdZ03dq2Ceqd^0Qbc25e0eC0JbJcx0j0P7 b9b#aO9'cmdacy4BdSdc090(b4a+b{c|1k2.0D0N0(2s5+c^dfeLc04}eQ0pdnaT1depbza%c/6hb)616{cvdE0tb03S0 1-2@f18M5)d.bJ4)ca2C0l0t8vbEd4eJa~evdid7eWb/097 e(5)0?eY0)ed9vc^2Gca8o2sa:a=0Q0(c~0Dbi0-190tbo1-eG8@0ybacB2G3QcE1@1%2k6_0%aaaccP4Kc$4Oc(af0-0/0;0J02.