random
— 生成伪随机数¶
源代码: Lib/random.py
该模块为各种分布实现伪随机数生成器。
对于整数,可以从一个范围内均匀选择。 对于序列,可以均匀选择一个随机元素,可以使用一个函数来就地生成列表的随机排列,还可以使用一个函数进行无放回随机抽样。
在实数线上,可以使用函数来计算均匀分布、正态(高斯)分布、对数正态分布、负指数分布、伽马分布和贝塔分布。 对于生成角度分布,可以使用 von Mises 分布。
几乎所有的模块函数都依赖于基本函数 random()
,它在半开区间 0.0 <= X < 1.0
中均匀生成一个随机浮点数。 Python 使用 Mersenne Twister 作为核心生成器。 它生成 53 位精度的浮点数,周期为 2**19937-1。 C 中的底层实现既快速又线程安全。 Mersenne Twister 是现有的经过最广泛测试的随机数生成器之一。 但是,由于它是完全确定性的,因此它不适用于所有目的,并且完全不适合用于加密目的。
此模块提供的函数实际上是 random.Random
类的隐藏实例的绑定方法。 你可以实例化你自己的 Random
实例来获取不共享状态的生成器。
如果您想使用自己设计的不同的基本生成器,也可以对 Random
类进行子类化:请参阅该类的文档了解更多详细信息。
random
模块还提供了 SystemRandom
类,它使用系统函数 os.urandom()
从操作系统提供的源生成随机数。
警告
本模块的伪随机生成器不应用于安全目的。 对于安全或加密用途,请参阅 secrets
模块。
另请参阅
M. Matsumoto 和 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) 配方,用于兼容的替代随机数生成器,具有长周期和相对简单的更新操作。
备注
全局随机数生成器和 Random
的实例是线程安全的。 但是,在自由线程构建中,对全局生成器或 Random
的同一实例的并发调用可能会遇到争用和性能不佳。 请考虑为每个线程使用单独的 Random
实例。
簿记函数¶
- random.seed(a=None, version=2)¶
初始化随机数生成器。
如果省略 a 或为
None
,则使用当前系统时间。 如果操作系统提供了随机性来源,则使用这些来源而不是系统时间(有关可用性的详细信息,请参阅os.urandom()
函数)。如果 a 是一个整数,则直接使用它。
使用版本 2(默认),一个
str
、bytes
或bytearray
对象将被转换为int
,并使用其所有位。使用版本 1(提供用于从旧版本的 Python 重现随机序列),
str
和bytes
的算法会生成更窄范围的种子。在 3.2 版本中更改: 已移至使用字符串种子中的所有位的版本 2 方案。
- 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)¶
返回一个带有 *k* 个随机位的非负 Python 整数。此方法随梅森旋转生成器一起提供,其他一些生成器也可能将其作为 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.random()¶
返回
0.0 <= X < 1.0
范围内的下一个随机浮点数。
- random.uniform(a, b)¶
返回一个随机浮点数 N,当
a <= b
时,满足a <= N <= b
;当b < a
时,满足b <= N <= a
。端点值
b
是否包含在范围内取决于表达式a + (b-a) * random()
中的浮点数舍入。
- random.triangular(low, high, mode)¶
返回一个随机浮点数 N,满足
low <= N <= high
且具有介于这些边界之间的指定 *mode*。 *low* 和 *high* 边界默认为零和一。 *mode* 参数默认为边界之间的中点,从而给出对称分布。
- random.betavariate(alpha, beta)¶
Beta 分布。参数的条件是
alpha > 0
和beta > 0
。返回的值介于 0 和 1 之间。
- random.expovariate(lambd=1.0)¶
指数分布。 *lambd* 是所需平均值的倒数。它应该是非零的。(该参数本应称为“lambda”,但在 Python 中这是一个保留字。)如果 *lambd* 为正数,则返回的值范围从 0 到正无穷大;如果 *lambd* 为负数,则返回的值范围从负无穷大到 0。
在 3.12 版本中更改: 为
lambd
添加了默认值。
- random.gammavariate(alpha, beta)¶
Gamma 分布。(*不是* gamma 函数!)形状和尺度参数 *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)¶
Pareto 分布。 *alpha* 是形状参数。
- random.weibullvariate(alpha, beta)¶
Weibull 分布。 *alpha* 是尺度参数,*beta* 是形状参数。
替代生成器¶
- class random.Random([seed])¶
实现
random
模块使用的默认伪随机数生成器的类。Random
的子类如果希望使用不同的基本生成器,应覆盖以下方法- getstate()¶
在子类中重写此方法以自定义
getstate()
的Random
实例行为。
- setstate(state)¶
在子类中重写此方法以自定义
setstate()
的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)])
另请参阅
黑客统计,Jake Vanderplas 的视频教程,介绍了仅使用几个基本概念(包括模拟、抽样、洗牌和交叉验证)进行统计分析。
经济模拟,Peter Norvig 的市场模拟,展示了有效使用此模块提供的许多工具和分布(高斯、均匀、抽样、贝塔变量、选择、三角和 randrange)。
概率的具体介绍(使用 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()
返回范围 0.0 ≤ x < 1.0 内的 2⁻⁵³ 的倍数。所有这些数字都是均匀分布的,并且可以精确地表示为 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
该配方在概念上等同于一种算法,该算法从范围 0.0 ≤ x < 1.0 中 2⁻¹⁰⁷⁴ 的所有倍数中进行选择。所有这些数字都是均匀分布的,但大多数必须向下舍入到最接近的可表示的 Python 浮点数。(值 2⁻¹⁰⁷⁴ 是最小的正非规范化浮点数,等于 math.ulp(0.0)
。)
命令行用法¶
在版本 3.13 中添加。
可以从命令行执行 random
模块。
python -m random [-h] [-c CHOICE [CHOICE ...] | -i N | -f N] [input ...]
接受以下选项
- -h, --help¶
显示帮助消息并退出。
如果没有给出选项,则输出取决于输入
命令行示例¶
以下是 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