По умолчанию функция random.random()
возвращает числа, кратные 2-53, в диапазоне 0.0 ≤ x < 1.0
. Все такие числа расположены через равные промежутки и могут быть точно представлены как числа Python с плавающей запятой. Однако многие другие представимые числа float
в этом интервале не могут быть выбраны. Например 0,05954861408025609
не является целым числом, кратным 2-53.
В следующем рецепте применения модуля random
используется другой подход. Возможны все вещественные числа в интервале. Мантисса исходит из равномерного распределения целых чисел в диапазоне 2-53 ≤ mantissa
< 2-53. Показатель степени получается из геометрического распределения, в котором показатели меньше -53 встречаются вдвое реже, чем следующая большая экспонента.
from random import Random
from math import ldexp
class FullRandom(Random):
def random(self):
mantissa = 0x10_0000_0000_0000 | self.getrandbits(52)
exponent = -53
x = 0
while not x:
x = self.getrandbits(32)
exponent += x.bit_length() - 32
return ldexp(mantissa, exponent)
Все вещественные распределения в классе будут использовать новый метод:
>>> fr = FullRandom()
>>> fr.random()
# 0.05954861408025609
>>> fr.expovariate(0.25)
# 8.87925541791544
Этот рецепт концептуально эквивалентен алгоритму, который выбирает из всех кратных 2-1074 в диапазоне 0.0 ≤ x < 1.0
. Все такие числа равномерно распределены, но большинство из них необходимо округлить до ближайшего представимого числа с плавающей запятой Python. Значение 2-1074 является наименьшим положительным ненормализованным числом с плавающей запятой и равно math.ulp(0.0)
.