Exercice 2 : Simulations aléatoires
Concours BCPST — Informatique
Section titled “Concours BCPST — Informatique”Problème Guidé : Simulations aléatoires et modélisation stochastique
Section titled “Problème Guidé : Simulations aléatoires et modélisation stochastique”Ce problème aborde la modélisation de processus aléatoires en Python, un classique des épreuves écrites et pratiques en BCPST. L’utilisation rigoureuse des bibliothèques scientifiques et l’analyse critique des résultats numériques y sont particulièrement évaluées.
Partie I : Échauffement et manipulation de base (15 minutes)
Section titled “Partie I : Échauffement et manipulation de base (15 minutes)”On importe dans tout le sujet les modules nécessaires :
import matplotlib.pyplot as pltimport numpy as npQuestion 1
Section titled “Question 1”Écrire une fonction Python lancer_de(n) qui simule le lancer de dés à six faces équilibrés et renvoie la somme obtenue. La fonction doit utiliser les fonctionnalités vectorisées de numpy.
Corrigé :
def lancer_de(n): """Simule le lancer de n dés à 6 faces et renvoie la somme.""" # Tirage de n entiers aléatoires entre 1 et 6 inclus tirs = np.random.randint(1, 7, size=n) return int(np.sum(tirs))Analyse de l’examinateur : L’utilisation de np.random.randint(low, high, size) est optimale car elle évite les boucles for en Python, qui sont lentes. Attention aux bornes : randint exclut la borne supérieure, d’où 7 pour obtenir des valeurs jusqu’à .
Partie II : Étude d’une marche aléatoire sur (30 minutes)
Section titled “Partie II : Étude d’une marche aléatoire sur Z\mathbb{Z}Z (30 minutes)”On s’intéresse à une particule se déplaçant sur l’axe des entiers . À chaque pas de temps , la particule se déplace de avec une probabilité et de avec une probabilité .
Question 2
Section titled “Question 2”Écrire une fonction trajectoire_marche(N, p) qui simule une trajectoire de cette marche aléatoire pour pas, partant de l’origine . La fonction renvoie un tableau numpy de taille contenant la position de la particule à chaque instant de à .
Corrigé :
def trajectoire_marche(N, p): """Simule une marche aléatoire de N pas.""" # Génération des pas : +1 avec proba p, -1 avec proba 1-p pas = np.random.choice([1, -1], size=N, p=[p, 1 - p])
# La position initiale est 0, les positions successives sont les sommes cumulées trajectoire = np.zeros(N + 1, dtype=int) trajectoire[1:] = np.cumsum(pas)
return trajectoireQuestion 3
Section titled “Question 3”On pose (marche symétrique). Écrire un script utilisant matplotlib pour tracer trajectoires indépendantes de pas sur un même graphique. On veillera à ajouter un titre, des légendes pour les axes et une grille.
Corrigé :
N = 1000p = 0.5nb_trajectoires = 5
plt.figure(figsize=(10, 6))for i in range(nb_trajectoires): traj = trajectoire_marche(N, p) plt.plot(traj, label=f"Trajectoire {i+1}")
plt.title("Simulation de 5 marches aléatoires symétriques ($p = 0.5$)")plt.xlabel("Temps ($t$ en nombre de pas)")plt.ylabel("Position ($x$)")plt.legend()plt.grid(True)plt.show()Partie III : Estimation de probabilités par la méthode de Monte-Carlo (45 minutes)
Section titled “Partie III : Estimation de probabilités par la méthode de Monte-Carlo (45 minutes)”On considère une particule effectuant cette marche aléatoire symétrique () en partant de . On souhaite estimer la probabilité qu’après pas, la particule n’ait jamais touché la position (en partant de ).
Question 4
Section titled “Question 4”Écrire une fonction a_touche_cinq(N) qui simule une trajectoire de pas et renvoie un booléen indiquant si la position a été atteinte (ou dépassée) au moins une fois lors des pas.
Corrigé :
def a_touche_cinq(N): """Renvoie True si la particule atteint la position 5 au cours des N pas.""" traj = trajectoire_marche(N, 0.5) # np.any teste si au moins un élément satisfait la condition return np.any(traj >= 5)Question 5
Section titled “Question 5”Écrire une fonction estimer_probabilite(N, M) qui réalise simulations indépendantes de la marche précédente et renvoie une estimation de la probabilité de ne pas avoir touché la position au bout de pas.
Corrigé :
def estimer_probabilite(N, M): """Estime par Monte-Carlo la probabilité de ne jamais toucher 5 en N pas.""" compteur_sans_toucher = 0 for _ in range(M): if not a_touche_cinq(N): compteur_sans_toucher += 1 return compteur_sans_toucher / MAlternative plus efficace (vectorisée) :
def estimer_probabilite_opti(N, M): # Génération matricielle de M trajectoires de N pas : (M, N+1) pas = np.random.choice([1, -1], size=(M, N), p=[0.5, 0.5]) trajectoires = np.hstack([np.zeros((M, 1), dtype=int), np.cumsum(pas, axis=1)])
# Test si la valeur 5 est atteinte sur chaque ligne touche = np.any(trajectoires >= 5, axis=1)
# Proportion de trajectoires n'ayant PAS touché 5 return float(np.mean(~touche))Partie IV : Analyse critique et relecture d’expert (15 minutes)
Section titled “Partie IV : Analyse critique et relecture d’expert (15 minutes)”Un candidat propose le code suivant pour étudier la position finale moyenne après pas sur un grand nombre de simulations :
# CODE DU CANDIDATM = 100000N = 500positions_finales = []for i in range(M): t = trajectoire_marche(N, 0.5) positions_finales.append(t[-1])
moyenne = sum(positions_finales) / len(positions_finales)print("Position moyenne finale :", moyenne)Question 6
Section titled “Question 6”Analyser ce code.
- Est-il correct sur le plan mathématique et informatique ?
- Quelle critique majeure peut-on formuler concernant son coût algorithmique et sa mise en œuvre en Python ?
- Proposer une correction optimisée.
Corrigé et Analyse Critique :
- Correction mathématique : Le code est correct. Pour une marche aléatoire symétrique (), l’espérance de la position finale est nulle (). Le script affichera une valeur très proche de .
- Critique algorithmique (Performance) :
- L’utilisation d’une liste standard Python (
positions_finales.append(...)) combinée à une boucleforexplicite est extrêmement inefficace en mémoire et en temps de calcul pour . - Le typage dynamique de Python ralentit l’exécution.
- L’utilisation d’une liste standard Python (
- Version optimisée (tendue vers les attendus de concours) :
Bilan de l’expert : Cette écriture réduit le temps d’exécution d’un facteur 10 à 100 grâce à la vectorisation sous-jacente en C de la bibliothèque# Version vectorisée ultra-rapide avec NumPyM = 100000N = 500# Génération de tous les pas d'un coup : matrice (M, N)pas_matrice = np.random.choice([1, -1], size=(M, N), p=[0.5, 0.5])# Somme cumulée le long des lignes (axe 1) pour obtenir les positions finales en dernière colonnepositions_finales = np.sum(pas_matrice, axis=1)moyenne = np.mean(positions_finales)print("Position moyenne finale :", moyenne)
numpy, ce qui est indispensable face à des volumes de données importants en sciences.