面试题60:n个骰子的点数

题目:

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

分析:

n个骰子点数和最小值为n,最大值为6n。根据排列组合,n个骰子的所有点数的排列数为6^{n},需要先统计每个点数出现的次数,再把次数除以6^{n},就能求出每个点数出现的概率。

基于对个求骰子点数,时间效率不够高

先把n个骰子分成两堆:第一堆有1个,第二堆有n-1个。第一堆可能出现1~6的情况,我们需要计算1~6的每一种点数和剩下的n-1个骰子的点数和。接下来把剩下的n-1个骰子仍然分成两堆:第一堆有1个,第二堆有n-2个。把上一轮单独的骰子和这一轮单独的骰子点数相加,再和剩下的n-2个骰子来计算点数和。这是一个递归的思路,递归终止的 条件是最后剩下了一个骰子。

定义一个6n-n+1长度的数组,将和为s的点数出现的次数放到下标为s-n的位置。

基于循环求骰子点数,时间性能好

递归中存在大量的重复计算,理解起来也有难度,我没看懂,我们使用循环的方式解决这个问题。

设f(n,k)表示n个骰子,某次骰子之和为k的次数,要想让n个骰子的结果为k,这个结果和n-1个骰子的结果有联系,那么有6种情况,第n个骰子可能出现1~6的情况,那么前n-1个骰子就要分别出现k-1~k-6的情况,于是f(n,k)=f(n-1,k-1)+f(n-1,k-2)+f(n-1,k-3)+f(n-1,k-4)+f(n-1,k-5)+f(n-1,k-6),这里的n>0,k≤6n。最开始的情况是什么样呢?f(1,1)=f(1,2)=f(1,3)=f(1,4)=f(1,5)=f(1,6)=1。使用递归的思路分析,使用循环的方式,自底向上实现代码求解。

解法:

基于对个求骰子点数,时间效率不够高

package com.wsy;

public class Main {
    public static int[] result;
    public static int maxValue = 6;

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

    public static void printProbability(int n) {
        if (n < 1) {
            return;
        }
        result = new int[n * maxValue - n + 1];
        int maxSum = maxValue * n;
        for (int i = 1; i <= maxValue; i++) {
            probability(n, n, i);
        }
        int total = (int) Math.pow(maxValue, n);
        for (int i = n; i <= maxSum; i++) {
            System.out.println("s=" + i + "\tp=" + (1.0 * result[i - n] / total));
        }
    }

    public static void probability(int original, int current, int sum) {
        if (current == 1) {
            result[sum - original]++;
        } else {
            for (int i = 1; i <= maxValue; i++) {
                probability(original, current - 1, i + sum);
            }
        }
    }
}

基于循环求骰子点数,时间性能好

package com.wsy;

public class Main {
    public static int[] result;
    public static int maxValue = 6;

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

    public static void printProbability(int n) {
        if (n < 1) {
            return;
        }
        result = new int[maxValue * n + 1];
        // 投出第1个骰子
        for (int i = 1; i <= maxValue; i++) {// f(1,1)=f(1,2)=f(1,3)=f(1,4)=f(1,5)=f(1,6)=1
            result[i] = 1;
        }
        for (int i = 2; i <= n; i++) {// 依次投出第i个骰子
            // 从后面向前更新result,因为投出第i个骰子后,[i,i*maxValue]之间的值都要更新,新增了一个骰子,可能构成的情况更多了
            // [1,i]之间的值也要更新,增加了骰子,有的值就取不到了,比如n个骰子,sum<n的情况
            // 后面的值要用到前面的值在n-1时候的状态,如果先更新前面的值,后面的值在计算的时候,前面的值就是n时候的状态了
            for (int j = i * maxValue; j >= 1; j--) {
                int temp = 0;
                // 计算f(n-1,j-1)+f(n-1,j-2)+f(n-1,j-3)+f(n-1,j-4)+f(n-1,j-5)+f(n-1,j-6),注意有的时候j小于6,累加不到j-6,就用0代替
                for (int k = 1; k <= maxValue; k++) {
                    temp += j > k ? result[j - k] : 0;
                }
                result[j] = temp;
            }
        }
        int total = (int) Math.pow(maxValue, n);
        for (int i = n; i <= n * maxValue; i++) {
            System.out.println("s=" + i + "\tp=" + (1.0 * result[i] / total));
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值