random — Генерирование псевдослучайных чисел

Исходный код: Lib/random.py.


Этот модуль реализует генераторы псевдослучайных чисел для различных дистрибутивов.

Для целых чисел - равномерный выбор из диапазона. Для последовательностей есть равномерный выбор случайного элемента, функция для генерации случайной перестановки списка на месте и функция для случайной выборки без замены.

На вещественной прямой имеются функции для вычисления равномерного, нормального (гауссова), логнормального, отрицательного экспоненциального, гамма- и бета-распределения. Для генерации распределений углов доступно распределение фон Мизеса.

Почти все функции модуля зависят от базовой функции random(), которая генерирует случайное плавающее число равномерно в полуоткрытом диапазоне [0.0, 1.0). В качестве базового генератора в Python используется твистер Мерсенна. Он производит 53-битные плавающие числа точности и имеет период 2**19937-1. Базовая реализация на языке C является быстрой и потокобезопасной. Mersenne Twister - один из наиболее интенсивно тестируемых генераторов случайных чисел. Однако, будучи полностью детерминированным, он не подходит для всех целей, и совершенно не подходит для криптографических целей.

Функции, предоставляемые этим модулем, на самом деле являются связанными методами скрытого экземпляра класса random.Random. Вы можете создавать свои собственные экземпляры Random для получения генераторов, не разделяющих состояние.

Класс Random также может быть подклассом, если вы хотите использовать другой базовый генератор собственного изобретения: в этом случае переопределите методы random(), seed(), getstate() и setstate(). Как вариант, новый генератор может предоставить метод getrandbits() - это позволяет randrange() производить выборки в произвольно большом диапазоне.

Модуль random также предоставляет класс SystemRandom, который использует системную функцию os.urandom() для генерации случайных чисел из источников, предоставляемых операционной системой.

Предупреждение

Псевдослучайные генераторы этого модуля не следует использовать в целях безопасности. Для использования в целях безопасности или криптографии смотрите модуль secrets.

См.также

M. Мацумото и Т. Нишимура, «Mersenne Twister: 623-мерный равнораспределенный равномерный генератор псевдослучайных чисел», ACM Transactions on Modeling and Computer Simulation Vol. 8, No. 1, January pp.3–30 1998.

Complementary-Multiply-with-Carry recipe для совместимого альтернативного генератора случайных чисел с большим периодом и сравнительно простыми операциями обновления.

Бухгалтерские функции

random.seed(a=None, version=2)

Инициализация генератора случайных чисел.

Если a опущено или None, используется текущее системное время. Если источники случайности предоставляются операционной системой, они используются вместо системного времени (подробнее о доступности см. в функции os.urandom()).

Если a - это int, то оно используется напрямую.

В версии 2 (по умолчанию) объект str, bytes или bytearray преобразуется в int и используются все его биты.

В версии 1 (предоставляемой для воспроизведения случайных последовательностей из старых версий Python) алгоритм для str и bytes генерирует более узкий диапазон семян.

Изменено в версии 3.2: Перешли на схему версии 2, которая использует все биты в строковом семени.

Не рекомендуется, начиная с версии 3.9: В будущем семя должно быть одним из следующих типов: NoneType, int, float, str, bytes или bytearray.

random.getstate()

Возвращает объект, фиксирующий текущее внутреннее состояние генератора. Этот объект может быть передан в setstate() для восстановления состояния.

random.setstate(state)

state должно было быть получено из предыдущего вызова getstate(), а setstate() восстанавливает внутреннее состояние генератора до того, каким оно было на момент вызова getstate().

Функции для байтов

random.randbytes(n)

Сгенерируйте n случайных байтов.

Этот метод не следует использовать для генерации маркеров безопасности. Вместо этого используйте secrets.token_bytes().

Добавлено в версии 3.9.

Функции для целых чисел

random.randrange(stop)
random.randrange(start, stop[, step])

Возвращает случайно выбранный элемент из range(start, stop, step). Это эквивалентно choice(range(start, stop, step)), но на самом деле не строит объект диапазона.

Шаблон позиционного аргумента соответствует шаблону range(). Аргументы с ключевыми словами не следует использовать, поскольку функция может использовать их неожиданным образом.

