22 k个骰子掷出n的概率

前言

本博文部分图片, 思路来自于剑指offer 或者编程珠玑

问题描述

这里写图片描述

思路

对于这个问题, 书中给出了两种思路
这里 我代码中求解的是给定k表示k个骰子, n表示各个向上的点子和为n, 求解k个骰子投掷出n的概率

思路一 : 穷举..

思路二 : 首先我们已知的条件为1个骰子掷出1, 2, 3, 4, 5, 6的可能性为1种, 然后我们递推 其余k个骰子掷出的2, 3, 4, …的和为(i, i*6)的可能情况的个数
这其实 就是一种自底向上的动态规划[当然 也可以替换为带缓存的自顶向下]

对于2个骰子来说, 投掷出3的可能性为, 一个骰子投掷出1的可能情况个数 * 另外的(2-i)个骰子投出(3-1)的可能情况个数 + 一个骰子投出2的可能情况个数 * 另外的(2-i)个骰子投出(3-2)的可能情况个数 = 1 * 1 + 1 * 1 = 2, 所以 两个骰子掷出3的可能情况有两种, 这样就可以以递推到k个骰子了

参考代码

/**
 * file name : Test18DiceProbability.java
 * created at : 10:53:30 AM Jun 9, 2015
 * created by 970655147
 */

package com.hx.test05;

public class Test18DiceProbability {

    static final int DICE_FACE_NUM = 6;

    // 计算k个骰子, 投出n的概率
    public static void main(String []args) {

        // 计算一个骰子, 投出各种数的次数
        Map<NumToVal, Integer> numToVal = new HashMap<NumToVal, Integer>();
        for(int i=1; i<=DICE_FACE_NUM; i++) {
            numToVal.put(new NumToVal(1, i), 1);
        }

        int k = 2;
        int n = 7;
        calcNumToVal(numToVal, k);
//      Log.log(numToVal);
        if(logical(k, n) ) {
            Log.log(numToVal.get(new NumToVal(k, n)) + " / " + Math.pow(DICE_FACE_NUM, k) );
        } else {
            Log.log("can't be...");
        }
    }

    // i 表示有i个骰子, j表示和为j, cur表示第一个骰子投cur
    // 思路 : i个骰子,投出和为j的概率为, 第1个骰子投cur 属于[curMin, curMax]的时候  剩余的(i-1)个骰子和为(j-cur)的次数
        // 这里已知一个骰子, 投各种和的可能情况为1, 递推, 其余的2, 3, 4, ...的和为(i, i*6)的可能情况的个数
    public static void calcNumToVal(Map<NumToVal, Integer> numToVal, int k) {
        NumToVal tmp = new NumToVal();
        for(int i=2; i<=k; i++) {
            int sum = 0;
            tmp.setDiceNum(i-1);
            int maxVal = i * DICE_FACE_NUM;
            int minVal = i;
            for(int j=minVal; j<=maxVal; j++) {
                int curMin = getMax(j - (i - 1) * 6, 1 );
                int curMax = getMin(j - i + 1, DICE_FACE_NUM);
                sum = 0;
                for(int cur=curMin; cur<=curMax; cur++) {
                    tmp.setVal(j-cur);
                    sum += numToVal.get(tmp);
                }
                numToVal.put(new NumToVal(i, j), sum);
            }
        }
    }

    // 判断给定的n, k输入是否符合逻辑
    private static boolean logical(int k, int n) {
        if((n < k) || (n > DICE_FACE_NUM * k) ) {
            return false;
        }

        return true;
    }

    // 获取x, y的较小者
    private static int getMin(int x, int y) {
        return x > y ? y : x;
    }
    // 获取x, y的较大者
    private static int getMax(int x, int y) {
        return x < y ? y : x;
    }

    // 封装了num个骰子, 和为tar的bean
    static class NumToVal {
        // 骰子的个数, 和为val
        int diceNum;
        int val;

        // 初始化
        public NumToVal() {
            super();
        }
        public NumToVal(int diceNum, int val) {
            this.diceNum = diceNum;
            this.val = val;
        } 

        // setter & getter
        public int getDiceNum() {
            return diceNum;
        }
        public void setDiceNum(int diceNum) {
            this.diceNum = diceNum;
        }
        public int getVal() {
            return val;
        }
        public void setVal(int val) {
            this.val = val;
        }

        // 重写hashCode方法, 用于从集合中取出元素的判定
        public int hashCode() {
            return diceNum * 31 + val;
        }

        // 重写equals方法, 用于从集合中取出元素的判定
        public boolean equals(Object obj) {
            if(!(obj instanceof NumToVal) ) {
                return false;
            }

            NumToVal other = (NumToVal) obj;
            return (this.diceNum == other.diceNum) && (this.val == other.val);
        }

        // Debug
        public String toString() {
            return "diceNum : " + diceNum + "  val : " + val;
        }
    }

}

效果截图

这里写图片描述

总结

其实在当初做这道面试题目的时候, 我虽然了解过动态规划, 但是 仅仅是了解
然而 最近因为啃了很久的算法导论, 看到这个场景 感觉也太像自底向上的dp了吧,,, 哈哈哈 真是人生何处不相逢!

注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值