random --- 生成偽隨機(jī)數(shù)?

源碼: Lib/random.py


該模塊實(shí)現(xiàn)了各種分布的偽隨機(jī)數(shù)生成器。

對(duì)于整數(shù),從范圍中有統(tǒng)一的選擇。 對(duì)于序列,存在隨機(jī)元素的統(tǒng)一選擇、用于生成列表的隨機(jī)排列的函數(shù)、以及用于隨機(jī)抽樣而無(wú)需替換的函數(shù)。

在實(shí)數(shù)軸上,有計(jì)算均勻、正態(tài)(高斯)、對(duì)數(shù)正態(tài)、負(fù)指數(shù)、伽馬和貝塔分布的函數(shù)。 為了生成角度分布,可以使用 von Mises 分布。

幾乎所有模塊函數(shù)都依賴于基本函數(shù) random() ,它在半開(kāi)放區(qū)間 [0.0,1.0) 內(nèi)均勻生成隨機(jī)浮點(diǎn)數(shù)。 Python 使用 Mersenne Twister 作為核心生成器。 它產(chǎn)生 53 位精度浮點(diǎn)數(shù),周期為 2**19937-1 ,其在 C 中的底層實(shí)現(xiàn)既快又線程安全。 Mersenne Twister 是現(xiàn)存最廣泛測(cè)試的隨機(jī)數(shù)發(fā)生器之一。 但是,因?yàn)橥耆_定性,它不適用于所有目的,并且完全不適合加密目的。

這個(gè)模塊提供的函數(shù)實(shí)際上是 random.Random 類(lèi)的隱藏實(shí)例的綁定方法。 你可以實(shí)例化自己的 Random 類(lèi)實(shí)例以獲取不共享狀態(tài)的生成器。

如果你想使用自己設(shè)計(jì)的不同基礎(chǔ)生成器,類(lèi) Random 也可以作為子類(lèi):在這種情況下,重載 random() 、 seed()getstate() 以及 setstate() 方法。可選地,新生成器可以提供 getrandbits() 方法——這允許 randrange() 在任意大的范圍內(nèi)產(chǎn)生選擇。

random 模塊還提供 SystemRandom 類(lèi),它使用系統(tǒng)函數(shù) os.urandom() 從操作系統(tǒng)提供的源生成隨機(jī)數(shù)。

警告

不應(yīng)將此模塊的偽隨機(jī)生成器用于安全目的。 有關(guān)安全性或加密用途,請(qǐng)參閱 secrets 模塊。

參見(jiàn)

M. Matsumoto and 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 用于兼容的替代隨機(jī)數(shù)發(fā)生器,具有長(zhǎng)周期和相對(duì)簡(jiǎn)單的更新操作。

簿記功能?

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

初始化隨機(jī)數(shù)生成器。

如果 a 被省略或?yàn)?None ,則使用當(dāng)前系統(tǒng)時(shí)間。 如果操作系統(tǒng)提供隨機(jī)源,則使用它們而不是系統(tǒng)時(shí)間(有關(guān)可用性的詳細(xì)信息,請(qǐng)參閱 os.urandom() 函數(shù))。

如果 a 是 int 類(lèi)型,則直接使用。

對(duì)于版本2(默認(rèn)的),str 、 bytesbytearray 對(duì)象轉(zhuǎn)換為 int 并使用它的所有位。

對(duì)于版本1(用于從舊版本的Python再現(xiàn)隨機(jī)序列),用于 strbytes 的算法生成更窄的種子范圍。

在 3.2 版更改: 已移至版本2方案,該方案使用字符串種子中的所有位。

在 3.11 版更改: The seed must be one of the following types: NoneType, int, float, str, bytes, or bytearray.

random.getstate()?

返回捕獲生成器當(dāng)前內(nèi)部狀態(tài)的對(duì)象。 這個(gè)對(duì)象可以傳遞給 setstate() 來(lái)恢復(fù)狀態(tài)。

