Python 隨機(random)模組的不可預測之美

一枚大果殼發表於2022-03-01

1 . 概念

1.1 真、偽隨機數

大部分的計算機語言都會提供 API 生成隨機數,此類 API 稱為隨機數生成器

計算機可以用隨機數模擬現實世界中的各種隨機概率問題,沒有隨機生成器的程式語言不是“好語言”。

什麼是真隨機數?

現實世界中的隨機數:比如擲錢幣、骰子、轉輪、使用電子元件的噪音、核裂變等等。

計算機通過硬體技術摸擬現實世界中這種物理現象所生成的隨機數,我們稱其為真隨機數。 這樣的隨機數生成器叫做物理性隨機數生成器。生成真隨機數對計算機的硬體技術要求較高。

真正隨機數的特點:不可預測。

如在擲硬幣時,你無法真正預測到下一次硬幣的面向。

什麼是偽隨機數?

由演算法摸擬生成的隨機數稱其為偽隨機數。計算機程式語言中所生成的隨機數基本上都是偽隨機數。

偽隨機數的特點:既然是由演算法模擬的,雖然在一個較短的週期內是無法預測的,在一個較長的週期內的隨機數具有可預測性。

1.2 隨機數種子

生成偽隨機數時,需要設定隨機種子,種子作用就是在隨機數的生成演算法裡注入一個動態變化量。

比如說使用系統的當前時間做隨機種子,隨機演算法就可以在時間變化的基礎上生成隨機性更大的隨機數。但是,如果不是在毫秒級別下生成隨機數,同一時間點下所生成的大量隨機數就有可能出現相等的情況。

選擇種子時,可以考慮綜合多維度的變化值進行運算。如在 UNIX 系統中,將系統時間、連入WIFI、甚至按下的鍵盤次數都量化為了seed。

參考指標越多,偽隨機數就越接近真正的隨機生成。

2. Python random 模組

random 模組實現了各種分佈的偽隨機數生成器。因為完全確定性,它不適用於所有目的,並且完全不適合加密目的。不應將此模組的偽隨機生成器用於安全目的。 有關安全性或加密用途,可使用 Python 中的 secrets 模組。

使得之前需要匯入 random 模組

import random

2.1 隨機模組的方法

  1. 初始化隨機種子
random.seed(a=None, version=2)
  • 如果 a 被省略或為 None ,則使用當前系統時間做隨機種子。

  • 如果作業系統提供隨機源,則使用它們而不是系統時間。

  • 如果 a 是 int 型別,則直接使用。

    當設定隨機種子是一個常量,則每一次隨機數是固定的。

    import random
    #設定隨機種子是一個 int 常量
    random.seed(10)
    print(random.random())
    #設定隨機種子是一個 int 常量
    random.seed(10)
    print(random.random())
    #設定隨機種子是一個 int 常量
    random.seed(10)
    print(random.random())
    

    輸出結果:

    0.5714025946899135
    0.5714025946899135
    0.5714025946899135
    
  1. 從一個數字範圍內產生隨機數字
 random.randrange(start, stop[, step])

range(start, stop, step) 返回一個隨機選擇的元素。

這相當於 choice(range(start, stop, step)),但實際上並沒有構建一個 range 物件

  1. 返回隨機整數
   random.randint(a, b)

相當於 randrange(a, b+1)

結果 N 滿足: a <= N <= b

  1. 從非空序列 seq 返回一個隨機元素。 如果 seq 為空,則引發 IndexError 異常。
random.choice(seq)
import random

lst = [5, 3, 90, 12, 4, 6]
r = random.choice(lst)
print(r)

每一次執行會從列表中隨機獲得一個數字。

  1. 將序列 x 隨機打亂
andom.shuffle(x[, random])

可選引數 random 是一個無引數函式,在 [0.0, 1.0) 中返回隨機浮點數;預設情況下,這是函式 random()

import random

lst = [5.0, 3.0, 90.0, 12.0, 4.0, 6.0]
#使用 random.random 函式
random.shuffle(lst, random.random)
print(lst)
#輸出結果
[3.0, 90.0, 6.0, 12.0, 5.0, 4.0]
#----------------------------------
def my_random():
    return float(random.randint(0, 1))

lst = [5.0, 3.0, 90.0, 12.0, 4.0, 6.0]
#使用使用者自定義函式
random.shuffle(lst, my_random)
print(lst)
  1. 返回從總體序列或集合中選擇的唯一元素的 k 長度列表。 用於無重複的隨機抽樣。
random.sample(population, k, *, counts=None)
  1. 返回 [0.0, 1.0) 範圍內的下一個隨機浮點數。
random.random()
  1. 返回一個隨機浮點數 N
random.uniform(a, b)

取決於等式 a + (b-a) * random() 中的浮點舍入,終點 b 可以包括或不包括在該範圍內。

結果 N 滿足:當 a <= ba <= N <= b ,當 b < ab <= N <= a

更多方法可查閱官方文件。

3. 不可預測之美

3.1 隨機彩色點

解題思路: 可結合 turtle 模組繪製,隨機小海龜出現的位置就可以了

import random
import turtle

colors = ["red", "blue", "green", "gray", "orange"]
for i in range(100):
    turtle.penup()
    x = random.randint(-300, 300)
    y = random.randint(-300, 300)

    turtle.goto(x, y)
    turtle.pendown()
    turtle.dot(20, colors[i % 5])

turtle.done()

3.2 求 π 的值

概率法又稱為蒙特卡羅法,是一種非常重要的數值計算方法。

該方法是以概率和統計理論方法為基礎的一種計算方法。將所求解的問題同一定的概率模型相聯絡,用計算機實現統計模擬或抽樣,以獲得問題的近似解。

假設有一個半徑為 1 的圓,如圖所示,則圖中陰影部分(1/4圓)的面積就等於值的1/4。通過概率法計算出陰影部分的面積,也就得到了π 值的 1/4,將陰影部分面積乘以 4 即可得到 π 的近似值。

求解思路

  • 利用隨機函式產生橫座標的值 x 和縱座標的值 y(這兩個值都應在0~1)

  • 判斷由這兩個隨機數構成的點是否位於1/4圓的區域內(陰影部分),若該點位於陰影區域內則進行計數。

  • 不斷產生新的點,由於隨機函式生成的點座標有一定的均勻性,當生成的點足夠多時,就可得到陰影內和陰影外點的近似均勻分佈。

  • 最後用在陰影內的點的數量除以總的點數,即可得到近似的陰影面積,也就得到了一個的1/4的近似值。

  import random
  
  i, n, s = 0, 0, 0
  x, y = 0.0, 0.0
  n = int(input("輸入點的數量:"))
  random.seed()
  for i in range(n):
      x = random.random()
      y = random.random()
      if (x * x + y * y) <= 1:
          s += 1
  
print("PI=%f\n", 4 * s / n)

輸出結果:

輸入點的數量:9000000
PI= 3.141477777777778

輸入的點數量越多,得到的 PI 的近似值就會越精確。

4 . 總結

隨機數可以完美地模擬真實世界裡的各種概率或隨機事件。python 的隨機數生成除了可以使用 random 模組外,還可以使用 numpy 庫中所提供的方法。

相關文章