Изменено в версии 3.2: randrange() является более сложным для получения равномерно распределенных значений. Раньше использовался стиль int(random()*n), который мог давать немного неравномерное распределение.

Не рекомендуется, начиная с версии 3.10: Автоматическое преобразование нецелых типов в эквивалентные целые числа устарело. В настоящее время randrange(10.0) без потерь преобразуется в randrange(10). В будущем это будет вызывать ошибку TypeError.

Не рекомендуется, начиная с версии 3.10: Исключение, возникающее для неинтегральных значений, таких как randrange(10.5) или randrange('10'), будет изменено с ValueError на TypeError.

random.randint(a, b)

Возвращает случайное целое число N такое, что a <= N <= b. Псевдоним для randrange(a, b+1).

random.getrandbits(k)

Возвращает неотрицательное целое число Python с k случайными битами. Этот метод поставляется с генератором MersenneTwister, и некоторые другие генераторы могут также предоставлять его в качестве дополнительной части API. Когда доступно, getrandbits() позволяет randrange() обрабатывать произвольно большие диапазоны.

Изменено в версии 3.9: Теперь этот метод принимает ноль для k.

Функции для последовательностей

random.choice(seq)

Возвращает случайный элемент из непустой последовательности seq. Если seq пуст, то выдает IndexError.

random.choices(population, weights=None, *, cum_weights=None, k=1)

Возвращает список размером k элементов, выбранных из населения с заменой. Если популяция пуста, выдает IndexError.

Если указана последовательность weights, выборка производится в соответствии с относительными весами. В качестве альтернативы, если задана последовательность cum_weights, то отбор производится в соответствии с суммарными весами (возможно, вычисленными с помощью itertools.accumulate()). Например, относительные веса [10, 5, 30, 5] эквивалентны кумулятивным весам [10, 15, 45, 50]. Внутри программы относительные веса преобразуются в кумулятивные перед выполнением выбора, поэтому предоставление кумулятивных весов экономит работу.

Если не указаны ни weights, ни cum_weights, выборки производятся с равной вероятностью. Если указана последовательность весов, она должна быть той же длины, что и последовательность популяции. Указать и весы, и сумму_весов - это TypeError.

В weights или cum_weights может использоваться любой числовой тип, который взаимодействует со значениями float, возвращаемыми random() (сюда входят целые, плавающие и дробные числа, но не входят десятичные). Предполагается, что веса неотрицательны и конечны. Если все веса равны нулю, выдается сообщение ValueError.

Для заданного семени функция choices() с равным весом обычно выдает другую последовательность, чем повторные вызовы choice(). Алгоритм, используемый choices(), использует арифметику с плавающей запятой для внутренней согласованности и скорости. Алгоритм, используемый choice(), по умолчанию использует целочисленную арифметику с повторными выборами, чтобы избежать небольших смещений из-за ошибки округления.

Добавлено в версии 3.6.

Изменено в версии 3.9: Вызывает ошибку ValueError, если все веса равны нулю.

random.shuffle(x[, random])

Перемешайте последовательность x на месте.

Необязательный аргумент random - это 0-аргументная функция, возвращающая случайное число в [0.0, 1.0]; по умолчанию это функция random().

Чтобы перетасовать неизменяемую последовательность и вернуть новый перетасованный список, используйте sample(x, k=len(x)) вместо этого.

Обратите внимание, что даже при малых len(x) общее число перестановок x может быстро стать больше, чем период большинства генераторов случайных чисел. Это означает, что большинство перестановок длинной последовательности никогда не может быть сгенерировано. Например, последовательность длиной 2080 - самая большая, которая может уложиться в период генератора случайных чисел Mersenne Twister.

Deprecated since version 3.9, will be removed in version 3.11: Необязательный параметр random.

random.sample(population, k, *, counts=None)

Возвращает список длиной k уникальных элементов, выбранных из последовательности или множества популяции. Используется для случайной выборки без замены.

Возвращает новый список, содержащий элементы из совокупности, оставляя исходную совокупность неизменной. Полученный список располагается в порядке выбора, так что все подвыборки также будут действительными случайными выборками. Это позволяет разделить победителей лотереи (выборку) на обладателей главного приза и второго места (подвыборки).