random.setstate(state)?

state 應(yīng)該是從之前調(diào)用 getstate() 獲得的,并且 setstate() 將生成器的內(nèi)部狀態(tài)恢復(fù)到 getstate() 被調(diào)用時(shí)的狀態(tài)。

用于字節(jié)數(shù)據(jù)的函數(shù)?

random.randbytes(n)?

生成 n 個(gè)隨機(jī)字節(jié)。

此方法不可用于生成安全憑據(jù)。 那應(yīng)當(dāng)使用 secrets.token_bytes()。

3.9 新版功能.

整數(shù)用函數(shù)?

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

Return a randomly selected element from range(start, stop, step).

This is roughly equivalent to choice(range(start, stop, step)) but supports arbitrarily large ranges and is optimized for common cases.

The positional argument pattern matches the range() function.

Keyword arguments should not be used because they can interpreted in unexpected ways. For example range(start=100) is interpreted as range(0, 100, 1).

在 3.2 版更改: randrange() 在生成均勻分布的值方面更為復(fù)雜。 以前它使用了像``int(random()*n)``這樣的形式,它可以產(chǎn)生稍微不均勻的分布。

在 3.12 版更改: Automatic conversion of non-integer types is no longer supported. Calls such as randrange(10.0) and randrange(Fraction(10, 1)) now raise a TypeError.

random.randint(a, b)?

返回隨機(jī)整數(shù) N 滿足 a <= N <= b。相當(dāng)于 randrange(a, b+1)。

random.getrandbits(k)?

返回具有 k 個(gè)隨機(jī)比特位的非負(fù) Python 整數(shù)。 此方法隨 MersenneTwister 生成器一起提供,其他一些生成器也可能將其作為 API 的可選部分提供。 在可能的情況下,getrandbits() 會(huì)啟用 randrange() 來(lái)處理任意大的區(qū)間。

在 3.9 版更改: 此方法現(xiàn)在接受零作為 k 的值。

序列用函數(shù)?

random.choice(seq)?

從非空序列 seq 返回一個(gè)隨機(jī)元素。 如果 seq 為空,則引發(fā) IndexError。

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

population 中選擇替換,返回大小為 k 的元素列表。 如果 population 為空,則引發(fā) IndexError。

如果指定了 weight 序列,則根據(jù)相對(duì)權(quán)重進(jìn)行選擇。 或者,如果給出 cum_weights 序列,則根據(jù)累積權(quán)重(可能使用 itertools.accumulate() 計(jì)算)進(jìn)行選擇。 例如,相對(duì)權(quán)重``[10, 5, 30, 5]``相當(dāng)于累積權(quán)重``[10, 15, 45, 50]``。 在內(nèi)部,相對(duì)權(quán)重在進(jìn)行選擇之前會(huì)轉(zhuǎn)換為累積權(quán)重,因此提供累積權(quán)重可以節(jié)省工作量。

如果既未指定 weight 也未指定 cum_weights ,則以相等的概率進(jìn)行選擇。 如果提供了權(quán)重序列,則它必須與 population 序列的長(zhǎng)度相同。 一個(gè) TypeError 指定了 weightscum_weights 。

weightscum_weights 可使用 random() 所返回的能與 float 值進(jìn)行相互運(yùn)算的任何數(shù)字類(lèi)型(包括整數(shù)、浮點(diǎn)數(shù)、分?jǐn)?shù)但不包括 decimal)。 權(quán)重值應(yīng)當(dāng)非負(fù)且為有限的數(shù)值。 如果所有的權(quán)重值均為零則會(huì)引發(fā) ValueError。

對(duì)于給定的種子,具有相等加權(quán)的 choices() 函數(shù)通常產(chǎn)生與重復(fù)調(diào)用 choice() 不同的序列。 choices() 使用的算法使用浮點(diǎn)運(yùn)算來(lái)實(shí)現(xiàn)內(nèi)部一致性和速度。 choice() 使用的算法默認(rèn)為重復(fù)選擇的整數(shù)運(yùn)算,以避免因舍入誤差引起的小偏差。

