"random" --- Génère des nombres pseudo-aléatoires
*************************************************

**Code source :** Lib/random.py

======================================================================

Ce module implémente des générateurs de nombres pseudo-aléatoires pour
différentes distributions.

Pour les entiers, il existe une sélection uniforme à partir d'une
plage. Pour les séquences, il existe une sélection uniforme d'un
élément aléatoire, une fonction pour générer une permutation aléatoire
d'une liste sur place et une fonction pour un échantillonnage
aléatoire sans remplacement.

Pour l'ensemble des réels, il y a des fonctions pour calculer des
distributions uniformes, normales (gaussiennes), log-normales,
exponentielles négatives, gamma et bêta. Pour générer des
distributions d'angles, la distribution de *von Mises* est disponible.

Presque toutes les fonctions du module dépendent de la fonction de
base "random()", qui génère un nombre à virgule flottante aléatoire de
façon uniforme dans la plage semi-ouverte [0.0, 1.0). Python utilise
l'algorithme *Mersenne Twister* comme générateur de base. Il produit
des flottants de précision de 53 bits et a une période de 2**19937-1.
L'implémentation sous-jacente en C est à la fois rapide et  compatible
avec les programmes ayant de multiples fils d'exécution. Le *Mersenne
Twister* est l'un des générateurs de nombres aléatoires les plus
largement testés qui existent. Cependant, étant complètement
déterministe, il n'est pas adapté à tous les usages et est totalement
inadapté à des fins cryptographiques.

Les fonctions fournies par ce module dépendent en réalité de méthodes
d’une instance cachée de la classe "random.Random". Vous pouvez créer
vos propres instances de "Random" pour obtenir des générateurs sans
états partagés.

La classe "Random" peut également être sous-classée si vous voulez
utiliser un générateur de base différent, de votre propre conception.
Dans ce cas, remplacez les méthodes "random()", "seed()", "gettsate()"
et "setstate()". En option, un nouveau générateur peut fournir une
méthode "getrandbits()" --- ce qui permet à "randrange()" de produire
des sélections sur une plage de taille arbitraire.

Le module "random" fournit également la classe "SystemRandom" qui
utilise la fonction système "os.urandom()" pour générer des nombres
aléatoires à partir de sources fournies par le système d'exploitation.

Avertissement:

  Les générateurs pseudo-aléatoires de ce module ne doivent pas être
  utilisés à des fins de sécurité.  Pour des utilisations de sécurité
  ou cryptographiques, voir le module "secrets".

Voir aussi:

  *M. Matsumoto and T. Nishimura, "Mersenne Twister: A
  623-dimensionally equidistributed uniform pseudorandom number
  generator", ACM Transactions on Modeling and Computer Simulation
  Vol. 8, No. 1,* Janvier pp.3--30 1998.

  Complementary-Multiply-with-Carry recipe pour un autre générateur de
  nombres aléatoires avec une longue période et des opérations de mise
  à jour relativement simples.


Fonctions de gestion d'état
===========================