Члены популяции не обязательно должны быть hashable или уникальными. Если совокупность содержит повторы, то каждое повторение является возможным отбором в выборку.

Повторяющиеся элементы могут быть указаны по одному или с помощью необязательного параметра counts, содержащего только ключевое слово. Например, sample(['red', 'blue'], counts=[4, 2], k=5) эквивалентно sample(['red', 'red', 'red', 'red', 'blue', 'blue'], k=5).

Чтобы выбрать выборку из диапазона целых чисел, используйте в качестве аргумента объект range(). Это особенно быстро и экономит место при выборке из большой популяции: sample(range(10000000), k=60).

Если размер выборки больше размера популяции, выдается сообщение ValueError.

Изменено в версии 3.9: Добавлен параметр counts.

Не рекомендуется, начиная с версии 3.9: В будущем население должно быть последовательностью. Экземпляры set больше не поддерживаются. Сначала набор должен быть преобразован в list или tuple, желательно в детерминированном порядке, чтобы выборка была воспроизводимой.

Распределения с реальными значениями

Следующие функции генерируют определенные распределения вещественных величин. Параметры функций названы по соответствующим переменным в уравнении распределения, используемом в обычной математической практике; большинство этих уравнений можно найти в любом тексте по статистике.

random.random()

Возвращает следующее случайное число с плавающей точкой в диапазоне [0.0, 1.0].

random.uniform(a, b)

Возвращает случайное число с плавающей точкой N такое, что a <= N <= b для a <= b и b <= N <= a для b < a.

Значение конечной точки b может быть включено или не включено в диапазон в зависимости от округления с плавающей точкой в уравнении a + (b-a) * random().

random.triangular(low, high, mode)

Возвращает случайное число с плавающей точкой N такое, что low <= N <= high и с заданным режимом между этими границами. Границы low и high по умолчанию равны нулю и единице. Аргумент mode по умолчанию равен средней точке между границами, что дает симметричное распределение.

random.betavariate(alpha, beta)

Бета-распределение. Условия на параметры - alpha > 0 и beta > 0. Возвращаемые значения лежат в диапазоне от 0 до 1.

random.expovariate(lambd)

Экспоненциальное распределение. lambd - это 1,0, деленное на желаемое среднее значение. Оно должно быть ненулевым. (Параметр можно было бы назвать «лямбда», но это зарезервированное слово в Python). Возвращаемые значения варьируются от 0 до положительной бесконечности, если lambd положительно, и от отрицательной бесконечности до 0, если lambd отрицательно.

random.gammavariate(alpha, beta)

Гамма-распределение. (Не гамма-функция!) Условия на параметры - alpha > 0 и beta > 0.

Функция распределения вероятностей имеет вид:

          x ** (alpha - 1) * math.exp(-x / beta)
pdf(x) =  --------------------------------------
            math.gamma(alpha) * beta ** alpha
random.gauss(mu, sigma)

Нормальное распределение, также называемое гауссовым распределением. mu - среднее значение, а sigma - стандартное отклонение. Это немного быстрее, чем функция normalvariate(), определенная ниже.

Примечание по многопоточности: Когда два потока одновременно вызывают эту функцию, возможно, что они получат одно и то же возвращаемое значение. Этого можно избежать тремя способами. 1) Пусть каждый поток использует свой экземпляр генератора случайных чисел. 2) Наложить блокировки на все вызовы. 3) Использовать более медленную, но безопасную для потоков функцию normalvariate().

random.lognormvariate(mu, sigma)

Логарифмически нормальное распределение. Если взять натуральный логарифм этого распределения, то получится нормальное распределение со средним mu и стандартным отклонением sigma. mu может иметь любое значение, а sigma должна быть больше нуля.

random.normalvariate(mu, sigma)

Нормальное распределение. mu - среднее значение, а sigma - стандартное отклонение.

random.vonmisesvariate(mu, kappa)

mu - средний угол, выраженный в радианах в диапазоне от 0 до 2*pi, а kappa - параметр концентрации, который должен быть больше или равен нулю. Если kappa равна нулю, это распределение сводится к равномерному случайному углу в диапазоне от 0 до 2*pi.

