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 __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)"
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)
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

.128013bqO,9vià3o_;}jlpwTf( g0]6-)2As1+8ené4è[m5tCLRPhkN:c.a=ryFSu/7{d050/0I0Q0#0h0p0E0v0Z0p0#0E0E0$010Q0h0q010406050E0+0O0O0#0%0(040*0k0p0+130k0J0v020#0O0q0m0v0T0I1d0%0c0+0I0E050,1a1c1e1g180q04051L1E1O0,1L180/0h0g0{0}0 110V0h0w0V0p1$0V0Q16050?0b0p0I1X0~10011#1%1)1%0Q1/1;1-0Q0%1M0Q0V0{1j0E0q0#0J110C011?1Z010t0^0I0J1r0I1-292b2g1^2j1;2m0O2o040a0v0U0%0k0q0k0E0h1m1o0;270%0%0I0Z2J1E2q0J1M0,252V2224231.0/2s111)0J2l2G1-1U1W0|1@2)0h2+0J0k2/1-0q2O1M2T2V30192a1o2;2h2_0%1d0p160v0F2S3417332r361^383a3c0C3f2b3h2T2(013m0#3b040v0j3q2U183t3k113w3y0v0L3C3s343u3I3c0P3M3E3O3G3v0k393x3c0z3T3i351Y3l3Y3n3z0-3%3F3*3H3,3!3z0H3:3V3=3X3Z3J0f3{3j3}3Q040F0x423)2=3~3-0F3e1F3g3U434b450F3p4g3r4i4a373@3y0F3B4o2U1P2~1E2/2Y0/242%3W0Z2`2y0:1V1M2}0I2 3g3M054I0;4Q4j2h0W160;0t4S3;4b0r3c4%3|4k0t4!0h0E0?0J0Z0I4,4X1^15040u4`4r3l160w0%0#0q0V4_4x4W51114}0e3M0v3(3P163Y1$2@59324(2h5e5g5i3W0J4!1C132m2R5a5u3}4}0B0Y3T0v5J5h5q52042u5o3g5L4-2h0k160$5t5M3H160)2k503u4}0u0B5I5K5D4k160#0+0l4I0+550J0Q5Y5T1^5V045X5a5S4{5d160.5(5v160q0I0%0E1n2+695E165H635/4Y0Z5#3x0E5Q3r645c014Z5O3Y5}653v6b6d6f0J6h6m5Z010k4*042@6B6w5w045456586i4b4}0n5-5J6n5N5P6X5U160!6*5N2l6)5C6K5*4 6=5~5!045l0w5n6.66045f6J6`010O0h16486_6C5F5,5a065K6v3u6y0r1#1;6Q3u6M162_5|746C6S6;5p75606-7b6R165y0l0g0h0;70015*5G6#7h7i6a040#2Q1B0p7K5s7u7D040J0b0l0=561C7o3W6062307Q44167x4R6K7A7K6S4#5%7C5)165+7O7h6%6{5=5@0k5_2b7t7y7c160N7{5;7U0I7W7 3W4}0y7,3}7.8q4b0W6p040s0%1B835.6K6y0h4$7Z5j7S8j8l7:856L5W7/5R8N7|5y2@0#5B8d6w4}6l307g7P8(8N6y2O0Q5_0J8t377r7%7)6c1D7f8(6$8D7?6A8H7R0g0k4;6P908r6N958M6K6S6U576t4y6?8f8h8J8-8k7X160y8#4h8{7P8*168F8:5 6N7s9x865?5^5`8c7^754}8g8m7=0492948/9L6Y9o9q4p9s9s8S7?7~8Y7p6,9j6:9!9H8e4~6^9#91932H9Q9/6j729B6D7#8?0?8^9`600G9`77164f9@9S040B7e8$848}048,8.9`8v160X1n9f3h0,4U4P4zar0,4C1E0Q4Eaw2#2W0#1:at4C1K5b3u2O0O0l0t0#0W0I0l0V0j161w1y1A1C0v9U4y1R3h1L0d1o1l0^4;1=0+1o2a0%9E0`2l0v0}0%0w0I5_0!0v0S1=4=0k0Z0W0#a{0v0;0`6f1c0=0`0i0v7H2H0Q6d0v5ybl0t0t2P8-0K0vbf0p000@2L0/00a.1=5P0v0u0O2`0M1;5h1x044O77bk2l220K0e0vbN0hbk6f0%131EbL0B7B1S1N040R0V0#aW0vbb2x0Qbt0v220@bj0%bl4=bl1s0^bs0#0g2Pb:0#0v011U4=2b4^2fbu006}5nb:890`0}0v0t3,1=bza.b^0+0q1;a~0D2@2HbT2F5_a^cg0%5m2+0v1nbl0JbD4T4J3u1`1(1*1,aH8n819jch6Ia75r16739a75a4047ac#4|16ab4Rap4J04b(a$04b0bU1ea=275{2@1Bb^8b0vb/cmbDbm2_0J0A7H2L0=d6d3bvcK0Jbwc}0`bh2Ibscmb95z8W0hcK6lb)0+0p3h1)6|2K0KbDbJb;dgbfb_2IbPb?0Ec7ca4?4^b@djcG6~cI0ubJ2_0O0b2Ob8000K9~1C0B73dD18dDcKbG1U2j1=bJ0b0k1j0Ka@1@b30hc20+0vd$b8e6dJb?0/2bclcq0h2Qdw6H0@d)b9bU6F6gaX5=278a5{d:0hdCexcKbj4=b10h0{1=e8bmds8U5AejbE5=aW0p0{0~cJ1o2O0J9Od{dR4;dT0Id/1Ed;05dD2HeFe700bm2aefeR0EbTcK0#0o89bjb:aX9O9=0`e;cL4Id)dp0Q0Kbea^c7d91o2@0Z0%0K2x5{d5dse!cc59e)0,dAc`0*eEbJa:c 6sb^6d77cIdQ0Jba3Y131=2}fi8W5_e0bT4^cm2H0w0J8FbWdheYb8eKdvdke/b~0Q89e{5_f2eSes9Ed52Lcm8z2xa}a d+db0Zbs0;1d0Jby1=eV920(bkcO2L3WcR1|1+2p75aj04alc!c=aqc^1Pao0;0?0^0E04.

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

.128013bqO,9vià3o_;}jlpwTf( g0]6-)2As1+8ené4è[m5tCLRPhkN:c.a=ryFSu/7{d050/0I0Q0#0h0p0E0v0Z0p0#0E0E0$010Q0h0q010406050E0+0O0O0#0%0(040*0k0p0+130k0J0v020#0O0q0m0v0T0I1d0%0c0+0I0E050,1a1c1e1g180q04051L1E1O0,1L180/0h0g0{0}0 110V0h0w0V0p1$0V0Q16050?0b0p0I1X0~10011#1%1)1%0Q1/1;1-0Q0%1M0Q0V0{1j0E0q0#0J110C011?1Z010t0^0I0J1r0I1-292b2g1^2j1;2m0O2o040a0v0U0%0k0q0k0E0h1m1o0;270%0%0I0Z2J1E2q0J1M0,252V2224231.0/2s111)0J2l2G1-1U1W0|1@2)0h2+0J0k2/1-0q2O1M2T2V30192a1o2;2h2_0%1d0p160v0F2S3417332r361^383a3c0C3f2b3h2T2(013m0#3b040v0j3q2U183t3k113w3y0v0L3C3s343u3I3c0P3M3E3O3G3v0k393x3c0z3T3i351Y3l3Y3n3z0-3%3F3*3H3,3!3z0H3:3V3=3X3Z3J0f3{3j3}3Q040F0x423)2=3~3-0F3e1F3g3U434b450F3p4g3r4i4a373@3y0F3B4o2U1P2~1E2/2Y0/242%3W0Z2`2y0:1V1M2}0I2 3g3M054I0;4Q4j2h0W160;0t4S3;4b0r3c4%3|4k0t4!0h0E0?0J0Z0I4,4X1^15040u4`4r3l160w0%0#0q0V4_4x4W51114}0e3M0v3(3P163Y1$2@59324(2h5e5g5i3W0J4!1C132m2R5a5u3}4}0B0Y3T0v5J5h5q52042u5o3g5L4-2h0k160$5t5M3H160)2k503u4}0u0B5I5K5D4k160#0+0l4I0+550J0Q5Y5T1^5V045X5a5S4{5d160.5(5v160q0I0%0E1n2+695E165H635/4Y0Z5#3x0E5Q3r645c014Z5O3Y5}653v6b6d6f0J6h6m5Z010k4*042@6B6w5w045456586i4b4}0n5-5J6n5N5P6X5U160!6*5N2l6)5C6K5*4 6=5~5!045l0w5n6.66045f6J6`010O0h16486_6C5F5,5a065K6v3u6y0r1#1;6Q3u6M162_5|746C6S6;5p75606-7b6R165y0l0g0h0;70015*5G6#7h7i6a040#2Q1B0p7K5s7u7D040J0b0l0=561C7o3W6062307Q44167x4R6K7A7K6S4#5%7C5)165+7O7h6%6{5=5@0k5_2b7t7y7c160N7{5;7U0I7W7 3W4}0y7,3}7.8q4b0W6p040s0%1B835.6K6y0h4$7Z5j7S8j8l7:856L5W7/5R8N7|5y2@0#5B8d6w4}6l307g7P8(8N6y2O0Q5_0J8t377r7%7)6c1D7f8(6$8D7?6A8H7R0g0k4;6P908r6N958M6K6S6U576t4y6?8f8h8J8-8k7X160y8#4h8{7P8*168F8:5 6N7s9x865?5^5`8c7^754}8g8m7=0492948/9L6Y9o9q4p9s9s8S7?7~8Y7p6,9j6:9!9H8e4~6^9#91932H9Q9/6j729B6D7#8?0?8^9`600G9`77164f9@9S040B7e8$848}048,8.9`8v160X1n9f3h0,4U4P4zar0,4C1E0Q4Eaw2#2W0#1:at4C1K5b3u2O0O0l0t0#0W0I0l0V0j161w1y1A1C0v9U4y1R3h1L0d1o1l0^4;1=0+1o2a0%9E0`2l0v0}0%0w0I5_0!0v0S1=4=0k0Z0W0#a{0v0;0`6f1c0=0`0i0v7H2H0Q6d0v5ybl0t0t2P8-0K0vbf0p000@2L0/00a.1=5P0v0u0O2`0M1;5h1x044O77bk2l220K0e0vbN0hbk6f0%131EbL0B7B1S1N040R0V0#aW0vbb2x0Qbt0v220@bj0%bl4=bl1s0^bs0#0g2Pb:0#0v011U4=2b4^2fbu006}5nb:890`0}0v0t3,1=bza.b^0+0q1;a~0D2@2HbT2F5_a^cg0%5m2+0v1nbl0JbD4T4J3u1`1(1*1,aH8n819jch6Ia75r16739a75a4047ac#4|16ab4Rap4J04b(a$04b0bU1ea=275{2@1Bb^8b0vb/cmbDbm2_0J0A7H2L0=d6d3bvcK0Jbwc}0`bh2Ibscmb95z8W0hcK6lb)0+0p3h1)6|2K0KbDbJb;dgbfb_2IbPb?0Ec7ca4?4^b@djcG6~cI0ubJ2_0O0b2Ob8000K9~1C0B73dD18dDcKbG1U2j1=bJ0b0k1j0Ka@1@b30hc20+0vd$b8e6dJb?0/2bclcq0h2Qdw6H0@d)b9bU6F6gaX5=278a5{d:0hdCexcKbj4=b10h0{1=e8bmds8U5AejbE5=aW0p0{0~cJ1o2O0J9Od{dR4;dT0Id/1Ed;05dD2HeFe700bm2aefeR0EbTcK0#0o89bjb:aX9O9=0`e;cL4Id)dp0Q0Kbea^c7d91o2@0Z0%0K2x5{d5dse!cc59e)0,dAc`0*eEbJa:c 6sb^6d77cIdQ0Jba3Y131=2}fi8W5_e0bT4^cm2H0w0J8FbWdheYb8eKdvdke/b~0Q89e{5_f2eSes9Ed52Lcm8z2xa}a d+db0Zbs0;1d0Jby1=eV920(bkcO2L3WcR1|1+2p75aj04alc!c=aqc^1Pao0;0?0^0E04.