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

.128013s3_8èufvy n7aS1me(P2C4:jtwi][h)6Oo;bcdg/T0làqAp.rFL-,}=+Nk95R{é050M0r0z0n0B0R0b0k0L0R0n0b0b0%010z0B0V010406050b0g0q0q0n0X0j040o0I0R0g130I0l0k020n0q0V0J0k0-0r1d0X0T0g0r0b050O1a1c1e1g180V041E1L051O0O1O1Q1L180M0B0i0{0}0 110E0B0N0E0R1(0E0z16050?0K0R0r1Z0~10011%1)1+1)0z1;1?1/0z0K0I0M1g1:0X1M0z0E0{1j0b0V0n0l110u011^1#010h0^0r0l1r0r1/2g2i2n1`2q1?2t0q2v040a0k0t0X0I0V0I0b0B1m1o0;2e0X0X0r0L2Q1E2x0l1M0O2c2$0z2a292b0M2z111+0l2s2N1/1W1Y0|1_2:0B2=0l261X1/0V2V1M2!2$37192h1o2{2o300X1d0R160k0p2Z3b173a2y3d1`3f3h3j0u3m2i3o2!2/013t0n3i040k0c3x2#183A3r113D3F0k0w3J3z3b3B3P3j0,3T3L3V3N3C0I3g3E3j0G3!3p3c1!3s3)3u3G0m3.3M3;3O3?3+3G0e3`3$3|3(3*3Q0+423q443X040p0Q493:2|453@0p3l1F3n3#4a4i4c0p3w4n3y4p4h3e3~3F0p3I4v2#1N351E2_2)0M2-3B0L262F0:1X1M340r363n3T054O0;4W4q2o0*160;0h4Y3{4i0A3j4-434r0h4*0B0b0?0l0L0r4=4%1`15040s504y3s160N0X0n0V0E4 4E4$5711530#3T0k3/3W163)1(2~5f394.2o5k5m5o3%0l4*1C132t2Y5g5A44530F0x3!0k5P5n5w58042B5u3n5R4?2o0I160%5z5S3O160Y2r563B530s0F5O5Q5J4r160n0g0d4O0g5b0l0z5(5Z1`5#045%5g5Y515j160.5.5B160V0r0X0b1n2=6f5K165N695^4(0L5+3E0b5W3y6a5i014)5U3)636b3C6h6j6l0l6n6s5)010I4:042~6H6C5C045a5c5e6o4i530$5?5P6t5T5V6%5!160W6:5T2s6/5I6Q5:556{645*045r0N5t6@6c045l6P70010q0B164f6 6I5L5=5g065Q6B3B6E0A1%1?6W3B6S1630627a6I6Y6`5v7b666?7h6X165E0d0i0B0;76015:5M6+7n7o6g040n2X1B0R7Q5y7A7J040l0K0d0=5c1C7u3%6668377W4b167D4X6Q7G7Q6Y4+5-7I5/165;7U7n6-715{5}0I5 2i7z7E7i160D815`7!0r7$853%530C7=447@8w4i0*6v040P0X1B895@6Q6E0B4,7)5p7Y8p8r7_8b6R5$7^5X8T825E2~0n5H8j6C536r377m7V8.8T6E2V0z5 0l8z3e7x7-7/6i1D7l8.6,8J7|6G8N7X0i0I4`6V968x6T9b8S6Q6Y6!5d6z4F6|8l8n8P8?8q7%160C8+4o917V8:168L8_656T7y9D8c5|5~608i7~7b538m8s7{04989a8^9R6(9u9w4w9y9y8Y7|848(7v6=9p6_9*9N8k546~9+97992O9W9^6p789H6J7+8|0?8~a0660(a07d164m9}9Y040F7k8,8a93048=8@a08B160)1n9l3o0O4!4V4Gax0O4J1E0z4LaC2+2%25272)0n1=az4J1K5h3B2V0q0d0h0n0*0r0d0E0c161w1y1A1C0k9!4F1R3o1L0H1o1l0^4`1@0g1o2h0X9K0`2s0k0}0X0N0r5 0W0k0Z1@4{0I0L0*0nb40k0;0`6l1c0=0`0S0k7N2O0z6j0k5Ebu0h0h2W8?0/0kbo0R000@2S0M00a`1@5V0k0s0q260f1?5n1x044U7dbt2s2*0/0#0kbW0Bbt6l0X131EbU0F7H1U1P040v0E0na)0kbk2E0zbC0k2*0@bs0Xbu4{bu1s0^bB0n0i2Wb|0n0k011W4{2i4~2mbD00735tb|8f0`0}0k0h3?1@bIa`c10g0V1?b70U2~2Ob$2M5 b1cp0X5s2=0k1nbu0lbM4Z4P3B1|1*1,1.aQ8t879pcq6Oad5x16799g7baa047gc.5216ah4Xav4P04b;a/04b9b%1ea~2e612~1Bc18h0kb{cvbMbv300l0!7N2S0=dfdcbEcT0lbFd60`bq2PbBcvbi5F8$0BcT6rb=0g0R3o1+722R0/bMbSb}dpboc22PbYb 0bcgcj4|4~c0dscP74cR0sbS300q0K2Vbh000/a41C0F79dM18dMcTbP1W2q1@bS251j0/b01_bc0Bcb0g0kd/bheedSb 0M2icucz0B2XdF6N0@d=bib%6L6ma*5{2e8g61d|0BdLeFcTbs4{ba0B0{1@egbvdB8!5GerbN5{a)0R0{0~cS1o2V0l9Ue4d!4`d$0rd{1Ed}05dM2OeNef00bv2heneZ0bb$cT0n0y8fbsb|a*9U9{0`e|cU4Od=dy0z0/bnb1cgdi1o2~0L0X0/2E61dedBe,cl5fe;0OdJd30oeMbSa|d86yc16j7dcRdZ0lbj3)131@34fq8$5 e8b$4~cv2O0N0l8Lb)dqe*bheSdEdte`c70z8ff35 fae!eA9Kde2Scv8F2Eb6b8d@dk0LbB0;1d0lbH1@e%980jbtcX2S3%c!1~1-2w7bap04arc-c~awd11Nau0;0?0^0b04.

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

.128013s3_8èufvy n7aS1me(P2C4:jtwi][h)6Oo;bcdg/T0làqAp.rFL-,}=+Nk95R{é050M0r0z0n0B0R0b0k0L0R0n0b0b0%010z0B0V010406050b0g0q0q0n0X0j040o0I0R0g130I0l0k020n0q0V0J0k0-0r1d0X0T0g0r0b050O1a1c1e1g180V041E1L051O0O1O1Q1L180M0B0i0{0}0 110E0B0N0E0R1(0E0z16050?0K0R0r1Z0~10011%1)1+1)0z1;1?1/0z0K0I0M1g1:0X1M0z0E0{1j0b0V0n0l110u011^1#010h0^0r0l1r0r1/2g2i2n1`2q1?2t0q2v040a0k0t0X0I0V0I0b0B1m1o0;2e0X0X0r0L2Q1E2x0l1M0O2c2$0z2a292b0M2z111+0l2s2N1/1W1Y0|1_2:0B2=0l261X1/0V2V1M2!2$37192h1o2{2o300X1d0R160k0p2Z3b173a2y3d1`3f3h3j0u3m2i3o2!2/013t0n3i040k0c3x2#183A3r113D3F0k0w3J3z3b3B3P3j0,3T3L3V3N3C0I3g3E3j0G3!3p3c1!3s3)3u3G0m3.3M3;3O3?3+3G0e3`3$3|3(3*3Q0+423q443X040p0Q493:2|453@0p3l1F3n3#4a4i4c0p3w4n3y4p4h3e3~3F0p3I4v2#1N351E2_2)0M2-3B0L262F0:1X1M340r363n3T054O0;4W4q2o0*160;0h4Y3{4i0A3j4-434r0h4*0B0b0?0l0L0r4=4%1`15040s504y3s160N0X0n0V0E4 4E4$5711530#3T0k3/3W163)1(2~5f394.2o5k5m5o3%0l4*1C132t2Y5g5A44530F0x3!0k5P5n5w58042B5u3n5R4?2o0I160%5z5S3O160Y2r563B530s0F5O5Q5J4r160n0g0d4O0g5b0l0z5(5Z1`5#045%5g5Y515j160.5.5B160V0r0X0b1n2=6f5K165N695^4(0L5+3E0b5W3y6a5i014)5U3)636b3C6h6j6l0l6n6s5)010I4:042~6H6C5C045a5c5e6o4i530$5?5P6t5T5V6%5!160W6:5T2s6/5I6Q5:556{645*045r0N5t6@6c045l6P70010q0B164f6 6I5L5=5g065Q6B3B6E0A1%1?6W3B6S1630627a6I6Y6`5v7b666?7h6X165E0d0i0B0;76015:5M6+7n7o6g040n2X1B0R7Q5y7A7J040l0K0d0=5c1C7u3%6668377W4b167D4X6Q7G7Q6Y4+5-7I5/165;7U7n6-715{5}0I5 2i7z7E7i160D815`7!0r7$853%530C7=447@8w4i0*6v040P0X1B895@6Q6E0B4,7)5p7Y8p8r7_8b6R5$7^5X8T825E2~0n5H8j6C536r377m7V8.8T6E2V0z5 0l8z3e7x7-7/6i1D7l8.6,8J7|6G8N7X0i0I4`6V968x6T9b8S6Q6Y6!5d6z4F6|8l8n8P8?8q7%160C8+4o917V8:168L8_656T7y9D8c5|5~608i7~7b538m8s7{04989a8^9R6(9u9w4w9y9y8Y7|848(7v6=9p6_9*9N8k546~9+97992O9W9^6p789H6J7+8|0?8~a0660(a07d164m9}9Y040F7k8,8a93048=8@a08B160)1n9l3o0O4!4V4Gax0O4J1E0z4LaC2+2%25272)0n1=az4J1K5h3B2V0q0d0h0n0*0r0d0E0c161w1y1A1C0k9!4F1R3o1L0H1o1l0^4`1@0g1o2h0X9K0`2s0k0}0X0N0r5 0W0k0Z1@4{0I0L0*0nb40k0;0`6l1c0=0`0S0k7N2O0z6j0k5Ebu0h0h2W8?0/0kbo0R000@2S0M00a`1@5V0k0s0q260f1?5n1x044U7dbt2s2*0/0#0kbW0Bbt6l0X131EbU0F7H1U1P040v0E0na)0kbk2E0zbC0k2*0@bs0Xbu4{bu1s0^bB0n0i2Wb|0n0k011W4{2i4~2mbD00735tb|8f0`0}0k0h3?1@bIa`c10g0V1?b70U2~2Ob$2M5 b1cp0X5s2=0k1nbu0lbM4Z4P3B1|1*1,1.aQ8t879pcq6Oad5x16799g7baa047gc.5216ah4Xav4P04b;a/04b9b%1ea~2e612~1Bc18h0kb{cvbMbv300l0!7N2S0=dfdcbEcT0lbFd60`bq2PbBcvbi5F8$0BcT6rb=0g0R3o1+722R0/bMbSb}dpboc22PbYb 0bcgcj4|4~c0dscP74cR0sbS300q0K2Vbh000/a41C0F79dM18dMcTbP1W2q1@bS251j0/b01_bc0Bcb0g0kd/bheedSb 0M2icucz0B2XdF6N0@d=bib%6L6ma*5{2e8g61d|0BdLeFcTbs4{ba0B0{1@egbvdB8!5GerbN5{a)0R0{0~cS1o2V0l9Ue4d!4`d$0rd{1Ed}05dM2OeNef00bv2heneZ0bb$cT0n0y8fbsb|a*9U9{0`e|cU4Od=dy0z0/bnb1cgdi1o2~0L0X0/2E61dedBe,cl5fe;0OdJd30oeMbSa|d86yc16j7dcRdZ0lbj3)131@34fq8$5 e8b$4~cv2O0N0l8Lb)dqe*bheSdEdte`c70z8ff35 fae!eA9Kde2Scv8F2Eb6b8d@dk0LbB0;1d0lbH1@e%980jbtcX2S3%c!1~1-2w7bap04arc-c~awd11Nau0;0?0^0b04.