3.6 新版功能.

在 3.9 版更改: 如果所有權(quán)重均為負(fù)值則將引發(fā) ValueError。

random.shuffle(x)?

就地將序列 x 隨機(jī)打亂位置。

要改變一個(gè)不可變的序列并返回一個(gè)新的打亂列表,請(qǐng)使用``sample(x, k=len(x))``。

請(qǐng)注意,即使對(duì)于小的 len(x),x 的排列總數(shù)也可以快速增長(zhǎng),大于大多數(shù)隨機(jī)數(shù)生成器的周期。 這意味著長(zhǎng)序列的大多數(shù)排列永遠(yuǎn)不會(huì)產(chǎn)生。 例如,長(zhǎng)度為2080的序列是可以在 Mersenne Twister 隨機(jī)數(shù)生成器的周期內(nèi)擬合的最大序列。

Deprecated since version 3.9, removed in version 3.11: 可選形參 random。

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

Return a k length list of unique elements chosen from the population sequence. Used for random sampling without replacement.

返回包含來(lái)自總體的元素的新列表,同時(shí)保持原始總體不變。 結(jié)果列表按選擇順序排列,因此所有子切片也將是有效的隨機(jī)樣本。 這允許抽獎(jiǎng)獲獎(jiǎng)?wù)撸颖荆┍粍澐譃榇螵?jiǎng)和第二名獲勝者(子切片)。

總體成員不必是 hashable 或 unique 。 如果總體包含重復(fù),則每次出現(xiàn)都是樣本中可能的選擇。

重復(fù)的元素可以一個(gè)個(gè)地直接列出,或使用可選的僅限關(guān)鍵字形參 counts 來(lái)指定。 例如,sample(['red', 'blue'], counts=[4, 2], k=5) 等價(jià)于 sample(['red', 'red', 'red', 'red', 'blue', 'blue'], k=5)。

要從一系列整數(shù)中選擇樣本,請(qǐng)使用 range() 對(duì)象作為參數(shù)。 對(duì)于從大量人群中采樣,這種方法特別快速且節(jié)省空間:sample(range(10000000), k=60) 。

如果樣本大小大于總體大小,則引發(fā) ValueError 。

在 3.9 版更改: 增加了 counts 形參。

在 3.11 版更改: The population must be a sequence. Automatic conversion of sets to lists is no longer supported.

實(shí)值分布?

以下函數(shù)生成特定的實(shí)值分布。如常用數(shù)學(xué)實(shí)踐中所使用的那樣,函數(shù)形參以分布方程中的相應(yīng)變量命名,大多數(shù)這些方程都可以在任何統(tǒng)計(jì)學(xué)教材中找到。

random.random()?

返回 [0.0, 1.0) 范圍內(nèi)的下一個(gè)隨機(jī)浮點(diǎn)數(shù)。

random.uniform(a, b)?

返回一個(gè)隨機(jī)浮點(diǎn)數(shù) N ,當(dāng) a <= b 時(shí) a <= N <= b ,當(dāng) b < a 時(shí) b <= N <= a 。

取決于等式 a + (b-a) * random() 中的浮點(diǎn)舍入,終點(diǎn) b 可以包括或不包括在該范圍內(nèi)。

random.triangular(low, high, mode)?

返回一個(gè)隨機(jī)浮點(diǎn)數(shù) N ,使得 low <= N <= high 并在這些邊界之間使用指定的 mode 。 lowhigh 邊界默認(rèn)為零和一。 mode 參數(shù)默認(rèn)為邊界之間的中點(diǎn),給出對(duì)稱(chēng)分布。

random.betavariate(alpha, beta)?