random.paretovariate(alpha)

Распределение Парето. alpha - это параметр формы.

random.weibullvariate(alpha, beta)

Распределение Вейбулла. альфа - параметр масштаба, а бета - параметр формы.

Альтернативный генератор

class random.Random([seed])

Класс, реализующий генератор псевдослучайных чисел по умолчанию, используемый модулем random.

Не рекомендуется, начиная с версии 3.9: В будущем семя должно быть одного из следующих типов: NoneType, int, float, str, bytes или bytearray.

class random.SystemRandom([seed])

Класс, использующий функцию os.urandom() для генерации случайных чисел из источников, предоставляемых операционной системой. Доступен не во всех системах. Не полагается на состояние программного обеспечения, и последовательности не воспроизводятся. Соответственно, метод seed() не имеет эффекта и игнорируется. Методы getstate() и setstate() при вызове вызывают ошибку NotImplementedError.

Заметки о воспроизводимости

Иногда полезно иметь возможность воспроизводить последовательности, выдаваемые генератором псевдослучайных чисел. Повторное использование значения затравки позволяет воспроизводить одну и ту же последовательность от запуска к запуску, пока не запущено несколько потоков.

Большинство алгоритмов и функций посева модуля random подвержены изменениям в разных версиях Python, но два аспекта гарантированно не изменятся:

  • Если добавляется новый метод посева, то будет предложена сеялка с обратной совместимостью.

  • Метод генератора random() будет продолжать производить ту же самую последовательность, когда совместимому сеятелю дается тот же самый посевной материал.

Примеры

Основные примеры:

>>> random()                             # Random float:  0.0 <= x < 1.0
0.37444887175646646

>>> uniform(2.5, 10.0)                   # Random float:  2.5 <= x <= 10.0
3.1800146073117523

>>> expovariate(1 / 5)                   # Interval between arrivals averaging 5 seconds
5.148957571865031

>>> randrange(10)                        # Integer from 0 to 9 inclusive
7

>>> randrange(0, 101, 2)                 # Even integer from 0 to 100 inclusive
26

>>> choice(['win', 'lose', 'draw'])      # Single random element from a sequence
'draw'

>>> deck = 'ace two three four'.split()
>>> shuffle(deck)                        # Shuffle a list
>>> deck
['four', 'two', 'ace', 'three']

>>> sample([10, 20, 30, 40, 50], k=4)    # Four samples without replacement
[40, 10, 50, 30]

Симуляции:

>>> # Six roulette wheel spins (weighted sampling with replacement)
>>> choices(['red', 'black', 'green'], [18, 18, 2], k=6)
['red', 'green', 'black', 'black', 'red', 'black']

>>> # Deal 20 cards without replacement from a deck
>>> # of 52 playing cards, and determine the proportion of cards
>>> # with a ten-value:  ten, jack, queen, or king.
>>> dealt = sample(['tens', 'low cards'], counts=[16, 36], k=20)
>>> dealt.count('tens') / 20
0.15

>>> # Estimate the probability of getting 5 or more heads from 7 spins
>>> # of a biased coin that settles on heads 60% of the time.
>>> def trial():
...     return choices('HT', cum_weights=(0.60, 1.00), k=7).count('H') >= 5
...
>>> sum(trial() for i in range(10_000)) / 10_000
0.4169

>>> # Probability of the median of 5 samples being in middle two quartiles
>>> def trial():
...     return 2_500 <= sorted(choices(range(10_000), k=5))[2] < 7_500
...
>>> sum(trial() for i in range(10_000)) / 10_000
0.7958

Пример statistical bootstrapping использования выборки с заменой для оценки доверительного интервала для среднего значения выборки:

# https://www.thoughtco.com/example-of-bootstrapping-3126155
from statistics import fmean as mean
from random import choices

data = [41, 50, 29, 37, 81, 30, 73, 63, 20, 35, 68, 22, 60, 31, 95]
means = sorted(mean(choices(data, k=len(data))) for i in range(100))
print(f'The sample mean of {mean(data):.1f} has a 90% confidence '
      f'interval from {means[5]:.1f} to {means[94]:.1f}')

