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

Источник: Lib/random.py


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

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

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

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

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

Класс Random также может быть подклассом, если вы хотите использовать другой базовый генератор собственной разработки: смотрите документацию по этому классу для более подробной информации.

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

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

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

См.также

M. Matsumoto and T. Nishimura, «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 безопасны для потоков. Однако в свободно-поточной сборке одновременные обращения к глобальному генератору или к одному и тому же экземпляру Random могут привести к возникновению конфликтов и снижению производительности. Рассмотрите возможность использования отдельных экземпляров Random для каждого потока.

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

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.11: Семя должно быть одного из следующих типов: None, int, float, str, bytes или bytearray.

random.getstate()

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

random.setstate(state)

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

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

random.randbytes(n)

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

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

Added in version 3.9.

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

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

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

Это примерно эквивалентно choice(range(start, stop, step)), но поддерживает произвольно большие диапазоны и оптимизировано для распространенных случаев.

Шаблон позиционного аргумента соответствует функции range().

Аргументы с ключевыми словами не следует использовать, поскольку они могут быть интерпретированы неожиданным образом. Например, randrange(start=100) интерпретируется как randrange(0, 100, 1).

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

Изменено в версии 3.12: Автоматическое преобразование нецелых типов больше не поддерживается. Такие вызовы, как randrange(10.0) и randrange(Fraction(10, 1)), теперь вызывают ошибку TypeError.

random.randint(a, b)

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

random.getrandbits(k)

Возвращает неотрицательное целое число Python с k случайными битами. Этот метод поставляется с генератором Mersenne Twister, а некоторые другие генераторы могут предоставлять его в качестве дополнительной части 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, выборки производятся с равной вероятностью. Если указана последовательность весов, она должна быть той же длины, что и последовательность популяции. Если указаны и веса, и cum_weights, то это TypeError.

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

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

Added in version 3.6.

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

random.shuffle(x)

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

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

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

Изменено в версии 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.11: Население* должно быть последовательностью. Автоматическое преобразование множеств в списки больше не поддерживается.

Дискретные распределения

Следующая функция генерирует дискретное распределение.

random.binomialvariate(n=1, p=0.5)

Binomial distribution. Возвращает число успехов для n независимых испытаний с вероятностью успеха в каждом испытании p:

Математически эквивалентно:

sum(random() < p for i in range(n))

Число испытаний n должно быть неотрицательным целым числом. Вероятность успеха p должна находиться в диапазоне 0.0 <= p <= 1.0. Результатом будет целое число в диапазоне 0 <= X <= n.

Added in version 3.12.

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

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

random.random()

Возвращает следующее случайное число с плавающей точкой в диапазоне 0.0 <= X < 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=1.0)

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

Изменено в версии 3.12: Добавлено значение по умолчанию для lambd.

random.gammavariate(alpha, beta)

Гамма-распределение. (не функция гамма!) Параметры формы и масштаба, альфа и бета, должны иметь положительные значения. (Условные обозначения различны, и в некоторых источниках «бета» определяется как обратная величина шкалы).

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

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

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

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

Изменено в версии 3.11: У mu и sigma теперь есть аргументы по умолчанию.

random.lognormvariate(mu, sigma)

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

random.normalvariate(mu=0.0, sigma=1.0)

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

Изменено в версии 3.11: У mu и sigma теперь есть аргументы по умолчанию.

random.vonmisesvariate(mu, kappa)

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

random.paretovariate(alpha)

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

random.weibullvariate(alpha, beta)

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

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

class random.Random([seed])

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

Изменено в версии 3.11: Раньше seed мог быть любым хэшируемым объектом. Теперь оно ограничено: None, int, float, str, bytes или bytearray.

Подклассы Random должны переопределить следующие методы, если они хотят использовать другой базовый генератор:

seed(a=None, version=2)

Переопределите этот метод в подклассах, чтобы настроить поведение seed() для экземпляров Random.

getstate()

Переопределите этот метод в подклассах, чтобы настроить поведение getstate() для экземпляров Random.

setstate(state)

Переопределите этот метод в подклассах, чтобы настроить поведение setstate() для экземпляров Random.

random()

Переопределите этот метод в подклассах, чтобы настроить поведение random() для экземпляров Random.

В качестве опции подкласс пользовательского генератора может также предоставить следующий метод:

getrandbits(k)

Переопределите этот метод в подклассах, чтобы настроить поведение getrandbits() для экземпляров Random.

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.
>>> deal = sample(['tens', 'low cards'], counts=[16, 36], k=20)
>>> deal.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.
>>> sum(binomialvariate(n=7, p=0.6) >= 5 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.

Рецепты

Эти рецепты показывают, как эффективно делать случайные выборки из комбинаторных итераторов в модуле itertools:

def random_product(*args, repeat=1):
    "Random selection from itertools.product(*args, **kwds)"
    pools = [tuple(pool) for pool in args] * repeat
    return tuple(map(random.choice, pools))

def random_permutation(iterable, r=None):
    "Random selection from itertools.permutations(iterable, r)"
    pool = tuple(iterable)
    r = len(pool) if r is None else r
    return tuple(random.sample(pool, r))

def random_combination(iterable, r):
    "Random selection from itertools.combinations(iterable, r)"
    pool = tuple(iterable)
    n = len(pool)
    indices = sorted(random.sample(range(n), r))
    return tuple(pool[i] for i in indices)

def random_combination_with_replacement(iterable, r):
    "Choose r elements with replacement.  Order the result to match the iterable."
    # Result will be in set(itertools.combinations_with_replacement(iterable, r)).
    pool = tuple(iterable)
    n = len(pool)
    indices = sorted(random.choices(range(n), k=r))
    return tuple(pool[i] for i in indices)

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

Использование командной строки

Added in version 3.13.

Модуль random может быть запущен из командной строки.

python -m random [-h] [-c CHOICE [CHOICE ...] | -i N | -f N] [input ...]

Принимаются следующие варианты:

-h, --help

Покажите сообщение о помощи и выйдите.

-c CHOICE [CHOICE ...]
--choice CHOICE [CHOICE ...]

Выведите случайный выбор, используя choice().

-i <N>
--integer <N>

Выведите случайное целое число от 1 до N включительно, используя randint().

-f <N>
--float <N>

Выведите случайное число с плавающей точкой в диапазоне от 1 до N включительно, используя uniform().

Если опции не указаны, вывод зависит от ввода:

  • Строка или множество: то же, что и --choice.

  • Целое число: то же, что и --integer.

  • Плавающая величина: то же, что и --float.

Пример командной строки

Вот несколько примеров использования интерфейса командной строки random:

$ # Choose one at random
$ python -m random egg bacon sausage spam "Lobster Thermidor aux crevettes with a Mornay sauce"
Lobster Thermidor aux crevettes with a Mornay sauce

$ # Random integer
$ python -m random 6
6

$ # Random floating-point number
$ python -m random 1.8
1.7080016272295635

$ # With explicit arguments
$ python  -m random --choice egg bacon sausage spam "Lobster Thermidor aux crevettes with a Mornay sauce"
egg

$ python -m random --integer 6
3

$ python -m random --float 1.8
1.5666339105010318

$ python -m random --integer 6
5

$ python -m random --float 6
3.1942323316565915