random.seed(a=None, version=2)

   Initialise le générateur de nombres aléatoires.

   Si *a* est omis ou "None", l'heure système actuelle est utilisée.
   Si des sources aléatoires sont fournies par le système
   d'exploitation, elles sont utilisées à la place de l'heure système
   (voir la fonction "os.urandom()" pour les détails sur la
   disponibilité).

   Si *a* est un entier, il est utilisé directement.

   Avec la version 2 (par défaut), un objet "str", "bytes" ou
   "bytearray" est converti en "int" et tous ses bits sont utilisés.

   Avec la version 1 (fournie pour reproduire des séquences aléatoires
   produites par d'anciennes versions de Python), l'algorithme pour
   "str" et "bytes" génère une gamme plus étroite de graines.

   Modifié dans la version 3.2: Passée à la version 2 du schéma qui
   utilise tous les bits d'une graine de chaîne de caractères.

   Obsolète depuis la version 3.9: In the future, the *seed* must be
   one of the following types: *NoneType*, "int", "float", "str",
   "bytes", or "bytearray".

random.getstate()

   Renvoie un objet capturant l'état interne actuel du générateur. Cet
   objet peut être passé à "setstate()" pour restaurer cet état.

random.setstate(state)

   Il convient que *state* ait été obtenu à partir d'un appel
   précédent à "getstate()", et "setstate()" restaure l'état interne
   du générateur à ce qu'il était au moment où "getstate()" a été
   appelé.


Functions for bytes
===================

random.randbytes(n)

   Generate *n* random bytes.

   This method should not be used for generating security tokens. Use
   "secrets.token_bytes()" instead.

   Nouveau dans la version 3.9.


Fonctions pour les entiers
==========================

random.randrange(stop)
random.randrange(start, stop[, step])

   Renvoie un élément sélectionné aléatoirement à partir de
   "range(start, stop, step)".  C'est équivalent à
   "choice(range(start, stop, step))", mais ne construit pas
   réellement un objet range.

   Le motif d'argument positionnel correspond à celui de "range()".
   N'utilisez pas d'arguments nommés parce que la fonction peut les
   utiliser de manière inattendue.

   Modifié dans la version 3.2: "randrange()" est plus sophistiquée
   dans la production de valeurs uniformément distribuées.
   Auparavant, elle utilisait un style comme "int(random()*n)" qui
   pouvait produire des distributions légèrement inégales.

random.randint(a, b)

   Renvoie un entier aléatoire *N* tel que "a <= N <= b".  Alias pour
   "randrange(a, b+1)".

random.getrandbits(k)

   Returns a non-negative Python integer with *k* random bits. This
   method is supplied with the MersenneTwister generator and some
   other generators may also provide it as an optional part of the
   API. When available, "getrandbits()" enables "randrange()" to
   handle arbitrarily large ranges.

   Modifié dans la version 3.9: This method now accepts zero for *k*.


Fonctions pour les séquences
============================

random.choice(seq)

   Renvoie un élément aléatoire de la séquence non vide *seq*. Si
   *seq* est vide, lève "IndexError".

random.choices(population, weights=None, *, cum_weights=None, k=1)

   Renvoie une liste de taille *k* d'éléments choisis dans la
   *population* avec remise. Si la *population* est vide, lève
   "IndexError".

   Si une séquence de *poids* est spécifiée, les tirages sont
   effectués en fonction des poids relatifs.  Alternativement, si une
   séquence *cum_weights* est donnée, les tirages sont faits en
   fonction des poids cumulés (peut-être calculés en utilisant
   "itertools.accumulate()").  Par exemple, les poids relatifs "[10,
   5, 30, 5]" sont équivalents aux poids cumulatifs "[10, 15, 45,
   50]".  En interne, les poids relatifs sont convertis en poids
   cumulatifs avant d'effectuer les tirages, ce qui vous permet
   d'économiser du travail en fournissant des pondérations
   cumulatives.

   Si ni *weights* ni *cum_weights* ne sont spécifiés, les tirages
   sont effectués avec une probabilité uniforme.  Si une séquence de
   poids est fournie, elle doit être de la même longueur que la
   séquence *population*.  Spécifier à la fois *weights* et
   *cum_weights* lève une "TypeError".

   The *weights* or *cum_weights* can use any numeric type that
   interoperates with the "float" values returned by "random()" (that
   includes integers, floats, and fractions but excludes decimals).
   Behavior is undefined if any weight is negative.  A "ValueError" is
   raised if all weights are zero.

   Pour une graine donnée, la fonction "choices()" avec pondération
   uniforme produit généralement une séquence différente des appels
   répétés à "choice()".  L'algorithme utilisé par "choices()" utilise
   l'arithmétique à virgule flottante pour la cohérence interne et la
   vitesse.  L'algorithme utilisé par "choice()" utilise par défaut
   l'arithmétique entière avec des tirages répétés pour éviter les
   petits biais dus aux erreurs d'arrondi.

   Nouveau dans la version 3.6.

   Modifié dans la version 3.9: Raises a "ValueError" if all weights
   are zero.

