隨機

隨機 在 Python

{other: "%{count} 個練習"}

關於 隨機

許多程式需要用(看似)隨機的值來模擬真實世界的事件。

常見且熟悉的例子包括:

  • 擲硬幣:從 ('Heads', 'Tails')中取出的一個隨機值。
  • 擲骰子:1 到 6 之間的隨機整數。
  • 洗牌:將一疊卡片隨機重新排序。

用電腦產生真正隨機的值,是一項出乎意料地困難的技術挑戰,所以你會看到這類結果被稱為「偽隨機」。

實際上,像 Python 標準函式庫中的 random模組這樣設計良好的函式庫,既快速又有彈性,對建模、模擬和遊戲中的大多數應用來說,產生的結果已經綽綽有餘。

本頁接下來會列出 random中幾個最常用的函式。 我們鼓勵你自行探索完整的 random文件,因為可用的選項遠比這裡介紹的還要多。

Caution

random模組__不應__用於安全性或密碼學相關的應用。

Python 改為提供 secrets模組。 它專門針對密碼學安全性進行了最佳化。 建立 secrets 模組的部分先前問題與原因,可以參閱 PEP 506。

匯入

在你能使用 random模組中的工具之前,必須先匯入它:

>>> import random

# Choose random integer from a range
>>> random.randrange(1000)
360

>>> random.randrange(-1, 500)
228

>>> random.randrange(-10, 11, 2)
-8

# Choose random integer between two values (inclusive)
>>> random.randint(5, 25)
22

為了避免輸入模組名稱,你可以依名稱匯入特定的函式:

>>> from random import choice, choices

# Using choice() to pick Heads or Tails 10 times
>>> tosses = []
... for side in range(10):
...    tosses.append(choice(['H', 'T']))    

>>> print(tosses)
['H', 'H', 'H', 'H', 'H', 'H', 'H', 'T', 'T', 'H']


# Using choices() to pick Heads or Tails 8 times
>>> picks = []
... picks.extend(choices(['H', 'T'], k=8))
... print(picks)
['T', 'H', 'H', 'T', 'H', 'H', 'T', 'T']

建立隨機整數

randrange()函式有三種形式,用來從 range(start, stop, step)中選出一個隨機值:

  1. randrange(stop) 會給出整數 n,使得 0 <= n < stop
  2. randrange(start, stop) 會給出整數 n,使得 start <= n < stop
  3. randrange(start, stop, step) 會給出整數 n,使得 start <= n < stop,且 n 位於序列 start, start + step, start + 2*step... 之中

對於 step == 1 這種常見情況,randint(a, b)函式可能更方便、更好讀。 randint()的結果可能_包含_上界,所以 randint(a, b) 等同於使用 randrange(a, b+1):

>>> import random

# Select one number at random from the range 0, 499
>>> random.randrange(500)
219

# Select 10 numbers at random between 0 and 9 two steps apart.
>>> numbers = []
... for integer in range(10):
...    numbers.append(random.randrange(0, 10, 2))

>>> print(numbers)
[2, 8, 4, 0, 4, 2, 6, 6, 8, 8]

# roll a die
>>> random.randint(1, 6)  
4

處理序列

本節的函式假設你是從某個序列或其他容器開始。

這通常是 list,或在某些限制下是 tuple 或 set(tuple 不可變,而 set 無序)。

choice() 和 choices()

choice()函式會從給定的序列中隨機回傳一個項目。 最簡單的情況,可能就是擲硬幣:

# This will pick one of the two values in the list at random 5 separate times 
>>> [random.choice(['H', 'T']) for _ in range(5)]
['T', 'H', 'H', 'T', 'H']

我們也可以用 choices()函式達到基本上相同的效果,只要提供一個帶有列表長度的關鍵字引數:

>>> random.choices(['H', 'T'], k=5)
['T', 'H', 'T', 'H', 'H']

