При исследовании для этого вопроса и чтении исходного кода в random.py
, Я начал задаваться вопросом ли randrange
и randint
действительно ведите себя, как "рекламируется". Я очень склонен верить так, но путь я считал его, randrange
по существу реализован как
start + int(random.random()*(stop-start))
(принимающий целочисленные значения для start
и stop
), таким образом, randrange(1, 10)
должен возвратить случайное число между 1 и 9.
randint(start, stop)
звонит randrange(start, stop+1)
, таким образом, возвращая число между 1 и 10.
Мой вопрос теперь:
Если random()
должны были когда-либо возвращаться 1.0
, затем randint(1,10)
возвратился бы 11
, не был бы он?
Из random.py
и документации:
"""Get the next random number in the range [0.0, 1.0)."""
The )
indicates that the interval is exclusive 1.0. То есть, он никогда не вернет 1.0.
Это общее соглашение в математике, [
и ]
является инклюзивным, а (
и )
- эксклюзивным, и эти два типа скобок можно смешивать как (a, b)
или [a, b)
. Загляните в Википедию: Интервал (математика) для формального объяснения.
В других ответах указано, что результат random ()
всегда строго меньше, чем 1.0
; однако это только половина дела.
Если вы вычисляете randrange (n)
как int (random () * n)
, вы также должны знать, что для любого Python с плавающей точкой x
, удовлетворяющий 0,0 <= x <1,0
, и любое положительное целое число n
, верно, что 0,0 <= x * n
int (x * n)
строго меньше, чем n
.
Здесь могут возникнуть две ошибки: во-первых, когда мы вычисляем x * n
, n
неявно преобразуется в число с плавающей запятой. Для достаточно большого n
это преобразование может изменить значение. Но если вы посмотрите на исходный код Python, вы увидите, что он использует метод int (random () * n)
только для n
меньше, чем 2 ** 53
(здесь и ниже я предполагаю, что платформа использует двойники IEEE 754), это диапазон, в котором при преобразовании n
в число с плавающей запятой гарантированно не будет потеряна информация (потому что n
можно представить в точности как число с плавающей запятой).
Второе, что может пойти не так, это то, что результат умножения x * n
(который сейчас выполняется как произведение чисел с плавающей запятой, помните), вероятно, не будет точно представлен, поэтому будет некоторое округление. Если x
достаточно близко к 1.0
, возможно, что округление округлит результат до n
.
Чтобы понять, что этого не может быть, нам нужно только рассмотреть максимально возможное значение для x
, которое (почти на всех машинах, на которых работает Python) 1-2 ** -53
. Итак, нам нужно показать, что (1-2 ** - 53) * n
n
, поскольку всегда будет верно, что random ( ) * n <= (1-2 ** - 53) * n
.
Доказательство (набросок) Пусть k
будет уникальным целым числом k
такое, что 2 ** (k-1)
n
будет n - 2 ** (k-53)
. Нам нужно показать, что n * (1-2 ** 53)
(т.е. фактическое, неокругленное, значение продукта) ближе к n - 2 ** (k-53)
, чем до n
, так что оно всегда будет округляться в меньшую сторону. Но небольшая арифметика показывает, что расстояние от n * (1-2 ** - 53)
до n
равно 2 ** - 53 * n
, а расстояние от n * (1-2 ** - 53)
до n - 2 ** (k-53)
равно (2 ** k - n) * 2 ** - 53
. Но 2 ** k - n
k
так, что 2 ** (k-1)
n - 2 ** (k-53)
, поэтому будет округляться в меньшую сторону (при условии, что платформа выполняет некоторую форму округления до ближайшего).
Итак, мы в безопасности.Уф!
Дополнение (04.07.2015): Вышеупомянутое предполагает арифметику двоичного 64-го стандарта IEEE 754 с режимом равномерного округления. На многих машинах это предположение довольно безопасно. Однако на машинах x86, которые используют FPU x87 для операций с плавающей запятой (например, различные варианты 32-разрядной версии Linux), существует возможность двойного округления при умножении, что делает возможным random () * n
для округления вверх до n
в случае, когда random ()
возвращает максимально возможное значение. Наименьший такой n
, для которого это может произойти, равен n = 2049
. См. Обсуждение на http://bugs.python.org/issue24546 для получения дополнительной информации.
Из документации Python:
Практически все функции модуля зависят от базовой функции random (), которая равномерно генерирует случайное число с плавающей запятой в полуоткрытом диапазоне [0.0, 1.0).
Как почти каждый ГПСЧ чисел с плавающей запятой ..