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

9

Dans la documentation, il est dit qu'il existe une chance qui uniform(0,1)peut générer les valeurs 0et 1.

J'ai couru uniform(0, 1)10 000 fois, mais cela n'a jamais produit zéro. Même dans le cas de uniform(0, 0.001).

Peut random.uniform(0,1)jamais générer 0ou 1?

Venkatesh Gandi
la source
3
C'est théoriquement possible, mais pratiquement ne se produira jamais. Mathématiquement, une variable aléatoire uniforme standard peut prendre n'importe quelle valeur dans l'intervalle de 0 à 1. si X ~ U(0,1), alors le P(X=x)est presque sûrement 0, pour toutes les valeurs de x. (C'est parce qu'il y a une infinité de valeurs possibles dans l'intervalle.) Si vous recherchez exactement 0 ou 1, vous devez utiliser une fonction différente - par exemplerandom.choice
pault
1
@pault a presque sûrement une signification très spécifique en mathématiques, ce qui n'a pas vraiment de sens ici car nous avons une distribution discrète et non un intervalle continu. Il n'y a qu'un nombre fini de flotteurs entre 0 et 1.
wim
2
@pault Alors pourquoi parlez-vous mathématiquement quand le PO vous pose des questions sur la mise en œuvre de random.uniform?
wim
1
Si cette documentation est exacte, je suis un peu curieux de savoir comment elle peut produire à la fois 0 et 1. Il semble que [0, 1) serait beaucoup plus facile (c'est ainsi que Math.random()fonctionne en JavaScript, par exemple).
Ry-
1
random.uniform(0, 1)
Prime de

Réponses:

13

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

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

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

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

Citant la random.random()documentation :

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 floattype de Python est une 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 random.random()produite est simplement la somme d'un 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 bpour certaines valeurs de aet b. Mais 0et 1ne font pas partie de ces valeurs.

Notez que random.random() peut produire 0,0, il aest donc toujours inclus dans les valeurs possibles pour random.uniform()( a + (b - a) * 0 == a). Parce qu'il existe 2 ** 53différentes valeurs qui random.random()peuvent produire (toutes les combinaisons possibles de ces 53 fractions binaires), il n'y a qu'une 2 ** 53chance sur 1 (donc 1 sur 9007199254740992) que cela se produise.

Donc, la valeur la plus élevée possible qui random.random()peut produire est 1 - (2 ** -53); il suffit de choisir une valeur suffisamment petite pour b - apermettre l'arrondi au coup d'envoi lorsqu'il est multiplié par des random.random()valeurs plus élevées. Plus la taille b - aest petite , 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 (en exécutant la fonction ci-dessus en 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 aet b, comme quand aet bont un exposant élevé et peuvent donc sembler éloignées. 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.maxet sys.float_info.max - (2 ** 970), il y a donc 50 à 50 chances de random.uniform(sys.float_info.max - (2 ** 970), sys.float_info.max)produire 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
Martijn Pieters
la source
5

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

Hobbs
la source
2
Car uniform(0, 1)il est impossible de produire 1comme résultat. C'est parce que la fonction est simplement définie comme def uniform(a, b): return a + (b - a) * random()et random()ne peut jamais produire 1.0.
Martijn Pieters
2
@MartijnPieters Je pense que vous avez raison et j'ai voté pour votre réponse. J'en soupçonnais autant, mais je n'étais pas sûr, et c'était en dehors de l'
idée
1

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 déclaré Hobbs, le montant des valeurs uniformlyéchantillonnées 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 de 10 quatrillions d'échantillons pour trouver un 0. Bien sûr, vous pourriez le trouver 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.

Celius Stingher
la source
1

Sûr. Vous étiez déjà sur la bonne voie en essayant à la uniform(0, 0.001)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
wim
la source