在上面的例子中,我們假設硬幣是公平的,正反面機率相等,但也可以指定權重。 例如,如果袋子裡有 10 顆紅球和 15 顆綠球,而我們想隨機抽出一顆:

>>> random.choices(['red', 'green'], [10, 15])
['red']

sample()

上面的 choices()例子假設的是統計學家所稱的「有放回抽樣」。 每一次抽取或選擇對之後選擇的機率沒有影響,而且每次抽取時,可能選擇的分布都保持不變。

在紅球和綠球的例子中:每次選擇之後,我們把球_放回_袋子裡,並在下一次抽取前充分搖勻。 這與另一種情況相反:我們抽出一顆紅球,然後_它就留在外面_。 不把球放回去,代表袋子裡的紅球變少了,下一次選擇是紅球的機率也就_更低_。

為了模擬這種「無放回抽樣」,random 模組提供了 sample()函式。 sample()的語法和 choices()類似,只是多了一個 counts 關鍵字參數:

>>> random.sample(['red', 'green'], counts=[10, 15], k=10)
['green', 'green', 'green', 'green', 'green', 'red', 'red', 'red', 'red', 'green']

樣本會按照被選出的順序回傳。

shuffle()

當 k > 1 時,choices()和 sample()都會回傳新的列表。 相對地,shuffle()會原地打亂列表的順序,原本的順序會遺失:

>>> my_list = [1, 2, 3, 4, 5]
>>> random.shuffle(my_list)
>>> my_list
[4, 1, 5, 2, 3]

處理分布

到目前為止,我們都聚焦在所有結果機率相等的情況。 例如,random.randrange(100)給出 0 到 99 之間任何整數的機率都相同。

許多真實世界的情況遠比這複雜。 因此,統計學家建立了各式各樣的distributions來用數學方式描述「真實世界」的結果。

均勻分布

對整數而言,當所有機率都相等時,會使用 randrange()和 randint()。 這稱為uniform分布。

randrange()和 randint()也有對應的浮點數版本。

__random()__會給出一個 float 值 x,使得 0.0 <= x < 1.0。

__uniform(a, b)__會給出 x,使得 a <= x <= b。

>>> [round(random.random(), 3) for _ in range(5)]
[0.876, 0.084, 0.483, 0.22, 0.863]

>>> [round(random.uniform(2, 5), 3) for _ in range(5)]
[2.798, 2.539, 3.779, 3.363, 4.33]

高斯分布

它也叫「常態」分布或「鐘形」曲線,是描述測量值不精確度的一種非常常見方式。

例如,假設你工作的工廠剛買了 10,000 顆應該完全相同的螺栓。 你想設定工廠機器人來處理它們,於是抽樣秤了 100 顆,發現它們的平均(或 mean)重量是 4.731g。 這極不可能代表它們全部都剛好重 4.731g。 也許你發現數值範圍從 4.627 到 4.794g,但集中在 4.731g 附近。

這就是Gaussian distribution,其機率在平均值處達到高峰,並在兩側對稱地遞減(因此稱為「鐘形」)。 要在軟體中模擬這種分布,我們需要某種方式來指定曲線的寬度(通常,昂貴的螺栓會比便宜的螺栓更緊密地集中在平均值附近!)。

按照慣例,這用standard deviation來表示:數值小代表曲線又尖又窄,數值大則代表曲線又低又寬。 數學家喜歡希臘字母,所以我們用 μ('mu')代表平均值,用 σ('sigma')代表標準差。 因此,當你讀到「95% 的數值落在 μ 的 2σ 範圍內」或「希格斯玻色子以 5-sigma 的信心水準被偵測到」時,這類說法都與標準差有關。

>>> mu = 4.731
>>> sigma = 0.316
>>> [round(random.gauss(mu, sigma), 3) for _ in range(5)]
[4.72, 4.957, 4.64, 4.556, 4.968]
透過 GitHub 編輯 連結會在新視窗或分頁中開啟