更新於 2024/06/17閱讀時間約 6 分鐘

The Nature of Code閱讀心得與Python實作:0.3 Probability and Non...

這一節的標題是
0.3 Probability and Nonuniform Distributions
因為方格子標題字數限制,所以沒完整顯現

在模擬自然界中的事物時導入隨機性,可以讓結果看起來比較自然,但如果導入的隨機性都是uniform distribution,那未免也太呆板了,畢竟很多自然界中的事物,雖然看起來都很隨機,但也沒那麼隨機。例如,在繁衍後代時,基因的傳遞具有一定的隨機性,但也有「適者生存」這樣子的趨勢存在。這時候,uniform distribution亂數已經不夠用了,我們還需要nonuniform distribution亂數,來讓模擬出來的結果,更像真的一樣。

那要怎麼產生nonuniform distribution亂數呢?其實利用一些小技巧,就可以利用會產生uniform distribution亂數的亂數產生器如random.random()random.randint()等,來產生符合特定分布的亂數。不過在此之前,要先來複習一下機率。

談論機率,就不免要提到丟銅板和抽撲克牌這兩個屬於單一事件機率(single-event probability)的例子。這類型的機率,指的是發生某個事件結果的機率,例如抽撲克牌抽到老K的機率、丟銅板出現正面的機率等,計算方式很簡單,就是把所有會產生那個事件結果的數量,除以所有可能發生情況的數量。以丟銅板來說,丟銅板這個事件,可能產生的結果有正面、反面兩種。所以,出現正面這個結果的機率是1/2;出現反面的機率,同樣也是1/2。

再來看看抽撲克牌的機率算法。拿一副牌,抽中老K的機率是

老K的數量 ⁄ 牌的總量 = 4 ⁄ 52 ≈ 0.077

而抽中方塊的機率則為

方塊牌的數量 ⁄ 牌的總量 = 13 ⁄ 52 = 0.25

同樣的道理,抽中某個點數的機率約為0.077;而抽中某個花色的機率是0.25。

要計算多個事件接續出現的機率時,只要把個別事件出現的機率相乘即可。例如,丟銅板時,連續出現3次正面的機率是

(1 ⁄ 2)×(1 ⁄ 2)×(1 ⁄ 2) = 0.125

Exercise 0.2

一副撲克牌有52張,ace有四張。把牌抽出後如果放回去重洗,則連續抽出2張ace的機率是

(4 ⁄ 52)×(4 ⁄ 52) ≈ 0.0059

如果牌抽出後不放回去重洗,則連續抽出2張ace的機率是

(4 ⁄ 52)×(3 ⁄ 51) ≈ 0.0045

應用前面單一事件機率的算法,寫程式時,可以利用亂數函數,來使不同的結果,其出現機率不同。例如執行下列程式,印出1、2、3的機率會分別是40%、20%、40%。

stuff = [1, 1, 2, 3, 3]
value = random.choice(stuff)
print(value)

雖然random.choice()在抽選stuff內的元素時,每個元素被抽選中的機率都一樣是20%,但因為元素1和3都有兩個,所以最後抽選出1和3的機率,都是40%。

我們也可以讓某個事件,只在取出的亂數介於某個範圍內才發生:

prob = 0.1
r = random.random()
if (r < prob):
print("Hi!")

因為0.0 <= r < 1.0,所以會印出Hi!的機率是0.1。

這種技巧,也可以用在多重結果的事件上。例如,假設某事件出現A、B、C三種結果的機率,分別為60%、10%、30%,這時可以取一個0~1間的浮點數亂數num,然後用它來決定哪個結果會出現。程式寫法如下:

num = random.random()  # 0.0 <= num < 1.0
if (num < 0.6):
# 0.0 <= num < 0.6
print("結果A")
elif (num < 0.7):
# 0.6 <= num < 0.7
print("結果B")
else:
# 0.7 <= num < 1.0
print("結果C")

利用這個技巧,可以讓Walker在移動的時候,傾向向右走。下面這個例子,就是讓Walker向上、下、左、右走的機率,分別是20%、20%、20%、40%,這樣子的Walker,就會具有傾向向右走的特性。

Example 0.3: A Walker That Tends to Move to the Right

Walker的step()方法,程式碼修改如下

def step(self):
r = random.random()
if (r < 0.2):
# up
self.y -= 1
elif (r < 0.4):
# down
self.y += 1
elif (r < 0.6):
# left
self.x -= 1
else:
# right
self.x += 1

Exercise 0.3

要讓Walker有50%的機率,會走向滑鼠游標的方向,可將step()方法修改為

def step(self):
r = random.random()
if (r < 0.125):
# up, 12.5%
self.y -= 1
elif (r < 0.25):
# down, 12.5%
self.y += 1
elif (r < 0.375):
# left, 12.5%
self.x -= 1
elif (r < 0.5):
# right, 12.5%
self.x += 1
else:
# mouse, 50%
(mouse_x, mouse_y) = pygame.mouse.get_pos()

if (mouse_x > self.x):
self.x += 1
else:
self.x -=1

if (mouse_y > self.y):
self.y += 1
else:
self.y -= 1

程式執行結果的截圖如下圖




分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.