引言:写这篇文章的初衷只是想做个笔记,因为这道题代码量有点大,有点抽象,而书上并没有详细的注释。为了加深印象和便于下次复习,做个记录。

原题:把n个骰子扔到地上,所有骰子朝上一面的点数之后为s. 输入n,打印出s所有可能的值出现的概率。(每个骰子6个面,点数从1到6)

解法一:基于递归,时间效率不高

递归的思想一般是分而治之,把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<g_maxValue;i++){//初始化数组
pProbabilities[0][i] = 0;
pProbabilities[1][i] = 0;
}
int flag = 0;
for(int i=1;i<=g_maxValue;i++){//当第一次抛掷骰子时,有6种可能,每种可能出现一次
pProbabilities[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++){
for(int i=0;i<k;i++){//第k次掷骰子,和最小为k,小于k的情况是不可能发生的!所以另不可能发生的次数设置为0!
pProbabilities[1-flag][i] = 0;
}
for(int i=k;i<=g_maxValue*k;i++){//第k次掷骰子,和最小为k,最大为g_maxValue*k
pProbabilities[1-flag][i] = 0;//初始化,因为这个数组要重复使用,上一次的值要清0
for(int j=1;j<=i&&j<=g_maxValue;j++){
pProbabilities[1-flag][i] += pProbabilities[flag][i-j];
}
}
flag = 1-flag;
}
double total = Math.pow(g_maxValue, number);
for(int i=number;i<=g_maxValue*number;i++){
double ratio = pProbabilities[flag][i]/total;
System.out.println("sum: "+i+" ratio: "+ratio);
}
}

运行结果:

sum: 6 ratio: 2.143347050754458E-5
sum: 7 ratio: 1.286008230452675E-4
sum: 8 ratio: 4.501028806584362E-4
sum: 9 ratio: 0.0012002743484224967
sum: 10 ratio: 0.002700617283950617
sum: 11 ratio: 0.005401234567901234
sum: 12 ratio: 0.00977366255144033
sum: 13 ratio: 0.016203703703703703
sum: 14 ratio: 0.02488425925925926
sum: 15 ratio: 0.03570816186556927
sum: 16 ratio: 0.048161008230452676
sum: 17 ratio: 0.061213991769547324
sum: 18 ratio: 0.07353823731138547
sum: 19 ratio: 0.08371913580246913
sum: 20 ratio: 0.09047067901234568
sum: 21 ratio: 0.09284979423868313
sum: 22 ratio: 0.09047067901234568
sum: 23 ratio: 0.08371913580246913
sum: 24 ratio: 0.07353823731138547
sum: 25 ratio: 0.061213991769547324
sum: 26 ratio: 0.048161008230452676
sum: 27 ratio: 0.03570816186556927
sum: 28 ratio: 0.02488425925925926
sum: 29 ratio: 0.016203703703703703
sum: 30 ratio: 0.00977366255144033
sum: 31 ratio: 0.005401234567901234
sum: 32 ratio: 0.002700617283950617
sum: 33 ratio: 0.0012002743484224967
sum: 34 ratio: 4.501028806584362E-4
sum: 35 ratio: 1.286008230452675E-4
sum: 36 ratio: 2.143347050754458E-5

