Les scripts de la ruine
Les deux programmes du laboratoire, complétés et exécutés, et l'expérience de la question 26 reproduite : la moyenne qui refuse de se stabiliser.
Les programmes du laboratoire Python du DM3, complétés, exécutés et confrontés aux valeurs exactes. Comme dans le DM, tout est écrit avec des boucles et des accumulateurs, sans listes. Attention aux scripts qui contiennent random() : ce sont de vrais tirages, les sorties affichées sont celles de nos exécutions, et les vôtres différeront. C’est précisément le sujet de la dernière section.
1. Une partie sous vos yeux
Le cœur de la simulation du DM, extrait et rendu bavard : une marche aléatoire sur qui part de et affiche la fortune coup par coup, jusqu’à l’absorption. Ici et , comme sur la figure des préliminaires.
from random import random
k, N, p = 5, 10, 0.5
fortune = k
coups = 0
while 0 < fortune < N:
if random() < p:
fortune = fortune + 1
else:
fortune = fortune - 1
coups = coups + 1
print(coups, ":", fortune)
Sortie (une exécution parmi d’autres) :
1 : 6
2 : 5
3 : 4
4 : 5
5 : 6
6 : 7
7 : 8
8 : 7
9 : 8
10 : 7
11 : 6
12 : 7
13 : 8
14 : 7
15 : 8
16 : 7
17 : 8
18 : 9
19 : 10
Dix-neuf coups, des hésitations autour de , et Alice rafle tout. Relancez : vous obtiendrez une autre partie, parfois expédiée en cinq coups, parfois interminable.
2. Le calcul exact (questions Q10 et Q11)
Le premier programme du laboratoire, complété : la formule close de Q8.b), valable pour (dans le cas équitable, le DM a démontré en Q4 que ). La fonction seuil cherche la plus petite fortune commune pour laquelle la ruine dépasse .
def ruine(k, N, p):
rho = (1 - p)/p
return (rho**N - rho**k)/(rho**N - 1)
def seuil(p):
a = 1
while ruine(a, 2*a, p) <= 0.99:
a = a + 1
return a
print(ruine(10, 20, 18/37))
print(seuil(18/37))
print(ruine(85, 170, 18/37))
Sortie :
0.6319687776828178
85
0.9900058784715651
La première valeur est le de la question Q10.a) : environ , au lieu de pour un jeu équitable. La deuxième répond à Q11.b) : à partir de jetons chacun, la roulette ruine Alice avec une probabilité qui dépasse , et la troisième ligne le vérifie. Aucun hasard dans ce script : ces trois nombres sont exacts (aux arrondis de la machine près) et retomberont à l’identique chez vous.
3. La simulation (questions Q26.a et Q26.b)
Le second programme du laboratoire, complété : il ne connaît aucune formule, il joue parties parties et compte. Il renvoie la fréquence des ruines et le nombre moyen de coups observés.
from random import random
def simule(k, N, p, parties):
ruines = 0
total_coups = 0
for i in range(parties):
fortune = k
coups = 0
while 0 < fortune < N:
if random() < p:
fortune = fortune + 1
else:
fortune = fortune - 1
coups = coups + 1
if fortune == 0:
ruines = ruines + 1
total_coups = total_coups + coups
return ruines/parties, total_coups/parties
print(simule(10, 20, 18/37, 10000))
print(simule(1, 100, 0.5, 10000))
Sortie (une exécution) :
(0.6328, 97.1094)
(0.9908, 92.9378)
Première ligne, la roulette de Q26.a) : la fréquence colle à la valeur exacte du script précédent. Seconde ligne, le cas équitable de Q26.b) avec et : les valeurs exactes du DM sont et , et la simulation répond et . Le calcul et la simulation tombent d’accord : c’est la meilleure validation possible de l’un comme de l’autre.
4. L’expérience de la question Q26.c)
On reprend le même appel avec , cinq fois de suite. Les valeurs exactes sont et coups. Nécessite la fonction simule du script 3.
for essai in range(5):
print(simule(1, 10000, 0.5, 10000))
Sortie (nos cinq exécutions, environ une minute de calcul au total) :
(1.0, 12869.5328)
(0.9999, 11137.1658)
(0.9999, 10142.9102)
(1.0, 3341.4784)
(0.9998, 16761.8576)
Le contraste annoncé par l’énoncé est là. La première valeur renvoyée est d’une fidélité totale : cinq estimations entre et , pour une valeur exacte de . La seconde est déboussolée : de à coups, du simple au quintuple, autour d’une valeur exacte de qu’aucune des cinq exécutions n’approche vraiment.
5. Pourquoi la moyenne ne se stabilise pas
Les deux estimateurs n’ont pas le même ennemi. La fréquence des ruines moyenne des valeurs qui valent ou : rien ne peut y peser lourd, et sur parties son écart-type est inférieur à . La durée moyenne, elle, additionne des nombres sans plafond, et le paradoxe de Q21 travaille contre elle : presque toutes les parties sont courtes (partie de jeton, la ruine arrive le plus souvent en quelques coups), mais de très rares parties voient la fortune s’élever loin des barrières et durent alors des millions de coups. Ce sont ces parties presque introuvables qui portent l’essentiel de la moyenne : selon qu’une exécution en attrape zéro, une ou deux, son estimation s’effondre ou s’envole.
On peut le chiffrer. La méthode de conditionnement de R1, appliquée au moment d’ordre deux de la durée (on admet encore son existence), donne dans le cas équitable, pour un départ à , en notant la durée, aléatoire, d’une telle partie :
formule que nous avons vérifiée par résolution exacte du système linéaire pour plusieurs petits . Pour , l’écart-type d’une seule partie vaut environ coups, cinquante-huit fois la moyenne ; et l’écart-type de la moyenne de parties vaut encore environ coups, soit de la valeur à estimer. Nos cinq exécutions, dispersées de à , sont exactement dans cette fourchette : le programme est juste, le calcul de Q21.a) est juste, et c’est la moyenne empirique qui n’est pas un bon instrument ici. Vous tenez la version numérique du sommet du DM : un événement peut être certain, et son attente moyenne hors de portée de toute expérience.
Sources
- DM3 du chapitre A1, annexe « Le laboratoire Python » et question Q26 : les deux programmes ci-dessus en sont les complétions exactes.
- DM3, questions Q10, Q11, Q21 : les valeurs exactes utilisées comme références (, seuil , , ).
- La formule de la variance de la durée : vérifiée par résolution exacte (fractions) du système de conditionnement pour , , , , puis appliquée à .