這一節的標題是
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
程式執行結果的截圖如下圖