Пример resampling permutation test для определения статистической значимости или p-value наблюдаемой разницы между эффектами лекарства и плацебо:

# Example from "Statistics is Easy" by Dennis Shasha and Manda Wilson
from statistics import fmean as mean
from random import shuffle

drug = [54, 73, 53, 70, 73, 68, 52, 65, 65]
placebo = [54, 51, 58, 44, 55, 52, 42, 47, 58, 46]
observed_diff = mean(drug) - mean(placebo)

n = 10_000
count = 0
combined = drug + placebo
for i in range(n):
    shuffle(combined)
    new_diff = mean(combined[:len(drug)]) - mean(combined[len(drug):])
    count += (new_diff >= observed_diff)

print(f'{n} label reshufflings produced only {count} instances with a difference')
print(f'at least as extreme as the observed difference of {observed_diff:.1f}.')
print(f'The one-sided p-value of {count / n:.4f} leads us to reject the null')
print(f'hypothesis that there is no difference between the drug and the placebo.')

Моделирование времени прибытия и доставки услуг для многосерверной очереди:

from heapq import heapify, heapreplace
from random import expovariate, gauss
from statistics import mean, quantiles

average_arrival_interval = 5.6
average_service_time = 15.0
stdev_service_time = 3.5
num_servers = 3

waits = []
arrival_time = 0.0
servers = [0.0] * num_servers  # time when each server becomes available
heapify(servers)
for i in range(1_000_000):
    arrival_time += expovariate(1.0 / average_arrival_interval)
    next_server_available = servers[0]
    wait = max(0.0, next_server_available - arrival_time)
    waits.append(wait)
    service_duration = max(0.0, gauss(average_service_time, stdev_service_time))
    service_completed = arrival_time + wait + service_duration
    heapreplace(servers, service_completed)

print(f'Mean wait: {mean(waits):.1f}   Max wait: {max(waits):.1f}')
print('Quartiles:', [round(q, 1) for q in quantiles(waits)])

См.также

Statistics for Hackers видеоурок Jake Vanderplas по статистическому анализу с использованием всего нескольких фундаментальных понятий, включая моделирование, выборку, перемешивание и перекрестную валидацию.

Economics Simulation имитация рынка по Peter Norvig, которая показывает эффективное использование многих инструментов и распределений, предоставляемых этим модулем (гауссово, равномерное, выборочное, бетавариативное, выбор, треугольное и ранжированное).

A Concrete Introduction to Probability (using Python) учебник от Peter Norvig, охватывающий основы теории вероятности, как писать симуляции и как выполнять анализ данных с помощью Python.

Рецепты

По умолчанию random() возвращает кратные 2-⁵³ в диапазоне 0.0 ≤ x < 1.0. Все такие числа равномерно распределены и точно представимы в виде плавающих чисел Python. Однако многие другие представимые плавающие числа в этом интервале не могут быть выбраны. Например, 0.05954861408025609 не является целым числом, кратным 2-⁵³.

В следующем рецепте используется другой подход. Все числа с плавающей точкой в интервале являются возможным выбором. Мантисса берется из равномерного распределения целых чисел в диапазоне 2⁵² ≤ мантисса < 2⁵³. Экспонента берется из геометрического распределения, где экспоненты меньше -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)

Все real valued distributions в классе будут использовать новый метод:

>>> fr = FullRandom()
>>> fr.random()
0.05954861408025609
>>> fr.expovariate(0.25)
8.87925541791544

Этот рецепт концептуально эквивалентен алгоритму, который выбирает из всех кратных 2-¹⁰⁷⁴ в диапазоне 0,0 ≤ x < 1,0. Все такие числа расположены равномерно, но большинство из них необходимо округлить до ближайшего представимого в Python плавающего числа. (Значение 2-¹⁰⁷⁴ является наименьшим положительным ненормированным числом и равно math.ulp(0.0)).

См.также

Generating Pseudo-random Floating-Point Values статья Аллена Б. Дауни, описывающая способы генерации более мелкозернистых поплавков, чем обычно генерируются random().

Вернуться на верх