题目
把n个骰子仍在地上,所有骰子朝上一面的点数之和为s.输入n,打印出s的所以可能的值出现的概率。(骰子有六个面)
思路
N个骰子最小的点数为n,最大的点数为6n,一共有6n-n+1种可能。
总的可能的次数为6^n
思路一
基于递归的思路
把n个骰子分为1个和n-1个,对于一个骰子来说,它有1-6六种可能。
然后把n-1个骰子在分为1个和n-1个,这一个也有六中可能,加上上一次的一个骰子出现的点数。
所以可以基于递归写出代码,因为有重复计算,所以时间复杂度高。
思路二
基于循环的思路
对于一个骰子,1,2,3,4,5,6出现的次数都为1.
对于两个骰子,也就是新增加一个骰子,对于5出现的次数,就是一个骰子时4出现的次数+一个骰子时3出现的次数+一个骰子时2出现次数+一个骰子时1出现次数。‘
对于n个骰子,对于出现的次数,就等于n-1个骰子时k-1出现次数+n-1个骰子时k-2出现次数+……+n-1个骰子时k-6出现次数。
基于这个思想,用两个数组来表示骰子出现的次数。
代码
思路一
public static void printProbability(int number){
int[] datas = new int[6 * number - number + 1];
for(int i = 0;i < datas.length;i++)datas[i] = 0;
probability(number,datas);
int total = (int)Math.pow(6,number);
for(int i = 0;i < datas.length;i++){
double probability = (double)datas[i] / total;
System.out.println(i+number + " : " + probability );
}
}
public static void probability(int number,int[] datas){
for(int i = 1;i <= 6;i++){
probability(number,number,i,datas);
}
}
public static void probability(int original,int current,int sum,int[] datas){
if(current == 1){
datas[sum - original]++;
}else {
for(int i = 1;i <= 6;i++){
probability(original,current - 1,i + sum,datas);
}
}
}
思路二
public static void printProbability(int number){
int[][] datas = new int[2][6 * number + 1];
for(int i = 0;i < datas[0].length;i++){
datas[0][i] = 0;
datas[1][i] = 0;
}
int flag = 0;
for(int i = 1;i <= 6;i++){
datas[flag][i] = 1;
}
for(int k = 2;k <= number;k++){
for(int i = 0;i < k;i++){
datas[1 - flag][i] = 0;
}
for(int i = k;i <= 6 * k;i++){
datas[1 - flag][i] = 0;
for(int j = 1;j <= i && j <= 6;j++){
datas[1 - flag][i] += datas[flag][i - j];
}
}
flag = 1 - flag;
}
int total = (int)Math.pow(6,number);
for(int i = number;i <= 6 * number;i++){
double probability = (double)datas[flag][i] / total;
System.out.println(i + " : " + probability);
}
}
总结
本题不管是基于递归与基于循环的解法,都需要有一定的抽象建模的能力,可以先举例按步计算骰子个数较少的情况,模拟计算机计算过程,发现计算过程的规律,然后在编程。