random.shuffle(x[, random])

   Mélange la séquence *x* sans créer de nouvelle instance (« sur
   place »).

   L'argument optionnel *random* est une fonction sans argument
   renvoyant un nombre aléatoire à virgule flottante dans [0.0, 1.0);
   par défaut, c'est la fonction "random()".

   Pour mélanger une séquence immuable et renvoyer une nouvelle liste
   mélangée, utilisez "sample(x, k=len(x))" à la place.

   Notez que même pour les petits "len(x)", le nombre total de
   permutations de *x* peut rapidement devenir plus grand que la
   période de la plupart des générateurs de nombres aléatoires. Cela
   implique que la plupart des permutations d'une longue séquence ne
   peuvent jamais être générées.  Par exemple, une séquence de
   longueur 2080 est la plus grande qui puisse tenir dans la période
   du générateur de nombres aléatoires Mersenne Twister.

   Deprecated since version 3.9, will be removed in version 3.11: The
   optional parameter *random*.

random.sample(population, k, *, counts=None)

   Renvoie une liste de *k* éléments uniques choisis dans la séquence
   ou l'ensemble de la population. Utilisé pour un tirage aléatoire
   sans remise.

   Renvoie une nouvelle liste contenant des éléments de la population
   tout en laissant la population originale inchangée.  La liste
   résultante est classée par ordre de sélection de sorte que toutes
   les sous-tranches soient également des échantillons aléatoires
   valides.  Cela permet aux gagnants du tirage (l'échantillon) d'être
   divisés en gagnants du grand prix et en gagnants de la deuxième
   place (les sous-tranches).

   Les membres de la population n'ont pas besoin d'être *hachables* ou
   uniques. Si la population contient des répétitions, alors chaque
   occurrence est un tirage possible dans l'échantillon.

   Repeated elements can be specified one at a time or with the
   optional keyword-only *counts* parameter.  For example,
   "sample(['red', 'blue'], counts=[4, 2], k=5)" is equivalent to
   "sample(['red', 'red', 'red', 'red', 'blue', 'blue'], k=5)".

   Pour choisir un échantillon parmi un intervalle d'entiers, utilisez
   un objet "range()" comme argument.  Ceci est particulièrement
   rapide et économe en mémoire pour un tirage dans une grande
   population : "échantillon(range(10000000), k=60)".

   Si la taille de l'échantillon est supérieure à la taille de la
   population, une "ValueError" est levée.

   Modifié dans la version 3.9: Added the *counts* parameter.

   Obsolète depuis la version 3.9: In the future, the *population*
   must be a sequence.  Instances of "set" are no longer supported.
   The set must first be converted to a "list" or "tuple", preferably
   in a deterministic order so that the sample is reproducible.


Distributions pour les nombre réels
===================================

Les fonctions suivantes génèrent des distributions spécifiques en
nombre réels. Les paramètres de fonction sont nommés d'après les
variables correspondantes de l'équation de la distribution, telles
qu'elles sont utilisées dans la pratique mathématique courante ; la
plupart de ces équations peuvent être trouvées dans tout document
traitant de statistiques.

random.random()

   Renvoie le nombre aléatoire à virgule flottante suivant dans la
   plage [0.0, 1.0).

random.uniform(a, b)

   Renvoie un nombre aléatoire à virgule flottante *N* tel que "a <= N
   <= b" pour "a <= b" et "b <= N <= a" pour "b < a".

   La valeur finale "b" peut ou non être incluse dans la plage selon
   l'arrondi à virgule flottante dans l'équation "a + (b-a) *
   random()".

random.triangular(low, high, mode)

   Renvoie un nombre aléatoire en virgule flottante *N* tel que "low
   <= N <= high" et avec le *mode* spécifié entre ces bornes.  Les
   limites *low* et *high* par défaut sont zéro et un.  L'argument
   *mode* est par défaut le point médian entre les bornes, ce qui
   donne une distribution symétrique.

random.betavariate(alpha, beta)

   Distribution bêta.  Les conditions sur les paramètres sont "alpha >
   0" et "beta > 0". Les valeurs renvoyées varient entre 0 et 1.

random.expovariate(lambd)

   Distribution exponentielle.  *lambd* est 1,0 divisé par la moyenne
   désirée.  Ce ne doit pas être zéro.  (Le paramètre aurait dû
   s'appeler "lambda", mais c'est un mot réservé en Python.)  Les
   valeurs renvoyées vont de 0 à plus l'infini positif si *lambd* est
   positif, et de moins l'infini à 0 si *lambd* est négatif.

random.gammavariate(alpha, beta)

   Distribution gamma.  (*Ce n'est pas* la fonction gamma !) Les
   conditions sur les paramètres sont "alpha > 0" et "beta > 0".

   La fonction de distribution de probabilité est :

                x ** (alpha - 1) * math.exp(-x / beta)
      pdf(x) =  --------------------------------------
                  math.gamma(alpha) * beta ** alpha

random.gauss(mu, sigma)

   Distribution gaussienne.  *mu* est la moyenne et *sigma* est la
   écart type.  C'est légèrement plus rapide que la fonction
   "normalvariate()" définie ci-dessous.

   Multithreading note:  When two threads call this function
   simultaneously, it is possible that they will receive the same
   return value.  This can be avoided in three ways. 1) Have each
   thread use a different instance of the random number generator. 2)
   Put locks around all calls. 3) Use the slower, but thread-safe
   "normalvariate()" function instead.

