python - ¿Puede random.uniform(0,1) generar 0 o 1?
uniform-distribution (4)
"Varias veces" no es suficiente.
10,000 no es suficiente.
random.uniform
elige entre 2 ^ 53 (9,007,199,254,740,992) valores diferentes.
Estás interesado en
dos
de ellos.
Como tal, debe esperar generar varios
billones de
valores aleatorios antes de obtener un valor que sea exactamente 0 o 1. Por lo tanto, es posible, pero es muy probable que nunca lo observe.
En
la documentación
se dice que existe la posibilidad de que
uniform(0,1)
pueda generar los valores
0
y
1
.
He corrido
uniform(0, 1)
10000 veces, pero nunca produjo cero.
Incluso en el caso del
uniform(0, 0.001)
.
¿Puede
random.uniform(0,1)
generar
0
o
1
?
Puede intentar generar un bucle que cuente la cantidad de iteraciones necesarias para que se muestre un 0 exacto (no).
Además, como dijo Hobbs, la cantidad de valores que se muestrea de manera
uniformly
son 9,007,199,254,740,992.
Lo que significa que la probabilidad de ver un 0 es exactamente 1 / 9,007,199,254,740,992.
Lo que en términos generales y redondeando significa que necesitará en
promedio
10 billones de muestras para encontrar un 0. Por supuesto, puede encontrarlo en sus primeros 10 intentos, o nunca.
El muestreo de un 1 es imposible ya que el intervalo definido para los valores se cierra con un paréntesis, por lo tanto, no incluye 1.
Seguro.
Ya estabas en el camino correcto con probar
uniform(0, 0.001)
lugar.
Solo sigue restringiendo los límites lo suficiente como para que suceda antes.
>>> random.uniform(0., 5e-324)
5e-324
>>> random.uniform(0., 5e-324)
5e-324
>>> random.uniform(0., 5e-324)
0.0
uniform(0, 1)
puede producir
0
, pero
nunca
producirá
1
.
La
documentación
le dice que el punto final
b
podría
incluirse en los valores producidos:
El valor de punto final
b
puede o no incluirse en el rango dependiendo del redondeo de punto flotante en la ecuacióna + (ba) * random()
.
Entonces, para el
uniform(0, 1)
, la fórmula
0 + (1-0) * random()
, simplificada a
1 * random()
, debería ser capaz de producir
1
exactamente.
Eso solo sucedería si
random.random()
es 1.0
exactly. However,
exactly. However,
random ()
*never* produces
1.0`.
Citando la
documentación
random.random()
:
Devuelve el siguiente número aleatorio de coma flotante en el rango [0.0, 1.0).
La notación
[..., ...)
significa que el primer valor es parte de todos los valores posibles, pero el segundo no.
random.random()
producirá como máximo valores
muy cercanos
a
1.0
.
El tipo
float
de Python es un
valor de coma flotante base64 IEEE 754
, que codifica varias
fracciones binarias
(1/2, 1/4, 1/5, etc.) que conforman el valor, y el valor
random.random()
produce es simplemente la suma de una selección aleatoria de esas 53 fracciones, desde
2 ** -1
(1/2) hasta
2 ** -53
(1/9007199254740992).
Sin embargo, debido a que puede producir valores muy cercanos a
1.0
, junto con los errores de redondeo que ocurren cuando multiplica los nubmers de punto flotante,
puede
producir
b
para
algunos
valores de
a
y
b
.
Pero
0
y
1
no están entre esos valores.
Tenga en cuenta que
random.random()
puede
producir 0.0, por lo que
a
siempre se incluye en los valores posibles para
random.uniform()
(
a + (b - a) * 0 == a
).
Debido a que hay
2 ** 53
valores diferentes que
random.random()
puede producir (todas las combinaciones posibles de esas 53 fracciones binarias), solo existe una probabilidad de 1 en
2 ** 53
(por lo tanto, 1 en 9007199254740992) de que eso ocurra.
Entonces, el valor más alto posible que
random.random()
puede producir es
1 - (2 ** -53)
;
simplemente elija un valor lo suficientemente pequeño para
b - a
para permitir que el redondeo se
random.random()
cuando se multiplica por valores
random.random()
más altos.
Cuanto más pequeño es
b - a
, mayores son las posibilidades de que eso suceda:
>>> 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 tocas
b = 0.0
, entonces hemos dividido 1023 veces, el valor anterior significa que tuvimos suerte después de 1019 divisiones.
El valor más alto que encontré hasta ahora (ejecutar la función anterior en un bucle con
max()
) es
8.095e-320
(1008 divisiones), pero probablemente haya valores más altos.
Todo es un juego de azar.
:-)
También puede suceder si no hay muchos pasos discretos entre
a
y
b
, como cuando
a
y
b
tienen un alto exponente y, por lo tanto, puede parecer muy diferente.
Los valores de coma flotante siguen siendo solo aproximaciones, y el número de valores que pueden codificar es finito.
Por ejemplo, solo hay 1 fracción binaria de diferencia entre
sys.float_info.max
y
sys.float_info.max - (2 ** 970)
, por lo que hay un 50-50 chance
random.uniform(sys.float_info.max - (2 ** 970), sys.float_info.max)
produce
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