15 minutes de Python
Recherche de valeur approchée : la méthode de Monte-Carlo
Méthode de Monte-Carlo : calculer des valeurs approchées à l’aide de simulation
La méthode de Monte-Carlo repose sur une idée centrale des probabilités : une moyenne empirique obtenue à partir de nombreuses simulations se rapproche d’une espérance. Elle permet ainsi d’approximer numériquement des probabilités, des intégrales ou des sommes de séries lorsqu’un calcul exact est difficile ou impossible.
Fondement mathématique : loi faible des grands nombres et théorème de transfert
-
Soit \((X_n)_{n \in \mathbb{N}^*}\) une suite de variables aléatoires indépendantes, de même loi qu’une variable aléatoire \(X\), admettant une espérance et une variance.
On pose, pour tout \(n \in \mathbb{N}^*\) :
\(\displaystyle \overline{X}_n = \frac{1}{n}\sum_{k=1}^n X_k\)
La loi faible des grands nombres permet d’affirmer que la suite \((\overline{X}_n)\) converge en probabilité vers \(\mathbb{E}(X)\), c’est-à-dire :
\(\displaystyle \forall \varepsilon > 0,\ \lim_{n \to +\infty}\mathbb{P}\!\left(|\overline{X}_n - \mathbb{E}(X)| \ge \varepsilon\right)=0\)
Autrement dit, lorsque \(n\) est grand, il est fortement probable que la moyenne empirique \(\overline{X}_n\) soit proche de l’espérance \(\mathbb{E}(X)\). Cela fournit un moyen de proposer une valeur approchée de \(\mathbb{E}(X)\) à l’aide de simulations.
-
Le théorème de transfert permet d’utiliser ce résultat pour calculer une valeur approchée d’une somme ou d’une intégrale, en l’écrivant comme l’espérance d’une variable aléatoire de la forme \( \varphi(X) \) où \( X\) est de loi connue.
-
Cas discret.
Si \(X\) est une variable aléatoire discrète et si \(\varphi\) est une fonction définie sur \( X(\Omega)\), alors
\(\displaystyle \mathbb{E}(\varphi(X)) = \sum_{x \in X(\Omega)} \varphi(x)\,\mathbb{P}(X=x)\)
l’existence de l’espérance étant équivalente à l’absolue convergence de la série.Ainsi, une somme peut être interprétée comme une espérance et approchée par une moyenne empirique.
-
Cas à densité.
Si \(X\) admet une densité \(f\) nulle en dehors d’un intervalle \( \left]a,b\right[ \) et si \(\varphi\) est une fonction continue sur \( ]a,b [\), éventuellement privé d’un nombre fini de points, alors :
\(\displaystyle \mathbb{E}(\varphi(X)) = \int_{a}^{b} \varphi(x)\,f(x)\,\mathrm{d}x,\)
l’existence de l’espérance étant équivalente à l’absolue convergence de l’intégrale.Ainsi, une intégrale peut être interprétée comme une espérance et approchée par simulation.
-
Cas discret.
Si \(X\) est une variable aléatoire discrète et si \(\varphi\) est une fonction définie sur \( X(\Omega)\), alors
Principe de la méthode de Monte-Carlo
Quand on cherche à calculer une valeur approchée d’une somme ou d’une intégrale :
- on commence par l’écrire comme l’espérance d’une variable aléatoire de la forme \( \varphi(X) \) où \( X\) est une variable aléatoire de loi usuelle (uniforme, exponentielle, normale par exemple).,
- on simule alors un grand nombre de variables aléatoires \( X_1,\dots,X_n \) indépendantes et toutes de même loi que \( X\),
- on calcule la moyenne des valeurs prises par \( \varphi(X_1),\dots,\varphi(X_n) \) : \[ \frac{1}{n} \sum_{k=1}^n \varphi(X_k)\]
Lorsque le nombre de simulations est grand, la valeur obtenue fournit une bonne approximation de la quantité cherchée.
À retenir
- le résultat varie d’une exécution à l’autre, mais se stabilise lorsque le nombre de simulations augmente ;
- la méthode repose toujours sur une interprétation en termes d’espérance;
- la première étape est toujours : identifier clairement la variable aléatoire simulée.
Exemple
import numpy as np
import numpy.random as rd
N = 10**5
U = rd.random(N)
Y = np.exp(-U**2)
I = np.mean(Y)
print(I)
Explication
On cherche à approximer l’intégrale \(\displaystyle I=\int_0^1 e^{-x^2}\,\mathrm{d}x\).
On fixe d’abord le nombre de simulations : N = 10**5
La commande U = rd.random(N)
crée un tableau U contenant \(N\) réalisations indépendantes d’une variable aléatoire uniforme sur \([0,1]\).
La commande Y = np.exp(-U**2) permet alors de stocker dans Y les valeurs \(\big(e^{-U_1^2},\dots,e^{-U_N^2}\big)\).
La ligne I = np.mean(Y) stocke dans I la moyenne empirique des valeurs de Y, c’est-à-dire la valeur de
\[ I=\frac{1}{N}\sum_{k=1}^N e^{-U_k^2}\]
Pour interpréter le résultat affiché, on pose \(g(x)=e^{-x^2}\) et \(U\sim\mathcal U([0,1])\). On a alors, par le théorème de transfert,
\(\displaystyle \mathbb E(g(U))=\int_0^1 g(x)\,\mathrm{d}x=\int_0^1 e^{-x^2}\,\mathrm{d}x\)
et par la loi faible des grands nombres, la moyenne empirique \(\displaystyle \frac{1}{N}\sum_{k=1}^N g(U_k)\) se rapproche de \(\mathbb E(g(U))\) lorsque \(N\) est grand. Ainsi la valeur affichée est une approximation de \(\displaystyle \int_0^1 e^{-x^2}\,\mathrm{d}x\).
Trois commandes utiles aujourd’hui
Clique sur une carte pour voir l’instruction utile.
rd.exponential(1/Lambda, N)
Cette instruction :
- simule
Nréalisations indépendantes d’une loi exponentielle, - de paramètre \(\Lambda>0\) (l’argument est donc l’espérance de la loi exponentielle),
- et renvoie un tableau numpy contenant ces valeurs.
Par exemple, rd.exponential(1/Lambda, 10000) simule 10 000 valeurs
d’une variable aléatoire exponentielle de paramètre \(\Lambda\).
rd.normal(mu, sigma, N)
Cette instruction :
- simule
Nréalisations indépendantes d’une loi normale \( \mathcal{N}(\text{mu},\text{sigma}^2) \), - de moyenne
muet d’écart-typesigma, - et renvoie un tableau numpy contenant ces valeurs.
Par exemple, rd.normal(0, 1, 5000) simule 5000 valeurs
d’une variable aléatoire suivant la loi \(\mathcal{N}(0,1)\).
X > a
Si X est un tableau numpy de valeurs simulées, alors cette instruction :
- compare chaque valeur de
Xau réela, - renvoie un tableau de booléens,
- met
Truequand la valeur est strictement supérieure àa, - et
Falsesinon.
En particulier, np.mean(X > a) donne la proportion de valeurs de X supérieures à a,
ce qui fournit une estimation de la probabilité \(\mathbb{P}(X > a)\).
Un exercice pour vérifier ta maîtrise
On veut approcher numériquement la somme de la série \[ S=\sum_{n=1}^{+\infty}\frac{1}{n^2}. \]
On considère une variable aléatoire \(X\) suivant la loi géométrique de paramètre \(\frac12\), définie sur \(\mathbb{N}^*\) par \[ \forall n\in\mathbb{N}^*,\quad \mathbb{P}(X=n)=\left(\frac12\right)^n. \]
On remarque alors que \[ S=\sum_{n=1}^{+\infty}\frac{2^n}{n^2}\,\mathbb{P}(X=n) =\mathbb{E}\!\left(\frac{2^X}{X^2}\right). \]
On peut donc estimer \(S\) par Monte-Carlo en simulant de nombreuses réalisations de \(X\) et en calculant une moyenne empirique de \(\dfrac{2^X}{X^2}\).
Compléter le script ci-dessous.
import numpy as np
import numpy.random as rd
N = 10**5
X = rd.geometric(1/2, N)
Y = (2**X) / (X**2)
S = np.mean(Y)
print(S)
Explication
On veut approximer \(\displaystyle S=\sum_{n=1}^{+\infty}\frac{1}{n^2}\). La somme étant sur \( \mathbb{N}^* \), on commence par chercher une variable aléatoire à valeurs dans \( \mathbb{N}^*\), par exemple une variable aléatoire de loi géométrique. On utilise ainsi la variable aléatoire \(X\) suivant une loi géométrique de paramètre \(\frac12\), telle que : \[ \forall n \in \mathbb{N}^*,\ \mathbb{P}(X=n)=\left(\frac12\right)^n\] de telle sorte que l’on a, d’après le théorème de transfert : \[ S =\sum_{n=1}^{+\infty}\frac{2^n}{n^2}\,\mathbb{P}(X=n) = \mathbb{E}\!\left(\frac{2^X}{X^2}\right) \]
Dans le script, on fixe le nombre de simulations :
N = 10**5
La ligne
X = rd.geometric(1/2, N)
crée un tableau X contenant \(N\) réalisations indépendantes de la variable aléatoire \(X\).
Autrement dit, X représente \((X_1,\dots,X_N)\), où les \(X_k\) sont i.i.d.
et suivent la loi géométrique de paramètre \(\frac12\) sur \(\mathbb{N}^*\).
On construit ensuite
Y = (2**X) / (X**2)
Ainsi, Y est le tableau
\(\left(\dfrac{2^{X_1}}{X_1^2},\dots,\dfrac{2^{X_N}}{X_N^2}\right)\).
La commande
S = np.mean(Y)
calcule la moyenne empirique de Y, c’est-à-dire
\[ S=\frac{1}{N}\sum_{k=1}^N \frac{2^{X_k}}{X_k^2}\]
Enfin print(S) affiche une seule valeur : cette moyenne empirique.
Ainsi, la quantité affichée est une approximation Monte-Carlo de \(\mathbb{E}\!\left(\dfrac{2^X}{X^2}\right)\), donc de \(S\).
Et pour quelques minutes de plus…
Prendre en main la programmation, pas à pas.
Ces exercices sont conçus pour t’aider à maîtriser progressivement la programmation en Python, en partant de la lecture et de la compréhension du code, jusqu’à l’écriture autonome de scripts complets.
L’objectif n’est pas d’aller vite, mais de construire des automatismes solides, en comprenant ce que fait chaque instruction, puis en apprenant à les utiliser par toi-même.
L’ordre des exercices est volontaire : on lit, on comprend, on complète, puis on écrit seul. Prends le temps de chaque étape : c’est la clé pour progresser durablement.
Niveau 1 Analyser un code
Comprendre avant d’écrire.
Dans cette première série, tu observes et analyses des scripts Python déjà écrits.
L’objectif est de comprendre la logique du programme, le rôle des variables, des boucles
et des conditions, et d’être capable de prévoir ce que le code affiche ou calcule.
Niveau 2 Compléter un code
Écrire, mais avec un cadre.
Ici, tu passes à l’écriture sans partir de zéro.
Le squelette du programme est fourni : il te reste à compléter certaines instructions
pour que le code fonctionne correctement.
Ce format permet de se concentrer sur l’essentiel, sans se perdre dans la structure générale.
Niveau 3 Écrire un code complet
Passer à l’autonomie.
Dans cette dernière série, tu écris un programme complet à partir d’une consigne.
Tu dois organiser ton code, choisir les bonnes instructions et construire la logique globale du script.
C’est ici que tu mets en pratique tout ce que tu as appris dans les niveaux précédents.
Contenu réservé aux membres
Il faut être membre et connecté pour accéder aux exercices et aux corrigés.