По умолчанию функция 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)
.