Beta 分布。 參數(shù)的條件是 alpha > 0beta > 0。 返回值的范圍介于 0 和 1 之間。

random.expovariate(lambd)?

指數(shù)分布。 lambd 是 1.0 除以所需的平均值,它應(yīng)該是非零的。 (該參數(shù)本應(yīng)命名為 “l(fā)ambda” ,但這是 Python 中的保留字。)如果 lambd 為正,則返回值的范圍為 0 到正無(wú)窮大;如果 lambd 為負(fù),則返回值從負(fù)無(wú)窮大到 0。

random.gammavariate(alpha, beta)?

Gamma 分布。 ( 不是 gamma 函數(shù)! ) 參數(shù)的條件是 alpha > 0beta > 0。

概率分布函數(shù)是:

          x ** (alpha - 1) * math.exp(-x / beta)
pdf(x) =  --------------------------------------
            math.gamma(alpha) * beta ** alpha
random.gauss(mu=0.0, sigma=1.0)?

正態(tài)分布,也稱(chēng)高斯分布。 mu 為平均值,而 sigma 為標(biāo)準(zhǔn)差。 此函數(shù)要稍快于下面所定義的 normalvariate() 函數(shù)。

多線程注意事項(xiàng):當(dāng)兩個(gè)線程同時(shí)調(diào)用此方法時(shí),它們有可能將獲得相同的返回值。 這可以通過(guò)三種辦法來(lái)避免。 1) 讓每個(gè)線程使用不同的隨機(jī)數(shù)生成器實(shí)例。 2) 在所有調(diào)用外面加鎖。 3) 改用速度較慢但是線程安全的 normalvariate() 函數(shù)。

在 3.11 版更改: mu and sigma now have default arguments.

random.lognormvariate(mu, sigma)?

對(duì)數(shù)正態(tài)分布。 如果你采用這個(gè)分布的自然對(duì)數(shù),你將得到一個(gè)正態(tài)分布,平均值為 mu 和標(biāo)準(zhǔn)差為 sigma 。 mu 可以是任何值,sigma 必須大于零。

random.normalvariate(mu=0.0, sigma=1.0)?

正態(tài)分布。 mu 是平均值,sigma 是標(biāo)準(zhǔn)差。

在 3.11 版更改: mu and sigma now have default arguments.

random.vonmisesvariate(mu, kappa)?

馮·米塞斯分布。 mu 是平均角度,以弧度表示,介于0和 2*pi 之間,kappa 是濃度參數(shù),必須大于或等于零。 如果 kappa 等于零,則該分布在 0 到 2*pi 的范圍內(nèi)減小到均勻的隨機(jī)角度。

random.paretovariate(alpha)?

帕累托分布。 alpha 是形狀參數(shù)。

random.weibullvariate(alpha, beta)?

威布爾分布。 alpha 是比例參數(shù),beta 是形狀參數(shù)。

替代生成器?

class random.Random([seed])?

該類(lèi)實(shí)現(xiàn)了 random 模塊所用的默認(rèn)偽隨機(jī)數(shù)生成器。

3.9 版后已移除: 在將來(lái),seed 必須是下列類(lèi)型之一: NoneType, int, float, str, bytesbytearray

class random.SystemRandom([seed])?

使用 os.urandom() 函數(shù)的類(lèi),用從操作系統(tǒng)提供的源生成隨機(jī)數(shù)。 這并非適用于所有系統(tǒng)。 也不依賴于軟件狀態(tài),序列不可重現(xiàn)。 因此,seed() 方法沒(méi)有效果而被忽略。 getstate()setstate() 方法如果被調(diào)用則引發(fā) NotImplementedError。

關(guān)于再現(xiàn)性的說(shuō)明?

有時(shí)能夠重現(xiàn)偽隨機(jī)數(shù)生成器給出的序列是很有用處的。 通過(guò)重用一個(gè)種子值,只要沒(méi)有運(yùn)行多線程,相同的序列就應(yīng)當(dāng)可在多次運(yùn)行中重現(xiàn)。

