把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...