Spec-Zone.ru › Python 3.12

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

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

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

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

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

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

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

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

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

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

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

См. также

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

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

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

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

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

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

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

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

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

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

Изменено в версии 3.11: seed должно иметь один из следующих типов: None, 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().

Не следует использовать именованные аргументы, так как они могут интерпретироваться неожиданным образом. Например, 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 случайных битов. Этот метод предоставляется генератором «вихрь Мерсенна»; некоторые другие генераторы также могут включать его в свой API в качестве необязательного метода. Если метод доступен, getrandbits() позволяет randrange() работать с диапазонами произвольно большого размера.

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

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

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. Одновременное указание weights и cum_weights вызывает исключение TypeError.

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

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

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

Изменено в версии 3.9: Если все веса равны нулю, вызывается исключение ValueError.

random.shuffle(x)

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

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

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

Изменено в версии 3.11: Удален необязательный параметр random.

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

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

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

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

Повторяющиеся элементы можно указать по одному или с помощью необязательного параметра 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: population должна быть последовательностью. Автоматическое преобразование множеств в списки больше не поддерживается.

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

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

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

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

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

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

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

Добавлено в версии 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, с заданным значением mode между этими границами. По умолчанию границы low и high равны нулю и единице. По умолчанию аргумент mode равен середине между границами, что задает симметричное распределение.

random.betavariate(alpha, beta)

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

random.expovariate(lambd=1.0)

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

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

random.gammavariate(alpha, beta)

Гамма-распределение. (Не гамма-функция!) Параметры формы и масштаба alpha и beta должны быть положительными. (Соглашения о вызове различаются, и в некоторых источниках «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

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

# 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}')

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

# 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)])

См. также

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

Моделирование экономики — модель рынка, созданная Питером Норвигом, которая демонстрирует эффективное использование многих средств и распределений, предоставляемых этим модулем (gauss, uniform, sample, betavariate, choice, triangular и randrange).

Наглядное введение в теорию вероятностей (с использованием Python) — учебное пособие Питера Норвига, посвященное основам теории вероятностей, написанию моделей и анализу данных с помощью 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⁵² ≤ mantissa < 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)

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

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

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

См. также

Генерация псевдослучайных чисел с плавающей точкой — статья Аллена Б. Дауни, в которой описаны способы генерации чисел с плавающей точкой с более высокой точностью, чем обычно генерирует random().

© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/random.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API