求n個骰子的點數和

2021-10-08 03:37:56 字數 3097 閱讀 8555

把n個骰子仍在地上,所有骰子朝上一面的點數之和為s。輸入骰子個數n,求所有s出現的概率。

最直接的方法, 把所有的情況都列出來, 然後統計出現和的次數, 用 (s出現的次數/6^n) 就是對應的概率.

用乙個數組裝所有的可能,  遞迴呼叫裝滿陣列, 當陣列的大小等於n的時候說明裝滿了, 對陣列計算一次和, 並存入到字典.  然後在處理字典, 用value/6^n 就是對應的概率了.

- (void)viewdidload 

/// n個骰子的點數概率,列舉法

- (void)probability ];

for (nsnumber * key in allkeys)

}/// 遞迴算出每種可能, 返回乙個字典, key是和, value是對應和的數量

/// @param size 可能的結果數量,此處恆等於6

/// @param n 幾個骰子

- (nsdictionary *)__sumallpossibilitywithsize:(int)size count:(int)n

// key是骰子和, value是對應和的數量

在實際測試中, 當n等於7以上的時候就可以感覺到明顯的延遲了, 從開始演算法到結果需要7s左右.  6^7=27936, 大約有28w種結果, 計算起來效率確實不好.

n個骰子的問題還可以看出是乙個動態規劃問題。首先該問題具備dp的兩個特徵:最優子結構性質和子問題的重疊性。具體的表現在:

(1)n個骰子的點數和依賴於n-1個骰子的點數和,相當於在n-1個骰子點數的基礎上再進行投擲一次。

(2)求父問題的同時,需要多次利用子問題。由此定義狀態轉移方程為f(n,k),  f(n,k)表示n個骰子點數和為k時出現的次數,於是可得:

f(n,k)=f(n−1,k−1)+f(n−1,k−2)+f(n−1,k−3)+f(n−1,k−4)+f(n−1,k−5)+f(n−1,k−6)

其中 n>0且k<=6n。其中f(n−1,k−i)表示的是第n-1次擲骰子時,骰子的點數為k-i對應的情況,所以從k−1到k−6分別對應第n次擲骰子時骰子正面為1到6的情況。而初始狀態可以定義為:

f(1,1)=f(1,2)=f(1,3)=f(1,4)=f(1,5)=f(1,6)=1. 

- (void)viewdidload 

/// n個骰子的點數概率,

- (void)probability2

}// [n+1][6*n+1], 由於c語言不太熟練, 就在這裡假設有20個骰子, 用來做快取, 防止遞迴的重複計算

int possibility[21][121]={};

- (int)__getpossibilitywithnum:(int)num size:(int)size count:(int)n

if (n==1&&num>size)

// 這裡就是 f(1,1)=f(1,2)=f(1,3)=f(1,4)=f(1,5)=f(1,6)=1. 

if (n==1&&num<=size)

// 如果已經有快取資料了, 取快取資料, 防止重複計算

if (possibility[n][num] > 0)

// 這裡對應 f(n−1,k−1)+f(n−1,k−2)+f(n−1,k−3)+f(n−1,k−4)+f(n−1,k−5)+f(n−1,k−6)

int sum = 0;

for (int i=1; i<=size; i++)

possibility[n][num] = sum;

return sum;

}

這個演算法就是處理起來就已經快多了, 

沒有使用快取的情況下,計算7個骰子在0.1s以內,計算10個骰子的時間在14s左右,  計算12個骰子大約需要14*36=504秒

使用快取的情況下, 計算7個骰子在0.1s以內, 計算10個骰子的時間在0.5s左右, 計算12個骰子在13s左右,

動態規劃 有遞迴的版本, 自然也有迴圈的版本, 目的是求n個骰子, 我不管求幾個, 都從1個骰子開始計算, 從1計算到n即可.

宣告乙個陣列, result[n][k],表示n個骰子出現和為k的數量 , 然後按照狀態轉移方程進行計算.

f(n,k) = f(n−1,k−1)+f(n−1,k−2)+f(n−1,k−3)+f(n−1,k−4)+f(n−1,k−5)+f(n−1,k−6)

初始值: f(1,1)=f(1,2)=f(1,3)=f(1,4)=f(1,5)=f(1,6)=1. 其他值都設為0.

以n=3,畫圖

/// n個骰子的點數概率, 動態規劃,迴圈

- (void)probability3

// 2個骰子以上,開始計算

for (int i = 2; i<=n; i++)

}double totle = pow(size, n);

for (int i = n; i<=size*n; i++)

}

即使n=20 , 計算也是在0.1s內出完所有結果, 時間複雜度在o(n^2)級別,  比起上面2種方式實在是太優秀了.

但是要說優化嗎? 其實還有有餘地的, 上面使用了乙個[n+1]行[6*n+1]列的陣列,  其實可以優化成2個[6*n+1]的陣列,  2個陣列迴圈相加得到最後的n個骰子.   

但是我覺得在學習理解階段還是用n+1行6*n+1列比較好理解, 可讀性更好.  如果有機會在生產環境中使用, 那就把多維陣列優化成2個陣列.

n個骰子的點數

把n個骰子扔在地上,所有骰子朝上的一面的點數之和為s。輸入n,列印出s的所有可能的值和出現的概率。1 基於迴圈求骰子點數 2int g maxvalue 6 骰子的點數個數 3void printprobability int number number 為骰子個數49 int pprobabili...

n個骰子的點數

題目 把n個骰子扔在地上,所有骰子朝上一面的點數之和為s。輸入n,列印出s的所有可能的值出現的概率。方法一 遞迴 思路 設n個骰子某次投擲點數和為s的出現次數是f n,s 那麼,f n,s 等於n 1個骰子投擲的點數和為s 1 s 2 s 3 s 4 s 5 s 6時的次數的總和 f n s f n...

n個骰子的點數

題目 把n個骰子扔在地上,所有骰子朝上一面的點數之和為s。輸入n,列印出s的所有可能的值出現的概率。方法一 遞迴 思路 設n個骰子某次投擲點數和為s的出現次數是f n,s 那麼,f n,s 等於n 1個骰子投擲的點數和為s 1 s 2 s 3 s 4 s 5 s 6時的次數的總和 f n s f n...