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