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.
The Naive approach is 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 using Dynamic 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, see this recursive 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 in RED are solved first time and sub-problems in BLUE are 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/