1. 程式人生 > >用蒙特卡洛方法計算小遊戲獲勝概率(Python實現)

用蒙特卡洛方法計算小遊戲獲勝概率(Python實現)

遊戲描述:

有兩人X和Y,遊戲之前兩人分別擁有籌碼x個和y個,兩人開始進行每人獲勝概率都是50%的比拼,如果X贏了,並且之前x>=y,那麼遊戲結束,X獲勝;如果X贏了,但是之前x<y,則X的籌碼翻倍,變成2x,Y的籌碼變成y-x,遊戲繼續; 問:當x,y取不同值時,X的獲勝概率分別是多少。

什麼是蒙特卡洛方法

  • 蒙特卡羅(Monte Carlo)方法:用統計結果去計算頻率,從而得到真實值的近似值。
  • 一個簡單例子: 比如你有一枚硬幣,想計算扔硬幣正面朝上的概率,那你可以仍很多次(例如100000次),記錄正面朝上的次數(比如50012次),最終可得到正面朝上概率=正面朝上次數/總次數=50012/100000=50.012%。
  • 扔100000次硬幣想想就很痛苦,不過沒關係,我們現在可以利用計算機幫助我們模擬大量的隨機實驗

解題思路:

  1. 首先構造一個雙方獲勝概率分別是50%的比拼: X和Y分別取0到1之間的一個隨機數,若x_score>y_score,則X獲勝,若y_score>x_score,則Y獲勝,那麼這個比拼中X和Y的獲勝概率都是50%;
  2. 對某個特定的x和y值,模擬一次遊戲,待遊戲結束,如果X獲勝,則記錄1,如果Y獲勝,則記錄0;
  3. 對某個特定的x和y值,模擬100000次遊戲,記錄X獲勝次數(1的個數),計算X獲勝概率。
  4. 對於不同的x和y值,進行實驗,得到不同x和y值時X的獲勝概率。
  5. 總結規律,X的獲勝概率和初始值x,y的關係是什麼?

用python實現

import numpy as np
import pandas as pd

# 模擬一次遊戲
def match(init_x, init_y):
    x = init_x
    y = init_y
    x_result = None
    while x_result is None:
        if x == 0:
            x_result = 0
        elif y == 0:
            x_result = 1
        else:
            x_score, y_score = np.random.rand(2) # X和Y分別得到一個0到1的隨機數
            if x >= y:
                if x_score > y_score:
                    x_result = 1
                elif x_score < y_score:
                    x = x - y
                    y = y + y
                else:
                    continue
            else:
                if x_score < y_score:
                    x_result = 0
                elif x_score > y_score:
                    y = y - x
                    x = x + x
                else:
                    continue
    return x_result

# 模擬n次遊戲,計算獲勝概率
def simulation_test(x, y, n):
    test_results = []
    for i in range(n):
        r = match(x, y)
        test_results.append(r)
    p = np.mean(test_results)*100
    return p

# x從1到10取值,y從1到10取值,對於不同的(x,y)數值對,計算X獲勝概率
def main():
    results = []
    for x in range(1,11):
        for y in range(1,11):
            x_win_rate = simulation_test(x, y, 100000)
            d = {'x': x, 'y': y, 'x_win_rate': x_win_rate}
            results.append(d)
    df = pd.DataFrame(results, columns=['x','y','x_win_rate'])
    pd.set_option('display.max_rows', None)
    print(df)

if __name__ == "__main__":
    main()

執行結果:

x y x_win_rate
0 1 1 49.919
1 1 2 33.460
2 1 3 25.110
3 1 4 19.996
4 1 5 16.797
5 1 6 14.293
6 1 7 12.578
7 1 8 11.211
8 1 9 10.010
9 1 10 9.026
10 2 1 66.556
11 2 2 50.027
12 2 3 40.093
13 2 4 33.200
14 2 5 28.639
15 2 6 24.925
16 2 7 22.363
17 2 8 20.185
18 2 9 18.279
19 2 10 16.671
20 3 1 75.035
21 3 2 60.017
22 3 3 49.626
23 3 4 42.879
24 3 5 37.835
25 3 6 33.289
26 3 7 30.064
27 3 8 27.138
28 3 9 24.967
29 3 10 23.038
30 4 1 79.894
31 4 2 66.645
32 4 3 57.186
33 4 4 50.064
34 4 5 44.298
35 4 6 39.963
36 4 7 36.201
37 4 8 33.507
38 4 9 30.812
39 4 10 28.466
40 5 1 83.283
41 5 2 71.550
42 5 3 62.713
43 5 4 55.711
44 5 5 49.919
45 5 6 45.489
46 5 7 41.779
47 5 8 38.394
48 5 9 35.679
49 5 10 33.524
50 6 1 85.775
51 6 2 74.984
52 6 3 66.658
53 6 4 60.043
54 6 5 54.330
55 6 6 49.834
56 6 7 46.212
57 6 8 42.831
58 6 9 40.023
59 6 10 37.740
60 7 1 87.472
61 7 2 77.576
62 7 3 69.897
63 7 4 63.773
64 7 5 58.175
65 7 6 53.834
66 7 7 50.219
67 7 8 46.754
68 7 9 43.511
69 7 10 41.235
70 8 1 88.769
71 8 2 79.780
72 8 3 72.694
73 8 4 66.335
74 8 5 61.715
75 8 6 57.178
76 8 7 53.487
77 8 8 50.012
78 8 9 46.984
79 8 10 44.524
80 9 1 89.938
81 9 2 81.958
82 9 3 75.107
83 9 4 69.155
84 9 5 64.436
85 9 6 59.974
86 9 7 55.978
87 9 8 52.774
88 9 9 50.242
89 9 10 47.185
90 10 1 90.896
91 10 2 83.411
92 10 3 76.798
93 10 4 71.518
94 10 5 66.650
95 10 6 62.489
96 10 7 58.818
97 10 8 55.756
98 10 9 52.601
99 10 10 50.057

大膽假設: P = x/(x+y)?

驗證: x=1, y=2, p=1/3=33.3%,實驗結果33.460%, x=1, y=3, p=1/4=25.0%,實驗結果25.110%, x=1, y=6, p=1/7=14.3%,實驗結果14.293%, x=1, y=8, p=1/9=11.1%,實驗結果11.211%, ......

通過實驗資料驗證,實驗結果確實接近x/(x+y)。

總結規律,找到公式:

X獲勝概率P=x/(x+y),也就是說獲勝概率等於初始籌碼佔總籌碼的比例