《剑指offer》面试题60:n个骰子的点数

题目:n个骰子的点数

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


思路一:基于递归,时间效率不高

递归的思想一般是分而治之,把n个骰子分为第一个和剩下的n-1个。先计算第一个骰子每个点数出现的次数,再计算剩余n-1个骰子出现的点数之和。求n-1个骰子的点数之的方法和前面讲的一样,即再次把n-1个骰子分成两堆------第一个和剩下的n-2个。n个骰子,每个骰子6个面,总共有6n个组合。这6n个组合之中肯定有重复的,我们知道其范围是n~6n,对于每种情况我们可以用缓存机制记录下来,每当其发生一次我们令其对应的单元加1。

我们定义一个长度为6n-n+1的数组,和为s的点数出现的次数保存到数组第s-n个元素里。为什么是6n-n+1呢?因为n个骰子的和最少是n,最大是6n,介于这两者之间的每一个情况都可能会发生,总共6n-n+1种情况。

基于思路一,java参考代码如下:

private static final int g_maxValue = 6;
    //基于递归求骰子点数,时间效率不高
    public static void PrintProbability(int number){
        if(number<1) return;
        int maxSum = number*g_maxValue;
        int[] pProbabilities = new int[maxSum-number+1];
        //初始化,开始统计之前都为0次
        for(int i=number;i<=maxSum;i++){
            pProbabilities[i-number] = 0;
        }
        double total = Math.pow(g_maxValue,number);
        //probability(number,pProbabilities);这个函数计算n~6n每种情况出现的次数
        probability(number,pProbabilities);
        for(int i=number;i<=maxSum;i++){
            double ratio = pProbabilities[i-number]/total;
            System.out.println("i: "+i+" ratio: "+ratio);
        }
    }
    public static void probability(int number,int[] pProbabilities){
        for(int i=1;i<=g_maxValue;i++){//从第一个骰子开始
            probability(number,number,i,pProbabilities);
        }
    }
    //总共original个骰子,当前第 current个骰子,当前的和,贯穿始终的数组
    public static void probability(int original,int current,int sum,int[] pProbabilities){
        if(current==1){
            pProbabilities[sum-original]++;
        }else{
            for(int i=1;i<=g_maxValue;i++){
                probability(original,current-1,sum+i,pProbabilities);
            }
        }
    }

这种方法思路非常简洁,但是递归实现会存在子问题重复求解的情况发生,所以当number很大的时候,其性能会慢的让人不能接受。

思路二:基于循环,时间性能好

递归一般是自顶向下的分析求解,而基于循环的方法则是自底向上。基于循环的一般需要更少的空间和更少的时间,性能较好,但是一般代码比较难懂。

基于以上思路二,java参考代码如下:

//基于循环求骰子点数
    public static void PrintProbability_1(int number){
        if(number<1){
            return;
        }
        int[][] pProbabilities = new int[2][g_maxValue*number +1];
        for(int i=0;i

测试用例:

a.功能测试(1,2,3,4个骰子的各点数的概率)。
b.特殊输入测试(输入0)。
c.性能测试(输入较大的数字,如11)。

参考:

http://www.cnblogs.com/xuanxufeng/p/6896569.html

你可能感兴趣的:(剑指offer,笔记)