random.lognormvariate(mu, sigma)

   Logarithme de la distribution normale.  Si vous prenez le
   logarithme naturel de cette distribution, vous obtiendrez une
   distribution normale avec *mu* moyen et écart-type *sigma*.  *mu*
   peut avoir n'importe quelle valeur et *sigma* doit être supérieur à
   zéro.

random.normalvariate(mu, sigma)

   Distribution normale.  *mu* est la moyenne et *sigma* est l'écart
   type.

random.vonmisesvariate(mu, kappa)

   *mu* est l'angle moyen, exprimé en radians entre 0 et 2**pi*, et
   *kappa* est le paramètre de concentration, qui doit être supérieur
   ou égal à zéro.  Si *kappa* est égal à zéro, cette distribution se
   réduit à un angle aléatoire uniforme sur la plage de 0 à 2**pi*.

random.paretovariate(alpha)

   Distribution de Pareto.  *alpha* est le paramètre de forme.

random.weibullvariate(alpha, beta)

   Distribution de Weibull.  *alpha* est le paramètre de l'échelle et
   *beta* est le paramètre de forme.


Générateur alternatif
=====================

class random.Random([seed])

   Classe qui implémente le générateur de nombres pseudo-aléatoires
   par défaut utilisé par le module "random".

   Obsolète depuis la version 3.9: In the future, the *seed* must be
   one of the following types: "NoneType", "int", "float", "str",
   "bytes", or "bytearray".

class random.SystemRandom([seed])

   Classe qui utilise la fonction "os.urandom()" pour générer des
   nombres aléatoires à partir de sources fournies par le système
   d'exploitation. Non disponible sur tous les systèmes. Ne repose pas
   sur un état purement logiciel et les séquences ne sont pas
   reproductibles. Par conséquent, la méthode "seed()" n'a aucun effet
   et est ignorée. Les méthodes "getstate()" et "setstate()" lèvent
   "NotImplementedError" si vous les appelez.


Remarques sur la reproductibilité
=================================