《剑指offer》 面试题43 n个骰子的点数 (java)的更多相关文章

  1. 剑指offer 面试题43. 1~n整数中1出现的次数

    leetcode上也见过一样的题,当时不会做 看了一下解法是纯数学解法就没看,结果剑指offer上也出现了这道题,那还是认真看下吧 对于数字abcde,如果第一位是1,比如12345,即计算f(123 ...

  2. 剑指offer面试题43:n个筛子的点数

    题目描述: 把n个筛子扔在地上,所有筛子朝上的一面点数之和为s,输入n,打印出s的所有可能的值出线的概率. 书上给了两种解法,第一种递归的方法由于代码太乱,没有看懂=.= 第二种方法很巧妙,lz已经根 ...

  3. 剑指Offer面试题43(Java版):n个骰子的点数

    题目:把n个骰子仍在地上.全部骰子朝上一面的点数之和为s,输入n,打印出s的全部可能的值出现的概率. 解法一:基于递归求骰子的点数,时间效率不够高 如今我们考虑怎样统计每个点数出现的次数. 要向求出n ...

  4. 剑指Offer:面试题27——二叉搜索树与双向链表(java实现)

    问题描述: 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表.要求不能创建任何新的结点,只能调整树中结点指针的指向. 思路: 将树分为三部分:左子树,根结点,右子树. 1.我们要把根结点与左 ...

  5. 剑指Offer:面试题21——包含min函数的栈(java实现)

    问题描述: 定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min函数.在该栈中,调用min,push及pop的时间复杂度都是O(1). 思路:加入一个辅助栈用来存储最小值集合 (这里要注 ...

  6. 剑指Offer:面试题8——旋转数组的最小值(java实现)

    题目描述: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入 一个递增排序的数组的一个旋转 输出 旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的 ...

  7. 剑指offer面试题5:逆序打印单链表(Java)

    Java创建单链表(头插法.尾插法),并逆序打印单链表: package day_0324; import java.util.Scanner; import java.util.Stack; cla ...

  8. 剑指Offer:面试题15——链表中倒数第k个结点(java实现)

    问题描述 输入一个链表,输出该链表中倒数第k个结点.(尾结点是倒数第一个) 结点定义如下: public class ListNode { int val; ListNode next = null; ...

  9. 剑指offer面试题3 二维数组中的查找(c)

    剑指offer面试题三:

  10. 剑指Offer——笔试题+知识点总结

    剑指Offer--笔试题+知识点总结 情景回顾 时间:2016.9.23 12:00-14:00 19:00-21:00 地点:山东省网络环境智能计算技术重点实验室 事件:笔试 注意事项:要有大局观, ...

随机推荐

  1. 20172319 实验三 《敏捷开发与XP实践》 实验报告

    20172319 2018.05.17-30 实验三 <敏捷开发与XP实践> 实验报告 课程名称:<程序设计与数据结构> 学生班级:1723班 学生姓名:唐才铭 学生学号:20 ...

  2. 团队冲刺——Four

    今日计划: 司宇航:网页生成桌面图标.如何将web项目上线部署到公网.测试与优化其他功能. 季方 :处理爬虫获取的数据. 马佳慧:研究css模板. 王金萱:登录注册界面. 遇到的问题:季方:爬虫获取的 ...

  3. java 线程的简单理解

    想要实现线程可以继承Thread也可以实现接口runnable,在类中重写 run()方法在主函数调用start方法就可以开辟线程. 对于java对象都有一个wait()和notify().notif ...

  4. java中static使用之静态方法注意点

    1.静态方法可以直接调用同类中的静态成员,但是不能直接调用非静态成员,这是为什么呢?大家想一下,静态成员在对象创建之前就要写入内存,所以它在内存中是实实在在的存在的,而非静态还不存在内存中,所以不能调 ...

  5. JS学习:JavaScript的核心

    分享到 分类 JS学习   发布 ourjs  2013-12-02 注意 转载须保留原文链接,译文链接,作者译者等信息.     作者: JeremyWei  原文: JavaScript The ...

  6. apache 2.4目录权限

    apache 2.4 好象不再支持以下指令...Order allow,denyAllow from all 用上面的指令访问页面时显示错误:client denied by server confi ...

  7. vue 选项卡(转载)

    !DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-e ...

  8. datatables 排序 如何禁止

    $.extend( true, $.fn.dataTable.defaults, {     "searching": false,     "ordering" ...

  9. jQuery之_事件绑定与解绑

    使用jQuery实现事件的绑定和解绑 就是所谓的事件操作. 1. 事件绑定(2种): * eventName(function(){}) 绑定对应事件名的监听, 例如:$('#div').click( ...

  10. 软工网络15团队作业8——Beta阶段敏捷冲刺(Day5)

    提供当天站立式会议照片一张 每个人的工作 1.讨论项目每个成员的昨天进展 赵铭: 进一步数据整理,写入数据库. 吴慧婷:主页面.查单词页面的改进.背单词界面改进. 陈敏: 单词学习功能及该界面按钮功能 ...