web-dev-qa-db-fra.com

Random.uniform (0,1) peut-il jamais générer 0 ou 1?

Dans la documentation , il est dit que uniform(0,1) peut générer les valeurs 0 Et 1.

J'ai exécuté uniform(0, 1) 10000 fois, mais il n'a jamais produit zéro. Même dans le cas de uniform(0, 0.001).

random.uniform(0,1) peut-il jamais générer 0 Ou 1?

9
Venkatesh Gandi

uniform(0, 1) peut produire 0, mais il ne produira jamais 1.

La documentation vous indique que le point final b pourrait être inclus dans les valeurs produites:

La valeur de point final b peut ou non être incluse dans la plage en fonction de l'arrondi à virgule flottante dans l'équation a + (b-a) * random().

Ainsi, pour uniform(0, 1), la formule 0 + (1-0) * random(), simplifiée en 1 * random(), devrait être capable de produire exactement 1. Cela ne se produirait que si random.random() est 1.0 exactly. However, Random () *never* produces 1.0`.

Citant la documentation random.random() :

Renvoie le prochain nombre à virgule flottante aléatoire dans la plage [0,0, 1,0).

La notation [..., ...) Signifie que la première valeur fait partie de toutes les valeurs possibles, mais pas la seconde. random.random() produira tout au plus des valeurs très proches de 1.0. Le type float de Python est un valeur à virgule flottante base64 IEEE 754 , qui code un certain nombre de fractions binaires ( 1/2, 1/4, 1/5, etc.) qui composent la valeur, et la valeur produite par random.random() est simplement la somme d'une sélection aléatoire de ces 53 fractions de 2 ** -1 (1/2) à 2 ** -53 (1/9007199254740992).

Cependant, comme il peut produire des valeurs très proches de 1.0, Ainsi que des erreurs d'arrondi qui se produisent lorsque vous multipliez des nombres à virgule flottante, vous pouvez produire b pour certaines valeurs de a et b. Mais 0 Et 1 Ne font pas partie de ces valeurs.

Notez que random.random() peut produire 0,0, donc a est toujours inclus dans les valeurs possibles pour random.uniform() (a + (b - a) * 0 == a). Comme il y a 2 ** 53 Différentes valeurs que random.random() peut produire (toutes les combinaisons possibles de ces 53 fractions binaires), il n'y a qu'un 1 dans 2 ** 53 (Donc 1 dans 9007199254740992) chance que cela se produise jamais.

Ainsi, la valeur la plus élevée possible que random.random() peut produire est 1 - (2 ** -53); il suffit de choisir une valeur suffisamment petite pour b - a pour permettre l'arrondi pour démarrer lorsqu'il est multiplié par des valeurs random.random() plus élevées. Plus le b - a Est petit, plus les chances que cela se produise sont grandes:

>>> import random, sys
>>> def find_b():
...     a, b = 0, sys.float_info.epsilon
...     while random.uniform(a, b) != b:
...         b /= 2
...     else:
...         return b
...
>>> print("uniform(0, {0}) == {0}".format(find_b()))
...
uniform(0, 4e-323) == 4e-323

Si vous frappez b = 0.0, Alors nous avons divisé 1023 fois, la valeur ci-dessus signifie que nous avons eu de la chance après 1019 divisions. La valeur la plus élevée que j'ai trouvée jusqu'à présent (exécuter la fonction ci-dessus dans une boucle avec max()) est 8.095e-320 (1008 divisions), mais il y a probablement des valeurs plus élevées. C'est tout un jeu de hasard. :-)

Cela peut également se produire s'il n'y a pas beaucoup d'étapes discrètes entre a et b, comme lorsque a et b ont un exposant élevé et peuvent donc sembler être loin appart. Les valeurs en virgule flottante ne sont encore que des approximations et le nombre de valeurs qu'elles peuvent coder est fini. Par exemple, il n'y a qu'une seule fraction binaire de différence entre sys.float_info.max Et sys.float_info.max - (2 ** 970), il y a donc 50-50 chance random.uniform(sys.float_info.max - (2 ** 970), sys.float_info.max) produit sys.float_info.max:

>>> a, b = sys.float_info.max - (2 ** 970), sys.float_info.max
>>> values = [random.uniform(a, b) for _ in range(10000)]
>>> values.count(sys.float_info.max)  # should be roughly 5000
4997
13
Martijn Pieters

"Plusieurs fois" ne suffit pas. 10 000 n'est pas suffisant. random.uniform choisit parmi 2 ^ 53 (9,007,199,254,740,992) valeurs différentes. Vous êtes intéressé par deux d'entre eux. En tant que tel, vous devez vous attendre à générer plusieurs quadrillion valeurs aléatoires avant d'obtenir une valeur qui est exactement 0 ou 1. Il est donc possible, mais il est très probable que vous ne l'observerez jamais.

5
hobbs

Sûr. Vous étiez déjà sur la bonne voie en essayant uniform(0, 0.001) à la place. Continuez à restreindre suffisamment les limites pour que cela se produise plus tôt.

>>> random.uniform(0., 5e-324)
5e-324
>>> random.uniform(0., 5e-324)
5e-324
>>> random.uniform(0., 5e-324)
0.0
1
wim

Vous pouvez essayer de générer une boucle qui compte le nombre d'itérations nécessaires pour afficher un 0 exact (ne le faites pas).

En outre, comme l'a indiqué Hobbs, le montant des valeurs échantillonnées uniformly est de 9 007 199 254 740 992. Ce qui signifie que la probabilité de voir un 0 est exactement de 1/9 007 199 254 740 992. Ce qui en termes généraux et en arrondissant signifie que vous aurez besoin en moyenne 10 quatrillions d'échantillons pour trouver un 0. Bien sûr, vous pourriez trouvez-le dans vos 10 premières tentatives, ou jamais.

L'échantillonnage de 1 est impossible car l'intervalle défini pour les valeurs est fermé par une parenthèse, donc sans inclure 1.

1
Celius Stingher