15 minutes de Python
Simulations de variables aléatoires continues : la méthode d’inversion
Méthode d’inversion
La méthode d’inversion permet de simuler une variable aléatoire réelle à densité à partir d’une variable uniforme. Dans toute cette page, on se place dans le cadre suivant :
- \(X\) est une variable aléatoire réelle admettant une densité \(f\) et de fonction de répartition \( F\),
- la densité \( f \) est nulle en dehors d’un intervalle \(\left]a,b\right[\) (avec \(a\) et \(b\) éventuellement infinis),
- la fonction de répartition \(F\) est strictement croissante sur \(\left]a,b\right[\).
Fondements mathématiques
Bijection induite par la fonction de répartition
Sur l’intervalle \(\left]a,b\right[\), la fonction de répartition \(F\) est continue et strictement croissante. De plus, \[ \lim_{x\to a^+}F(x)=0 \qquad\text{et}\qquad \lim_{x\to b^-}F(x)=1 \]
Ainsi, l’application \[ G : \left]a,b \right[ \to \left]0,1 \right[, \quad x \mapsto F(x) \] est une bijection. On peut donc définir sa fonction réciproque \[ G^{-1} : \left]0,1\right[ \to \left]a,b\right[ \]
Loi de \(G^{-1}(U)\)
Soit \(U\sim\mathcal{U}(\left]0,1\right[)\) et posons \[ Y = G^{-1}(U) \] Pour tout \(x\in\left]a,b\right[\), comme \(G\) est strictement croissante, on a \begin{align*} Y \le x &\Leftrightarrow\; G^{-1}(U) \le x \\ &\Leftrightarrow\; U \le G(x) \end{align*}
Donc, comme \( G(x) = F(x) \) lorsque \( x \in \left] a,b \right[ \) : \begin{align*} \mathbb{P}(Y\le x) &=\mathbb{P}(U\le G(x)) \\ &=\mathbb{P}(U\le F(x)) \\ &=F(x) \end{align*} car \(U\) est uniforme sur \(\left]0,1\right[\) et \( F(x) \) appartient à \( ]0,1[ \). Ainsi, \(Y\) admet la même fonction de répartition que \(X\), donc elle suit la même loi que \( X\).
Conséquence pour la simulation
Le raisonnement précédent montre que si l’on sait calculer explicitement la fonction réciproque \(G^{-1}\), alors on peut simuler \(X\) de la façon suivante :
- on simule une variable uniforme \(U\) sur \(\left]0,1\right[\),
- on calcule \(X = G^{-1}(U)\).
C’est exactement le principe de la méthode d’inversion.
Exemple : loi exponentielle
Soit \(X\) une variable aléatoire de loi exponentielle de paramètre \(\lambda>0\). Elle admet une densité nulle en dehors de \(]0,+\infty[\), et sa fonction de répartition vérifie \[ F(x)=\left\{ \begin{array}{ll} 0 & \text{si }x\le 0\\ 1-e^{-\lambda x} & \text{si }x>0 \end{array} \right. \] Sur \(]0,+\infty[\), \(F\) est strictement croissante.
Pour \(u\in\left]0,1\right[\) et \( x \in \left] 0, + \infty \right[ \), on a de plus : \begin{align*} u=1-e^{-\lambda x} &\Leftrightarrow e^{-\lambda x} = 1-u &\\ &\Leftrightarrow x=-\dfrac{1}{\lambda} \, \ln(1-u) \end{align*} Ainsi, si \(U\sim\mathcal{U}(\left]0,1\right[)\), alors \[ X=-\dfrac{1}{\lambda} \, \ln(1-U) \] suit une loi exponentielle de paramètre \(\lambda\) d’après le point précédent et on peut donc simuler une réalisation de \( X\) avec le code suivant :
import numpy as np
import numpy.random as rd
# Paramètre
Lambda = 2.0
# 1) Simulation d'une uniforme sur ]0,1[
U = rd.random(N)
# 2) Inversion : X = G^{-1}(U)
X = -np.log(1 - U) / Lambda
# Renvoie de la valeur
print(X)
Exemple
import numpy as np
import numpy.random as rd
alpha = 3
U = rd.random()
X = 1 / (1 - U)**(1/alpha)
print("U =", U)
print("X =", X)
Explication
On souhaite simuler une variable aléatoire suivant une loi de Pareto de paramètre \(\alpha>0\), définie sur \([1,+\infty[\) par la fonction de répartition :
\(F(x)= \begin{cases} 0 & \text{si } x < 1\\ 1-\dfrac{1}{x^{\alpha}} & \text{si } x \ge 1 \end{cases}\)
Pour \(x \ge 1\), on a donc :
\begin{align*}u = 1 - \dfrac{1}{x^{\alpha}} &\Leftrightarrow\; \dfrac{1}{x^{\alpha}} = 1-u \\ &\Leftrightarrow\; x = \dfrac{1}{(1-u)^{1/\alpha}} \end{align*}
Ainsi, si \(U\) suit la loi uniforme sur \([0,1]\), la variable aléatoire
\(X = \dfrac{1}{(1-U)^{1/\alpha}}\)
suit une loi de Pareto de paramètre \(\alpha\).
Dans le script :
U = rd.random()simule une réalisation d’une variable uniforme sur \([0,1]\).X = 1 / (1 - U)**(1/alpha)applique la fonction réciproque \(F^{-1}\).- Les deux instructions
printaffichent la valeur tirée deUet la valeur correspondante deX.
Trois commandes utiles aujourd’hui
Clique sur une carte pour voir l’instruction utile.
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)\).
np.sum(A)
Cette instruction :
- calcule la somme des valeurs du tableau numpy
A, - renvoie un nombre (entier ou flottant selon le cas).
Si A est un tableau de booléens, alors True compte pour 1 et False pour 0 :
np.sum(A) renvoie donc le nombre de cas où la condition est vérifiée.
Par exemple, np.sum(X > 1) compte le nombre de simulations pour lesquelles \(X > 1\).
plt.hist(X, bins=..., density=True)
Cette instruction :
- construit l’histogramme des valeurs contenues dans le tableau
X, - découpe l’axe horizontal en classes (définies par
bins), - compte le nombre de valeurs dans chaque classe.
L’option density=True normalise l’histogramme pour que l’aire totale soit égale à 1 :
on obtient alors une approximation graphique de la densité de la variable aléatoire simulée.
Cela permet de visualiser la loi de X et de la comparer à une densité théorique.
Un exercice pour vérifier ta maîtrise
On considère une variable aléatoire \(X\) dont la fonction de répartition \(F\) est définie sur \(\mathbb{R}\) par
\[ \forall x \in \mathbb{R},\ F(x)=\exp(-\exp(-x)) \]
- Montrer que \(F\) est une bijection de \(\mathbb{R}\) sur \(\left]0,1\right[\) et déterminer \(F^{-1}\).
-
Compléter la fonction Python suivante pour que l’appel de
Simul_X()renvoie une simulation de \(X\).
import numpy as np
import numpy.random as rd
def Simul_X():
U = rd.random()
X = -np.log(-np.log(U))
return X
Explication
-
La fonction \(F\) est continue sur \(\mathbb{R}\) comme composée de fonctions qui le sont.
De plus, \(F\) est dérivable sur \(\mathbb{R}\) et
\begin{align*} F’(x) &=\exp(-\exp(-x))\exp(-x) \\ &>0 \end{align*}
Enfin on sait que \( F\) est une fonction de répartition donc :
\[ \lim_{x\to -\infty}F(x)=0 \qquad \lim_{x\to +\infty}F(x)=1 \]
On déduit des trois points précédents, avec le théorème de la bijection, que \(F\) réalise une bijection de \(\mathbb{R}\) sur \(\left]0,1\right[\).
-
Pour déterminer \(F^{-1}\), on résout l’équation \(F(x)=u\), d’inconnue \(x\in\mathbb{R}\), pour tout \(u\in\left]0,1\right[\).
\[ \begin{align*} \exp(-\exp(-x))=u &\Leftrightarrow -\exp(-x)=\ln(u)\\ &\Leftrightarrow \exp(-x)=-\ln(u)\\ &\Leftrightarrow -x=\ln(-\ln(u))\\ &\Leftrightarrow x=-\ln(-\ln(u)) \end{align*} \]
Ainsi :
\[ \forall u \in \left]0,1\right[,\ F^{-1}(u)=-\ln(-\ln(u)) \]
-
-
On applique la méthode d’inversion : si \(U\sim\mathcal{U}([0,1])\), alors
\[ X=F^{-1}(U)=-\ln(-\ln(U)) \]
La fonction Python doit donc calculer \(X=-\ln(-\ln(U))\) après avoir simulé \(U\).
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.