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 b
peut 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 1
exactement. Cela ne se produirait que si random.random()
1.0 exactly. However,
random () *never* produces
1.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 float
type 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 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, il a
est donc toujours inclus dans les valeurs possibles pour random.uniform()
( a + (b - a) * 0 == a
). Parce qu'il existe 2 ** 53
différentes valeurs qui random.random()
peuvent produire (toutes les combinaisons possibles de ces 53 fractions binaires), il n'y a qu'une 2 ** 53
chance 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 - a
permettre l'arrondi au coup d'envoi lorsqu'il est multiplié par des random.random()
valeurs plus élevées. Plus la taille b - a
est 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 a
et b
, comme quand a
et b
ont 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.max
et 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
X ~ U(0,1)
, alors leP(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