Sometimes it is useful to be able to reproduce the sequences given by
a pseudo-random number generator.  By re-using a seed value, the same
sequence should be reproducible from run to run as long as multiple
threads are not running.

La plupart des algorithmes et des fonctions de génération de graine du
module aléatoire sont susceptibles d'être modifiés d'une version à
l'autre de Python, mais deux aspects sont garantis de ne pas changer :

* Si une nouvelle méthode de génération de graine est ajoutée, une
  fonction rétro-compatible sera offerte.

* La méthode "random()" du générateur continuera à produire la même
  séquence lorsque la fonction de génération de graine compatible
  recevra la même semence.


Examples
========

Exemples de base :

   >>> random()                             # Random float:  0.0 <= x < 1.0
   0.37444887175646646

   >>> uniform(2.5, 10.0)                   # Random float:  2.5 <= x < 10.0
   3.1800146073117523

   >>> expovariate(1 / 5)                   # Interval between arrivals averaging 5 seconds
   5.148957571865031

   >>> randrange(10)                        # Integer from 0 to 9 inclusive
   7

   >>> randrange(0, 101, 2)                 # Even integer from 0 to 100 inclusive
   26

   >>> choice(['win', 'lose', 'draw'])      # Single random element from a sequence
   'draw'

   >>> deck = 'ace two three four'.split()
   >>> shuffle(deck)                        # Shuffle a list
   >>> deck
   ['four', 'two', 'ace', 'three']

   >>> sample([10, 20, 30, 40, 50], k=4)    # Four samples without replacement
   [40, 10, 50, 30]

Simulations :

   >>> # Six roulette wheel spins (weighted sampling with replacement)
   >>> choices(['red', 'black', 'green'], [18, 18, 2], k=6)
   ['red', 'green', 'black', 'black', 'red', 'black']

   >>> # Deal 20 cards without replacement from a deck
   >>> # of 52 playing cards, and determine the proportion of cards
   >>> # with a ten-value:  ten, jack, queen, or king.
   >>> dealt = sample(['tens', 'low cards'], counts=[16, 36], k=20)
   >>> dealt.count('tens') / 20
   0.15

   >>> # Estimate the probability of getting 5 or more heads from 7 spins
   >>> # of a biased coin that settles on heads 60% of the time.
   >>> def trial():
   ...     return choices('HT', cum_weights=(0.60, 1.00), k=7).count('H') >= 5
   ...
   >>> sum(trial() for i in range(10_000)) / 10_000
   0.4169

   >>> # Probability of the median of 5 samples being in middle two quartiles
   >>> def trial():
   ...     return 2_500 <= sorted(choices(range(10_000), k=5))[2] < 7_500
   ...
   >>> sum(trial() for i in range(10_000)) / 10_000
   0.7958

Example of statistical bootstrapping using resampling with replacement
to estimate a confidence interval for the mean of a sample:

   # http://statistics.about.com/od/Applications/a/Example-Of-Bootstrapping.htm
   from statistics import fmean as mean
   from random import choices

   data = [41, 50, 29, 37, 81, 30, 73, 63, 20, 35, 68, 22, 60, 31, 95]
   means = sorted(mean(choices(data, k=len(data))) for i in range(100))
   print(f'The sample mean of {mean(data):.1f} has a 90% confidence '
         f'interval from {means[5]:.1f} to {means[94]:.1f}')

Exemple d'un *resampling permutation test* pour déterminer la
signification statistique ou valeur p d'une différence observée entre
les effets d'un médicament et ceux d'un placebo :

   # Example from "Statistics is Easy" by Dennis Shasha and Manda Wilson
   from statistics import fmean as mean
   from random import shuffle

   drug = [54, 73, 53, 70, 73, 68, 52, 65, 65]
   placebo = [54, 51, 58, 44, 55, 52, 42, 47, 58, 46]
   observed_diff = mean(drug) - mean(placebo)

   n = 10_000
   count = 0
   combined = drug + placebo
   for i in range(n):
       shuffle(combined)
       new_diff = mean(combined[:len(drug)]) - mean(combined[len(drug):])
       count += (new_diff >= observed_diff)

   print(f'{n} label reshufflings produced only {count} instances with a difference')
   print(f'at least as extreme as the observed difference of {observed_diff:.1f}.')
   print(f'The one-sided p-value of {count / n:.4f} leads us to reject the null')
   print(f'hypothesis that there is no difference between the drug and the placebo.')

