题目:把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。

方法一:

       要想求出n个骰子的点数之和,可以先把n个骰子分为两堆:第一堆只有一个,另一堆有n-1个。单独的那一个有可能出现从1到6的点数,需要计算从1到6的每一种点数和剩下的n-1个骰子来计算点数。接下来把剩下的n-1个骰子分成两堆,第一堆只有一个,第二堆有n-2个。重复前面的操作,直到最后只剩下一个骰子。

public class Solution{
    //一个骰子最大点数
    public static final int g_maxValue=6;
    public static void printProbability(int number){
        if(number<1){
            return;
        }
        //所有骰子的最大点数
        int maxSum=number*g_maxValue;
        //数组用来保存每个可能的点数对应的次数
        int[] prob=new int[maxSum-number+1];
        for(int i=number;i<=maxSum;i++){
            prob[i-number]=0;
        }
        //计算个数为number的骰子可能投出对应的次数
        probability(number,prob);
        //个数为number的骰子投出的总次数
        double total=Math.pow(g_maxValue,number);
        //计算每个点数出现的概率
        for(int i=number;i<=maxSum;i++){
            double ratio=(double)prob[i-number]/total;
            System.out.println(i+": "+ratio);
        }
    }

    public static void probability(int number,int[] prob){
       //投出第一个骰子,i为其点数
        for(int i=1;i<=g_maxValue;i++){
             probability(number,number,i,prob);
        }
    }

    public static void probability(int original,int current,int sum,int[] prob){
         //骰子为1个时,退出递归
         if(current==1){
             prob[sum-original]++;
         }else{
             //第curent个的点数=第current-1个的点数+当前点数
             for(int i=1;i<=g_maxValue;i++){
                  probability(original,current-1,i+sum,prob);
             }
         }
    }

    public static void main(String[] args){
         int number=2;
         printProbability(number);
    }
}

方法二:

       考虑用两个数组来储存骰子点数的每一个总数出现的次数。在一次循环中,第一个数组中第n个数字表示骰子和为n出现的次数。在下一循环中,加上一个新的骰子,此时和为n的骰子出现的次数应该等于上一次循环中骰子点数和为n-1,n-2,n-3,n-4,n-5,与n-6的次数的总和,所以把另一个数组的第n个数字设为前一个数组对应的第n-1,n-2,n-3,n-4,n-5与n-6之和。

public class Solution{
     //一个骰子的最大点数
     public static final int g_maxValue=6;
     public static void printProbability(int number){
         if(number<1){
              return;
         }
         //所有骰子的最大点数
         int maxSum=g_maxValue*number;
         //记录两个骰子投出点数对应的次数
         int[][] prob=new int[2][maxSum+1];
         for(int i=0;i<=maxSum;i++){
              prob[0][i]=0;
              prob[1][i]=0;
         }
         int flag=0;
         //初始化第一个骰子投出点数对应的次数
         for(int i=1;i<=g_maxValue;i++){
              prob[flag][i]=1;
         }
         //从第二次开始掷骰子,假设第一个数组中的第n个数字表示骰子和为n出现的次数,
         //在下一循环中,我们加上一个新骰子,此时和为n的骰子出现次数应该等于上一次循环中骰子点数和为n-1,n-2,n-3,n-4,n-5,
         //n-6的次数总和,所以我们把另一个数组的第n个数字设为前一个数组对应的n-1,n-2,n-3,n-4,n-5,n-6之和
         for(int k=2;k<=number;k++){
              //k个骰子最小点数为k,则小于k点出现的次数为0
              for(int i=1;i<k;i++){
                  prob[1-flag][i]=0;
              }
             //第k次掷骰子,和最小为k,最大为g_maxValue*k
              for(int i=k;i<=g_maxValue*k;i++){
                  //初始化,因为这个数组要重复使用,上一次的值要清0
                  prob[1-flag][i]=0;
                  for(int j=1;j<=i&&j<=g_maxValue;j++){
                       prob[1-flag][i]+=prob[flag][i-j];     
                  }
              }
              //切换至另外一个骰子
              flag=1-flag;
         }
         double total=Math.pow(g_maxValue,number);
         for(int i=number;i<=maxSum;i++){
              double ratio=prob[flag][i]/total;
              System.out.println(i+": "+ratio);
         }
    }
}

 

 posted on 2018-12-04 20:40  会飞的金鱼  阅读(233)  评论(0)    收藏  举报