clavier ouvert

Blog d'enseignement d'Adrien Foucart.
Toutes les opinions présentées ici n'engagent que moi. Blog garanti sans pub, sans traqueurs, et 100% rédigé par un humain.

🪙2025.11.04
simulation probabilités

[Lien vers le simulateur]

Dans le cours de mathématiques, on utilise le Paradoxe de Saint-Peterbourg comme exemple de calcul de fonction de masse et d’espérance d’une variable aléatoire.

Le paradoxe tourne autour d’un jeu qui se déroule ainsi:

Donc si, par exemple, j’ai la série: Face-Face-Pile, le jeu s’arrête après trois lancers et je reçois 22 = 4 euros.

Le paradoxe réside dans le fait que, théoriquement, l’espérance des gains obtenus est infinie. Pourtant, peu de joueurs seraient prêts à miser une grosse somme, vu que les chances de gagner plus qu’une poignée d’euros sont extrêmement faibles.

Notre cours de mathématique ne s’intéresse pas trop au cas infini. On ajoute donc une règle supplémentaire: si au bout de 10 lancers on n’a obtenu que des faces, le joueur gagne 210 = 1024 euros et la partie s’arrête aussi. Les gains ne sont plus infinis, mais le “paradoxe” reste présent. L’espérance du gain dans cette configuration est de 6 euros, mais les chances pour un joueur de gagner de l’argent en misant 6 euros sont faibles (12.5%, pour être précis).

On peut calculer toutes ces valeurs, mais dès qu’on fait des probabilités, j’aime bien simuler les choses, pour obtenir une bonne intuition de ce qui se passe. En plus, comme on enseigne à des futurs développeurs, approcher une solution via une simulation est une compétence qui semble intéressante à mettre en avant.

Le simulateur permet de voir comment l’espérance change avec le nombre maximal de lancers autorisé, et comment la précision de notre estimation de l’espérance s’améliore avec le nombre de parties simulées.

Pour que tout ça soit accessible sur une page web, on va faire un peu de Javascript. Commençons donc par la simulation d’une partie:

function jeuSaintPetersbourg(max_n) {
    let n = 1;
    while (Math.random() < 0.5) {
        if (n === max_n) return 2**n;
        n++;
    }
    return 2**(n-1);
}

On utilise pour “pile ou face” un test Math.random() < 0.5, et on considère ici que true représente face, et false représente pile. La fonction renvoie le gain: si on atteint le maximum de lancers, on renvoie 2n, sinon on s’arrête dès qu’on tombe sur pile et on renvoie 2n − 1.

Pour estimer une statistique via une simulation, il faut pouvoir relancer l’expérience beaucoup de fois. On va donc faire ça, en collectant la distribution des résultats:

function lancerSimulation(){
    // ... récupération d'inputs
    const resultats = {};
    for (let i = 0; i <= maxLancers; i++ ){
       resultats[2**i] = 0
    }

    let total = 0;
    for (let i = 0; i < numJeux; i++) {
        const gains = jeuSaintPetersbourg(maxLancers);
        resultats[gains]++;
        total += gains;
    }

    const moyenne = (total / numJeux).toFixed(2);
    // ... affichage des résultats
}

maxLancers et numJeux sont récupérés dans des <input>. On initialise le tableau des résultats avec tous les gains possibles, puis on lance numJeux parties, et on collecte les gains obtenus. On calcule notre estimation de l’espérance en faisant la moyenne des gains. L’affichage se fait via un diagramme en barres fait dans un <canvas>.

Il est clair que miser beaucoup sur une partie n’est pas très avantageux. Mais si on peut miser un peu sur beaucoup de parties, par contre, la dynamique change. C’est la deuxième partie de la simulation: que ce passe-t-il si on peut rejouer?

On va maintenant calculer le total des gains si on joue une série de parties, chacune avec une mise fixe:

function totalSerie(numJeux, prix) {
    let total = 0;
    for(let i = 0; i < numJeux; i++){
        res = jeuSaintPetersbourg(10);
        total += res - prix;
    }
    return total;
}

Et on va cette fois-ci dans notre simulation regarder une variable aléatoire binaire: est-ce qu’on gagne de l’argent à la fin? (bon, en vrai je l’ai faite ternaire parce que j’ai séparé le cas où on récupérait exactement notre mise).

function lancerSerieJeux(){
    // ... récupération d'inputs
    let partiesGagnees = 0;
    let partiesPerdues = 0;
    let partiesEgales = 0;
    for( let i = 0; i < numSimulations; i++ ){
        gains = totalSerie(numJeux, prix);
        if(gains > 0){
            partiesGagnees += 1;
        }
        else if( gains < 0){
            partiesPerdues += 1;
        }
        else{
            partiesEgales += 1;
        }
    }
    // ... affichage des résultats
}

En jouant avec le simulateur, on peut ainsi facilement voir que si on mise une fois 1000 euros, on va vraisemblablement les perdre (0.1% de victoires). Si par contre je joue 200 parties à 5 euros, j’ai plus d’une chance sur deux de sortir vainqueur de l’échange. Enfin, sauf si on compte le temps perdu à jeter la pièce.

Commentaires, remarques, erreurs qu'il faut absolument me faire remarquer? Contactez-moi sur Mastodon ou par mail (adrien@adfoucart.be)