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

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


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

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

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

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

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

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

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

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

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

См.также

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

Рецепт дополнительного умножения с переносом для совместимого альтернативного генератора случайных чисел с большим периодом и сравнительно простыми операциями обновления.

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

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

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

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

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

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

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

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

random.getstate()

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

random.setstate(state)

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

random.getrandbits(k)

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

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

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

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

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

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

random.randint(a, b)

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

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

random.choice(seq)

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

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

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

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

Если не указаны ни weights, ни cum_weights, выбор выполняется с равной вероятностью. Если предоставляется последовательность весов, она должна быть той же длины, что и последовательность population. Это TypeError, чтобы указать как weights, так и cum_weights.

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

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

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

random.shuffle(x[, random])

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

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

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

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

random.sample(population, k)

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

Возвращает новый список, содержащий элементы из population, оставляя исходную population неизменной. Результирующий список находится в порядке выбора, так что все субсрезы (sub-slices) также будут действительными случайными выборками. Это позволяет разделить победителей розыгрыша (образец) на обладателей главного приза и занявших второе место (фрагменты).

Элементы population не обязательно должны быть хэшируемы или уникальными. Если генеральная совокупность содержит повторы, то каждое вхождение является возможным выбором в выборке.

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

Если размер выборки больше, чем размер генеральной population, поднимается ValueError.

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

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

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 и с указанным mode между этими границами. Границы 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(), определенная ниже.

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)

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

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

class random.Random([seed])

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

class random.SystemRandom([seed])

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

Примечания по воспроизводимости

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

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

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

Примеры и рецепты

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

>>> random()                             # случайный float:  0.0 <= x < 1.0
0.37444887175646646

>>> uniform(2.5, 10.0)                   # случайный float:  2.5 <= x < 10.0
3.1800146073117523

>>> expovariate(1 / 5)                   # Интервал между прибытиями в среднем 5 секунд
5.148957571865031

>>> randrange(10)                        # Целое число от 0 до 9 включительно
7

>>> randrange(0, 101, 2)                 # Четное целое число от 0 до 100 включительно
26

>>> choice(['win', 'lose', 'draw'])      # Один случайный элемент из последовательности
'draw'

>>> deck = 'ace two three four'.split()
>>> shuffle(deck)                        # Перемешать список
>>> deck
['four', 'two', 'ace', 'three']

>>> sample([10, 20, 30, 40, 50], k=4)    # Четыре образца без замены
[40, 10, 50, 30]

Симуляторы:

>>> # Шесть вращений колеса рулетки (взвешенная выборка с заменой)
>>> choices(['red', 'black', 'green'], [18, 18, 2], k=6)
['red', 'green', 'black', 'black', 'red', 'black']

>>> # Раздача 20 карт без замены из колоды из 52 игральных карт
>>> # и определить пропорцию карточек с 10 значением
>>> # (a ten, jack, queen, or king).
>>> deck = collections.Counter(tens=16, low_cards=36)
>>> seen = sample(list(deck.elements()), k=20)
>>> seen.count('tens') / 20
0.15

>>> # Оценить вероятность получения 5 или более "орлов" за 7 подбрасываний
>>> # смещенной монеты, которая оседает на "орлах" 60% времени.
>>> 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

>>> # Вероятность того, что медиана из 5 выборок окажется в двух средних квартилях
>>> 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

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

# http://statistics.about.com/od/Applications/a/Example-Of-Bootstrapping.htm
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'Выборочное среднее значение {mean(data):.1f} имеющее 90% доверительный '
      f'интервал от {means[5]:.1f} до {means[94]:.1f}')

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

# Пример из книги "Статистика проста" Денниса Шаши и Аманды Уилсон
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} перестановки меток привели только к {count} экземпляров с разницей')
print(f'по крайней мере, такая же экстремальная, как наблюдаемая разница {observed_diff:.1f}.')
print(f'Одностороннее p-значение {count / n:.4f} приводит нас к отклонению нулевого')
print(f'гипотеза о том, что нет никакой разницы между препаратом и плацебо.')

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

from heapq import heappush, heappop
from random import expovariate, gauss
from statistics import mean, median, stdev

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  # время, когда каждый сервер становится доступным
for i in range(100_000):
    arrival_time += expovariate(1.0 / average_arrival_interval)
    next_server_available = heappop(servers)
    wait = max(0.0, next_server_available - arrival_time)
    waits.append(wait)
    service_duration = gauss(average_service_time, stdev_service_time)
    service_completed = arrival_time + wait + service_duration
    heappush(servers, service_completed)

print(f'Среднее ожидание: {mean(waits):.1f}.  Stdev ожидание: {stdev(waits):.1f}.')
print(f'Медианное ожидание: {median(waits):.1f}.  Макс. ожидание: {max(waits):.1f}.')

См.также

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

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

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