¿Puede random.uniform (0,1) generar 0 o 1?

9

En la documentación se dice que existe la posibilidad de que uniform(0,1)pueda generar los valores 0y 1.

He corrido uniform(0, 1)10000 veces, pero nunca produjo cero. Incluso en el caso de uniform(0, 0.001).

random.uniform(0,1)Alguna vez puede generar 0o 1?

Venkatesh Gandi
fuente
3
Teóricamente es posible, pero prácticamente nunca sucederá. Matemáticamente, una variable aleatoria uniforme estándar puede tomar cualquier valor en el intervalo de 0 a 1. si X ~ U(0,1), entonces P(X=x)es casi seguro 0, para todos los valores de x. (Esto se debe a que hay infinitos valores posibles en el intervalo). Si está buscando exactamente 0 o 1, debe usar una función diferente, por ejemplorandom.choice
pausar el
1
@pault casi seguramente tiene un significado muy específico en matemáticas, lo que realmente no tiene sentido aquí ya que tenemos una distribución discreta, no un intervalo continuo. Solo hay un número finito de flotadores entre 0 y 1.
wim
2
@pault Entonces, ¿por qué estás hablando matemáticamente cuando el OP pregunta por la implementación de random.uniform?
wim
1
Si la documentación que sea precisa, estoy un poco curioso en cuanto a cómo se hace a la posibilidad de producir tanto 0 y 1. Parece que [0, 1) sería mucho más fácil (que es como Math.random()funciona en JavaScript, por ejemplo).
Ry-
1
random.uniform(0, 1)
Recompensa de

Respuestas:

13

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 del punto final bpuede o no estar incluido en el rango dependiendo del redondeo de punto flotante en la ecuación a + (b-a) * random().

Entonces uniform(0, 1), para , la fórmula 0 + (1-0) * random(), simplificada a 1 * random(), tendría que ser capaz de producir 1exactamente. Eso solo sucedería si random.random()es 1.0 exactly. However,random () *never* produces1.0`.

Citando la random.random()documentación :

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 lo es. random.random()como máximo producirá valores muy cercanos a 1.0. El floattipo 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 que random.random()produce es simplemente la suma de un selección aleatoria de esas 53 fracciones de este tipo 2 ** -1( desde 1/2) hasta 2 ** -53(1/9007199254740992).

Sin embargo, debido a que puede producir valores muy cerca 1.0, junto con los errores de redondeo que se producen cuando se multiplican flotante nubmers de puntos, se puede producir bpara algunos valores de ay b. Pero 0y 1no están entre esos valores.

Tenga en cuenta que random.random() puede producir 0.0, por alo que siempre se incluye en los valores posibles para random.uniform()( a + (b - a) * 0 == a). Debido a que existen 2 ** 53diferentes valores que random.random()pueden producir (todas las combinaciones posibles de esas 53 fracciones binarias), solo hay una 2 ** 53posibilidad de 1 pulg.

Entonces el valor más alto posible que random.random()puede producir es 1 - (2 ** -53); simplemente elija un valor lo suficientemente pequeño como para b - apermitir que se redondee cuando se multiplica por random.random()valores más altos. Cuanto más pequeño b - aes, 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 aciertas 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 (ejecutando 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 ay b, como cuando ay btienen 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.maxy sys.float_info.max - (2 ** 970), por lo que hay una probabilidad de 50-50 que 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
Martijn Pieters
fuente
5

"Varias veces" no es suficiente. 10,000 no es suficiente. random.uniformelige 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.

hobbs
fuente
2
Porque uniform(0, 1)es imposible producir 1como resultado. Esto se debe a que la función simplemente se define como def uniform(a, b): return a + (b - a) * random()y random()nunca puede producir 1.0.
Martijn Pieters
2
@MartijnPieters Creo que tienes razón, y he votado tu respuesta. Sospeché eso, pero no estaba seguro, y fue aparte del objetivo principal de mi respuesta, así que lo dejé ser :)
hobbs
1

Puede intentar generar un ciclo 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 están uniformlymuestreando 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 1 es imposible ya que el intervalo definido para los valores se cierra con un paréntesis, por lo tanto, no incluye 1.

Celius Stingher
fuente
1

Por supuesto. Ya estabas en el camino correcto al intentarlo uniform(0, 0.001). 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
wim
fuente