De nombreux programmes ont besoin de valeurs aléatoires (en apparence) pour simuler des événements du monde réel.
Voici quelques exemples courants et familiers :
('Heads', 'Tails').Générer des valeurs véritablement aléatoires avec un ordinateur est un défi technique étonnamment difficile, c'est pourquoi on parle parfois de résultats « pseudo-aléatoires ».
En pratique, une bibliothèque bien conçue comme le module random de la bibliothèque standard de Python est rapide, flexible, et donne des résultats largement suffisants pour la plupart des applications de modélisation, de simulation et de jeux.
Le reste de cette page présente quelques-unes des fonctions les plus courantes de random.
Nous t'encourageons à explorer l'ensemble de la documentation de random, car elle offre bien plus d'options que ce que nous couvrons ici.
Le module random ne doit PAS être utilisé pour des applications de sécurité ou de cryptographie.
À la place, Python fournit le module secrets.
Celui-ci est spécialement optimisé pour la sécurité cryptographique.
Une partie des problèmes antérieurs et des raisons ayant conduit à la création du module secrets se trouvent dans la PEP 506.
Avant de pouvoir utiliser les outils du module random, tu dois d'abord l'importer :
>>> import random
# Choose random integer from a range
>>> random.randrange(1000)
360
>>> random.randrange(-1, 500)
228
>>> random.randrange(-10, 11, 2)
-8
# Choose random integer between two values (inclusive)
>>> random.randint(5, 25)
22
Pour éviter de taper le nom du module, tu peux importer directement certaines fonctions par leur nom :
>>> from random import choice, choices
# Using choice() to pick Heads or Tails 10 times
>>> tosses = []
... for side in range(10):
... tosses.append(choice(['H', 'T']))
>>> print(tosses)
['H', 'H', 'H', 'H', 'H', 'H', 'H', 'T', 'T', 'H']
# Using choices() to pick Heads or Tails 8 times
>>> picks = []
... picks.extend(choices(['H', 'T'], k=8))
... print(picks)
['T', 'H', 'H', 'T', 'H', 'H', 'T', 'T']
La fonction randrange() prend trois formes, pour sélectionner une valeur aléatoire dans range(start, stop, step) :
randrange(stop) donne un entier n tel que 0 <= n < stop
randrange(start, stop) donne un entier n tel que start <= n < stop
randrange(start, stop, step) donne un entier n tel que start <= n < stop et n fait partie de la suite start, start + step, start + 2*step...
Dans le cas courant où step == 1, la fonction randint(a, b) peut être plus pratique et plus lisible.
Les résultats possibles de randint() incluent la borne supérieure, donc randint(a, b) revient à utiliser randrange(a, b+1) :
>>> import random
# Select one number at random from the range 0, 499
>>> random.randrange(500)
219
# Select 10 numbers at random between 0 and 9 two steps apart.
>>> numbers = []
... for integer in range(10):
... numbers.append(random.randrange(0, 10, 2))
>>> print(numbers)
[2, 8, 4, 0, 4, 2, 6, 6, 8, 8]
# roll a die
>>> random.randint(1, 6)
4
Les fonctions de cette section supposent que tu partes d'une séquence ou d'un autre conteneur.
Il s'agira généralement d'une list, ou, avec quelques limitations, d'un tuple ou d'un set (un tuple est immuable, et un set n'est pas ordonné).
choice() et choices()
La fonction choice() renvoie une entrée choisie au hasard dans une séquence donnée.
Dans le cas le plus simple, cela peut être un lancer de pièce :
# This will pick one of the two values in the list at random 5 separate times
>>> [random.choice(['H', 'T']) for _ in range(5)]
['T', 'H', 'H', 'T', 'H']
On peut obtenir essentiellement le même résultat avec la fonction choices(), en lui passant un argument nommé qui donne la longueur de la liste :
>>> random.choices(['H', 'T'], k=5)
['T', 'H', 'T', 'H', 'H']
Dans les exemples ci-dessus, on suppose une pièce équilibrée, avec une probabilité égale de tomber sur pile ou sur face, mais on peut aussi préciser des poids. Par exemple, si un sac contient 10 boules rouges et 15 boules vertes, et qu'on veut en tirer une au hasard :
>>> random.choices(['red', 'green'], [10, 15])
['red']
sample()L'exemple avec choices() ci-dessus suppose ce que les statisticiens appellent un « échantillonnage avec remise ».
Chaque tirage ou choix n'a aucun effet sur la probabilité des choix suivants, et la distribution des choix possibles reste la même d'un tirage à l'autre.
Dans l'exemple des boules rouges et vertes : après chaque choix, on remet la boule dans le sac et on mélange bien avant le tirage suivant. Cela contraste avec une situation où on retire une boule rouge et où elle ne retourne pas dans le sac. Ne pas remettre la boule signifie qu'il y a désormais moins de boules rouges dans le sac, et que le prochain choix a moins de chances de tomber sur une rouge.
Pour simuler cet « échantillonnage sans remise », le module random fournit la fonction sample().
La syntaxe de sample() est semblable à celle de choices(), sauf qu'elle ajoute un paramètre nommé counts :
>>> random.sample(['red', 'green'], counts=[10, 15], k=10)
['green', 'green', 'green', 'green', 'green', 'red', 'red', 'red', 'red', 'green']
Les échantillons sont renvoyés dans l'ordre dans lequel ils ont été choisis.
shuffle()choices() et sample() renvoient toutes deux de nouvelles listes quand k > 1.
En revanche, shuffle() mélange l'ordre d'une liste en place, et l'ordre d'origine est perdu :
>>> my_list = [1, 2, 3, 4, 5]
>>> random.shuffle(my_list)
>>> my_list
[4, 1, 5, 2, 3]
Jusqu'ici, nous nous sommes concentrés sur des cas où tous les résultats sont équiprobables.
Par exemple, random.randrange(100) a autant de chances de donner n'importe quel entier de 0 à 99.
Beaucoup de situations réelles sont bien moins simples que cela.
C'est pourquoi les statisticiens ont créé une grande variété de distributions pour décrire mathématiquement des résultats « du monde réel ».
Pour les entiers, on utilise randrange() et randint() quand toutes les probabilités sont égales.
C'est ce qu'on appelle une distribution uniform.
Il existe des équivalents à virgule flottante de randrange() et randint().
random() renvoie une valeur float x telle que 0.0 <= x < 1.0.
uniform(a, b) renvoie x tel que a <= x <= b.
>>> [round(random.random(), 3) for _ in range(5)]
[0.876, 0.084, 0.483, 0.22, 0.863]
>>> [round(random.uniform(2, 5), 3) for _ in range(5)]
[2.798, 2.539, 3.779, 3.363, 4.33]
Aussi appelée distribution « normale » ou courbe « en cloche », c'est une façon très courante de décrire l'imprécision de valeurs mesurées.
Par exemple, imagine que l'usine où tu travailles vient d'acheter 10 000 boulons censés être identiques.
Tu veux configurer le robot de l'usine pour les manipuler, alors tu pèses un échantillon de 100 boulons et tu constates qu'ils ont un poids moyen (ou mean) de 4,731 g.
Il est extrêmement peu probable que cela signifie qu'ils pèsent tous exactement 4,731 g.
Tu constateras peut-être que les valeurs vont de 4,627 à 4,794 g mais se concentrent autour de 4,731 g.
C'est la Gaussian distribution, pour laquelle les probabilités atteignent un maximum à la moyenne et décroissent symétriquement de part et d'autre (d'où la forme « en cloche »).
Pour simuler cela dans un logiciel, il nous faut un moyen de préciser la largeur de la courbe (en général, des boulons chers se resserrent davantage autour de la moyenne que des boulons bon marché !).
Par convention, on utilise l'standard deviation : de petites valeurs pour une courbe étroite et pointue, de grandes valeurs pour une courbe basse et large.
Les mathématiciens adorent les lettres grecques, donc on utilise μ (« mu ») pour représenter la moyenne et σ (« sigma ») pour représenter l'écart type.
Ainsi, si tu lis que « 95 % des valeurs se situent à moins de 2σ de μ » ou que « le boson de Higgs a été détecté avec un niveau de confiance de 5 sigma », ce genre de remarques concerne l'écart type.
>>> mu = 4.731
>>> sigma = 0.316
>>> [round(random.gauss(mu, sigma), 3) for _ in range(5)]
[4.72, 4.957, 4.64, 4.556, 4.968]