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: A 623-dimensionally equidistributed uniform pseudorandom number generator”, 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 是一个整数,则直接使用它。
在版本 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 整数。此方法由 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 的列表,其中元素是从 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 的序列是 Mersenne Twister 随机数生成器周期内可以容纳的最大序列。版本 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 是 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
模块使用的默认伪随机数生成器的类。如果
Random
的子类希望使用不同的基本生成器,则应覆盖以下方法- getstate()¶
在子类中覆盖此方法以自定义
Random
实例的getstate()
行为。
- setstate(state)¶
在子类中覆盖此方法以自定义
Random
实例的setstate()
行为。
可选地,自定义生成器子类还可以提供以下方法
- getrandbits(k)¶
在子类中覆盖此方法以自定义
Random
实例的getrandbits()
行为。
- randbytes(n)¶
在子类中覆盖此方法以自定义
Random
实例的randbytes()
行为。
- class random.SystemRandom([seed])¶
使用
os.urandom()
函数从操作系统提供的源生成随机数的类。并非所有系统都可用。不依赖于软件状态,序列不可重现。因此,seed()
方法无效并被忽略。如果调用getstate()
和setstate()
方法,则会引发NotImplementedError
。
关于可重现性的说明¶
有时能够重现伪随机数生成器给出的序列是很有用的。通过重用种子值,只要没有多个线程运行,相同的序列就应该可以在不同运行之间重现。
随机模块的大多数算法和种子函数在 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)])
参见
Statistics for Hackers,由 Jake Vanderplas 制作的视频教程,介绍仅使用一些基本概念进行统计分析,包括模拟、采样、洗牌和交叉验证。
Economics Simulation,由 Peter Norvig 制作的市场模拟,展示了有效使用此模块提供的许多工具和分布(高斯、均匀、样本、贝塔分布、选择、三角分布和 randrange)。
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()
返回范围 0.0 ≤ x < 1.0 中 2⁻⁵³ 的倍数。所有这些数字均匀分布,并且可以精确表示为 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)
类中的所有 实值分布 将使用新方法
>>> fr = FullRandom()
>>> fr.random()
0.05954861408025609
>>> fr.expovariate(0.25)
8.87925541791544
该食谱在概念上等同于一种算法,该算法从范围 0.0 ≤ x < 1.0 中选择所有 2⁻¹⁰⁷⁴ 的倍数。所有这些数字都均匀分布,但大多数必须向下舍入到最接近的可表示 Python 浮点数。(值 2⁻¹⁰⁷⁴ 是最小的正非规范化浮点数,等于 math.ulp(0.0)
。)
参见
Generating Pseudo-random Floating-Point Values,Allen B. Downey 的一篇论文,描述了生成比 random()
通常生成的浮点数更精细的方法。
命令行用法¶
在 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