Given n dice each with m faces, numbered from 1 to m, find the number of ways to get sum X. X is the summation of values on each face when all the dice are thrown.
TheNaive approachis to find all the possible combinations of values from n dice and keep on counting the results that sum to X.
This problem can be efficiently solved usingDynamic Programming (DP).
Let the function to find X from n dice is: Sum(m, n, X) The function can be represented as: Sum(m, n, X) = Finding Sum (X - 1) from (n - 1) dice plus 1 from nth dice + Finding Sum (X - 2) from (n - 1) dice plus 2 from nth dice + Finding Sum (X - 3) from (n - 1) dice plus 3 from nth dice ................................................... ................................................... ................................................... + Finding Sum (X - m) from (n - 1) dice plus m from nth dice So we can recursively write Sum(m, n, x) as following Sum(m, n, X) = Sum(m, n - 1, X - 1) + Sum(m, n - 1, X - 2) + .................... + Sum(m, n - 1, X - m)
Why DP approach?
The above problem exhibits overlapping subproblems. See the below diagram. Also, seethisrecursive implementation. Let there be 3 dice, each with 6 faces and we need to find the number of ways to get sum 8:
Sum(6, 3, 8) = Sum(6, 2, 7) + Sum(6, 2, 6) + Sum(6, 2, 5) + Sum(6, 2, 4) + Sum(6, 2, 3) + Sum(6, 2, 2) To evaluate Sum(6, 3, 8), we need to evaluate Sum(6, 2, 7) which can recursively written as following: Sum(6, 2, 7) = Sum(6, 1, 6) + Sum(6, 1, 5) + Sum(6, 1, 4) + Sum(6, 1, 3) + Sum(6, 1, 2) + Sum(6, 1, 1) We also need to evaluate Sum(6, 2, 6) which can recursively written as following: Sum(6, 2, 6) = Sum(6, 1, 5) + Sum(6, 1, 4) + Sum(6, 1, 3) + Sum(6, 1, 2) + Sum(6, 1, 1) .............................................. .............................................. Sum(6, 2, 2) = Sum(6, 1, 1)
Please take a closer look at the above recursion. The sub-problems inREDare solved first time and sub-problems inBLUEare solved again (exhibit overlapping sub-problems). Hence, storing the results of the solved sub-problems saves time.
For DP:
Time Complexity:O(m * n * x) where m is number of faces, n is number of dice and x is given sum.
We can add following two conditions at the beginning of findWays() to improve performance of program for extreme cases (x is too high or x is too low)
// When x is so high that sum can not go beyond x even when we
// get maximum value in every dice throw.
if
(m*n <= x)
return
(m*n == x);
// When x is too low
if
(n >= x)
return
(n == x);
|
With above conditions added, time complexity becomes O(1) when x >= m*n or when x <= n.
文中的骰子不一定就是6面的,可以是任意面。
package DP;
public class DiceThrow {
public static void main(String[] args) {
System.out.println(findWaysRec(4, 2, 1));
System.out.println(findWaysRec(2, 2, 3));
System.out.println(findWaysRec(6, 3, 8));
System.out.println(findWaysRec(4, 2, 5));
System.out.println(findWaysRec(4, 3, 5));
System.out.println(findWaysDP(4, 2, 1));
System.out.println(findWaysDP(2, 2, 3));
System.out.println(findWaysDP(6, 3, 8));
System.out.println(findWaysDP(4, 2, 5));
System.out.println(findWaysDP(4, 3, 5));
}
// 返回能使组合之和为sum的方法数
// faces:一个dice的最大面值,所有面值为:[1...faces]
// dices:dices的个数,每个dice都可以选择一个face
public static int findWaysRec(int faces, int dices, int sum){
if(sum < 1){ // 总和过小
return 0;
}
if(dices == 1){ // 只有一个dice,判断总和和最大面值哪个大
return sum<=faces ? 1 : 0;
}
int ways = 0;
for(int i=1; i<=faces; i++){
ways += findWaysRec(faces, dices-1, sum-i);
}
return ways;
}
// The main function that returns number of ways to get sum
// Time Complexity: O(m * n * x) where m is number of faces, n is number of dice and x is given sum.
public static int findWaysDP(int faces, int dices, int sum){
int[][] dp = new int[dices+1][sum+1]; //dp[dices][sum]
for(int sum_=1; sum_<=sum; sum_++){ // 只有1个dice且sum大于
if(sum_ <= faces){
dp[1][sum_] = 1;
}
}
for(int dices_=2; dices_<=dices; dices_++){ // dices
for(int sum_=1; sum_<=sum; sum_++){ // sum
for(int faces_=1; faces_<=faces; faces_++){
if(sum_-faces_ >= 0){ // 防越界
dp[dices_][sum_] += dp[dices_-1][sum_-faces_];
}
}
}
}
return dp[dices][sum];
}
}
http://www.geeksforgeeks.org/dice-throw-problem/