Может ли random.uniform (0,1) генерировать 0 или 1?


9

В документации сказано, что есть шанс, что uniform(0,1)можно сгенерировать значения 0и 1.

Я бегал uniform(0, 1)10000 раз, но он никогда не выдавал ноль. Даже в случае uniform(0, 0.001).

Может random.uniform(0,1)когда-нибудь генерировать 0или 1?


3
Это теоретически возможно, но практически никогда не произойдет. Математически, стандартная равномерная случайная величина может принимать любое значение в интервале от 0 до 1. Если X ~ U(0,1), то P(X=x)это почти наверняка 0, для всех значений х. (Это потому, что в интервале бесконечно много возможных значений.) Если вы ищете ровно 0 или 1, вам следует использовать другую функциюrandom.choice
pault

1
@pault почти наверняка имеет очень специфическое значение в математике, которое здесь не имеет смысла, поскольку у нас есть дискретное распределение, а не непрерывный интервал. Между 0 и 1 существует только конечное число операций с плавающей точкой
wim

2
@pault Так почему вы говорите математически, когда ОП спрашивает о реализации random.uniform?
Вим

1
Если эта документация точна, мне любопытно, как она может генерировать и 0, и 1. Похоже, что [0, 1) будет намного проще (например, как это Math.random()работает в JavaScript).
Ry-

1
50 очков за первое лицо, которое отправит случайное начальное число, которое random.uniform(0, 1)возвращает 0 при первом вызове
wim

Ответы:


13

uniform(0, 1)может производить 0, но никогда не даст 1.

Документация говорит вам , что конечная точка b может быть включена в значениях , полученных:

Значение конечной точки bможет или не может быть включено в диапазон в зависимости от округления с плавающей точкой в ​​уравнении a + (b-a) * random().

Таким образом uniform(0, 1), формула 0 + (1-0) * random(), упрощенная до 1 * random(), должна была бы быть способной производить 1точно. Это произойдет, только если random.random()1.0 exactly. However,random () *never* produces1.0`.

Цитирование random.random()документации :

Вернуть следующее случайное число с плавающей запятой в диапазоне [0.0, 1.0).

Обозначение [..., ...)означает, что первое значение является частью всех возможных значений, а второе - нет. random.random()будет в большинстве случаев производить значения очень близко к 1.0. floatТип Python - это значение с плавающей точкой IEEE 754 base64 , которое кодирует число двоичных дробей (1/2, 1/4, 1/5 и т. Д.), Составляющих значение, а полученное значение random.random()представляет собой просто сумму случайный выбор этих 53 таких фракций от 2 ** -1(1/2) до 2 ** -53(1/9007199254740992).

Однако, поскольку он может давать очень близкие значения 1.0, а также ошибки округления, возникающие при умножении чисел с плавающей запятой, вы можете получить bдля некоторых значений aи b. Но 0и 1не входят в число этих ценностей.

Обратите внимание, что random.random() может выдавать 0.0, поэтому aвсегда включается в возможные значения для random.uniform()( a + (b - a) * 0 == a). Поскольку существуют 2 ** 53различные значения, которые random.random()могут быть получены (все возможные комбинации этих 53 двоичных дробей), существует только 1 2 ** 53(так что 1 в 9007199254740992) вероятность того, что это когда-либо произойдет.

Итак, максимально возможная ценность, которую random.random()можно произвести, это 1 - (2 ** -53); просто выберите достаточно маленькое значение для того, b - aчтобы округление включалось при умножении на более высокие random.random()значения. Чем меньше b - a, тем больше шансов на это:

>>> 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

Если вы нажмете b = 0.0, то мы делим 1023 раза, указанное выше значение означает, что нам повезло после 1019 делений. Наибольшее значение, которое я нашел до сих пор (выполнение вышеуказанной функции в цикле с max()), составляет 8.095e-320(1008 делений), но, вероятно, существуют более высокие значения. Это все азартная игра. :-)

Это также может произойти, если между aи не слишком много дискретных шагов b, например, когда aи bимеют высокий показатель степени, и поэтому может показаться, что они далеко впереди. Значения с плавающей запятой по-прежнему являются только приблизительными, и число значений, которые они могут кодировать, конечно. Например, существует только одна двоичная дробь разница между sys.float_info.maxи sys.float_info.max - (2 ** 970), таким образом есть шанс , 50-50 random.uniform(sys.float_info.max - (2 ** 970), sys.float_info.max)производит 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

5

«Несколько раз» недостаточно. 10000 не достаточно. random.uniformвыбирает из 2 ^ 53 (9,007,199,254,740,992) различных значений. Вы заинтересованы в двух из них. Таким образом, вы должны ожидать генерирования нескольких квадриллионов случайных значений, прежде чем получите значение, которое в точности равно 0 или 1. Таким образом, это возможно, но очень вероятно, что вы никогда не будете наблюдать это.


2
Для uniform(0, 1)это невозможно производить 1как результат. Это потому, что функция просто определяется как def uniform(a, b): return a + (b - a) * random()и random()никогда не может производить 1.0.
Мартин Питерс

2
@ MartijnPieters Я верю, что вы правы, и я проголосовал за ваш ответ. Я так и подозревал, но я не был уверен, и это было в стороне от основного
толка

1

Вы можете попытаться сгенерировать цикл, который подсчитывает количество итераций, необходимое для точного отображения 0 (не).

Кроме того, как заявил Хоббс, количество uniformlyвыборочных значений составляет 9 007 199 254 740 992. Это означает, что вероятность увидеть 0 в точности равна 1 / 9,007,199,254,740,992. Что в общих чертах и ​​округлении означает, что вам понадобится в среднем 10 кватриллионов образцов, чтобы найти 0. Конечно, вы можете найти его в своих первых 10 попытках или никогда.

Выборка 1 невозможна, так как интервал, определенный для значений, закрыт круглыми скобками, следовательно, не включая 1.


1

Конечно. Вы уже были на правильном пути, пытаясь uniform(0, 0.001)вместо этого. Просто продолжайте ограничивать границы достаточно, чтобы это произошло раньше.

>>> random.uniform(0., 5e-324)
5e-324
>>> random.uniform(0., 5e-324)
5e-324
>>> random.uniform(0., 5e-324)
0.0
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.