Simulation of arrival times and service deliveries for a multiserver
queue:

   from heapq import heappush, heappop
   from random import expovariate, gauss
   from statistics import mean, median, stdev

   average_arrival_interval = 5.6
   average_service_time = 15.0
   stdev_service_time = 3.5
   num_servers = 3

   waits = []
   arrival_time = 0.0
   servers = [0.0] * num_servers  # time when each server becomes available
   for i in range(100_000):
       arrival_time += expovariate(1.0 / average_arrival_interval)
       next_server_available = heappop(servers)
       wait = max(0.0, next_server_available - arrival_time)
       waits.append(wait)
       service_duration = gauss(average_service_time, stdev_service_time)
       service_completed = arrival_time + wait + service_duration
       heappush(servers, service_completed)

   print(f'Mean wait: {mean(waits):.1f}.  Stdev wait: {stdev(waits):.1f}.')
   print(f'Median wait: {median(waits):.1f}.  Max wait: {max(waits):.1f}.')

Voir aussi:

  Statistics for Hackers un tutoriel vidéo par Jake Vanderplas sur
  l'analyse statistique en utilisant seulement quelques concepts
  fondamentaux dont la simulation, l'échantillonnage, le brassage et
  la validation croisée.

  Economics Simulation simulation d'un marché par Peter Norvig qui
  montre l'utilisation efficace de plusieurs des outils et
  distributions fournis par ce module (*gauss*, *uniform*, *sample*,
  *betavariate*, *choice*, *triangular*, et *randrange*).

  A Concrete Introduction to Probability (using Python) un tutoriel
  par Peter Norvig couvrant les bases de la théorie des probabilités,
  comment écrire des simulations, et comment effectuer des analyses de
  données avec Python.


Recipes
=======

The default "random()" returns multiples of 2⁻⁵³ in the range *0.0 ≤ x
< 1.0*.  All such numbers are evenly spaced and are exactly
representable as Python floats.  However, many other representable
floats in that interval are not possible selections.  For example,
"0.05954861408025609" isn't an integer multiple of 2⁻⁵³.

The following recipe takes a different approach.  All floats in the
interval are possible selections.  The mantissa comes from a uniform
distribution of integers in the range *2⁵² ≤ mantissa < 2⁵³*.  The
exponent comes from a geometric distribution where exponents smaller
than *-53* occur half as often as the next larger exponent.

   from random import Random
   from math import ldexp

   class FullRandom(Random):

       def random(self):
           mantissa = 0x10_0000_0000_0000 | self.getrandbits(52)
           exponent = -53
           x = 0
           while not x:
               x = self.getrandbits(32)
               exponent += x.bit_length() - 32
           return ldexp(mantissa, exponent)

All real valued distributions in the class will use the new method:

   >>> fr = FullRandom()
   >>> fr.random()
   0.05954861408025609
   >>> fr.expovariate(0.25)
   8.87925541791544

The recipe is conceptually equivalent to an algorithm that chooses
from all the multiples of 2⁻¹⁰⁷⁴ in the range *0.0 ≤ x < 1.0*.  All
such numbers are evenly spaced, but most have to be rounded down to
the nearest representable Python float.  (The value 2⁻¹⁰⁷⁴ is the
smallest positive unnormalized float and is equal to "math.ulp(0.0)".)

Voir aussi:

  Generating Pseudo-random Floating-Point Values a paper by Allen B.
  Downey describing ways to generate more fine-grained floats than
  normally generated by "random()".