大多數(shù)隨機(jī)模塊的算法和種子函數(shù)都會(huì)在 Python 版本中發(fā)生變化,但保證兩個(gè)方面不會(huì)改變:

  • 如果添加了新的播種方法,則將提供向后兼容的播種機(jī)。

  • 當(dāng)兼容的播種機(jī)被賦予相同的種子時(shí),生成器的 random() 方法將繼續(xù)產(chǎn)生相同的序列。

例子?

基本示例:

>>>
>>> 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.
>>> dealt = sample(['tens', 'low cards'], counts=[16, 36], k=20)
>>> dealt.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.
>>> 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

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

statistical bootstrapping 的示例,使用重新采樣和替換來(lái)估計(jì)一個(gè)樣本的均值的置信區(qū)間:

# 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'The sample mean of {mean(data):.1f} has a 90% confidence '
      f'interval from {means[5]:.1f} to {means[94]:.1f}')

使用 重新采樣排列測(cè)試 來(lái)確定統(tǒng)計(jì)學(xué)顯著性或者使用 p-值 來(lái)觀察藥物與安慰劑的作用之間差異的示例:

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

多服務(wù)器隊(duì)列的到達(dá)時(shí)間和服務(wù)交付模擬:

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

參見(jiàn)

Statistics for Hackers Jake Vanderplas 撰寫(xiě)的視頻教程,使用一些基本概念進(jìn)行統(tǒng)計(jì)分析,包括模擬、抽樣、洗牌和交叉驗(yàn)證。

Economics Simulation Peter Norvig 編寫(xiě)的市場(chǎng)模擬,顯示了該模塊提供的許多工具和分布的有效使用( gauss 、 uniformsample 、 betavariate 、 choice 、 triangularrandrange )。

A Concrete Introduction to Probability (using Python) Peter Norvig 撰寫(xiě)的教程,涵蓋了概率論基礎(chǔ)知識(shí),如何編寫(xiě)模擬,以及如何使用 Python 進(jìn)行數(shù)據(jù)分析。

例程?

默認(rèn)的 random() 返回在 0.0 ≤ x < 1.0 范圍內(nèi) 2??3 的倍數(shù)。 所有這些數(shù)值間隔相等并能精確表示為 Python 浮點(diǎn)數(shù)。 但是在此間隔上有許多其他可表示浮點(diǎn)數(shù)是不可能的選擇。 例如,0.05954861408025609 就不是 2??3 的整數(shù)倍。

以下規(guī)范程序采取了一種不同的方式。 在間隔上的所有浮點(diǎn)數(shù)都是可能的選擇。 它們的尾數(shù)取值來(lái)自 2?2 ≤ 尾數(shù) < 2?3 范圍內(nèi)整數(shù)的均勻分布。 指數(shù)取值則來(lái)自幾何分布,其中小于 -53 的指數(shù)的出現(xiàn)頻率為下一個(gè)較大指數(shù)的一半。

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)

該類(lèi)中所有的 實(shí)值分布 都將使用新的方法:

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

該規(guī)范程序在概念上等效于在 0.0 ≤ x < 1.0 范圍內(nèi)對(duì)所有 2?1??? 的倍數(shù)進(jìn)行選擇的算法。 所有這樣的數(shù)字間隔都相等,但大多必須向下舍入為最接近的 Python 浮點(diǎn)數(shù)表示形式。 (2?1??? 這個(gè)數(shù)值是等于 math.ulp(0.0) 的未經(jīng)正規(guī)化的最小正浮點(diǎn)數(shù)。)

參見(jiàn)

生成偽隨機(jī)浮點(diǎn)數(shù)值 為 Allen B. Downey 所撰寫(xiě)的描述如何生成相比通過(guò) random() 正常生成的數(shù)值更細(xì)粒度浮點(diǎn)數(shù)的論文。