Может ли random.randint (1,10) когда-нибудь вернуть 11? - PullRequest
10 голосов
/ 14 июня 2010

При поиске этого вопроса и чтении исходного кода в 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, не так ли?

Ответы [ 3 ]

27 голосов
/ 14 июня 2010

От random.py и документов:

"""Get the next random number in the range [0.0, 1.0)."""

) указывает, что интервал исключает 1.0. То есть он никогда не вернет 1.0.

Это общее соглашение в математике, [ и ] включительно, в то время как ( и ) являются исключительными, и два типа скобок можно смешать как (a, b] или [a, b). Посмотрите wikipedia: Interval (математика) для формального объяснения.

12 голосов
/ 15 июня 2010

В других ответах указывалось, что результат random() всегда строго меньше 1.0;однако, это только половина истории.

Если вы вычисляете randrange(n) как int(random() * n), вам также нужно знать, что для любого числа с плавающей точкой Python x, удовлетворяющего 0.0 <= x < 1.0и любое положительное целое число n, это правда, что 0.0 <= x * n < 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 для нашего положительного целого числа n, поскольку всегда будет верно, что random() * n <= (1 - 2**-53) * n.

Доказательство (эскиз) Пусть k будетуникальное целое число k такое, что 2**(k-1) < n <= 2**k.Тогда следующий поплавок вниз от 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 < n (потому что мы выбрали k, чтобы 2**(k-1) < n), так что произведение на ближе к n - 2**(k-53), так что будет округляться (при условии, чтов том, что платформа выполняет ту или иную форму округления до ближайшего).

Итак, мы в безопасности.Фу!


Приложение (2015-07-04): В приведенном выше примере используется арифметика IEEE 754 для двоичных чисел 64, с режимом округления числа к четному.На многих машинах это предположение довольно безопасно.Однако на машинах x86, использующих FPU x87 для чисел с плавающей запятой (например, различные разновидности 32-битного Linux), существует вероятность двойного округления при умножении, что делает возможным *От 1073 * до до до n в случае, когда random() возвращает максимально возможное значение.Наименьшее значение n, для которого это может произойти, - n = 2049.См. Обсуждение в http://bugs.python.org/issue24546 для получения дополнительной информации.

3 голосов
/ 14 июня 2010

Из документации Python:

Почти все функции модуля зависят от базовой функции random (), которая генерирует случайное число с плавающей точкой в ​​полуоткрытом диапазоне [0.0, 1.0).

Как почти каждый PRNG чисел с плавающей запятой ..

...