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* produces
1,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)
существует только 1 двоичная дробь, поэтому есть вероятность 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