剑指Offer+第43题+n个筛子的点数+java

原题:把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种情况。

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

解法二:基于循环,时间性能好(还是没有完全搞懂,自己写还是不行)

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

代码:

public class Offer43 {

	private static final int maxValue = 6;
	//方法1, 递归,效率低
	public static void printProbability(int number){
		if(number <1)
			return ;
		int maxSum = number * maxValue;
		int [] probabilities = new int[maxSum - number+1];
		for(int i = number;i <= maxSum;i++)
			probabilities[i-number] = 0;//初始化,开始统计之前都为0次
		
		double total = Math.pow(maxValue, number);
		probability(number, probabilities);
		//probability(number,pProbabilities);这个函数计算n~6n每种情况出现的次数
		for(int i = number;i<=maxSum;i++){
			double ratio = probabilities[i-number]/total;
			System.out.println("i: "+ i+" ratio: "+ratio);
		}
	}
	
	public static void probability(int number, int[] probabilities) {
		for(int i = 1;i<= maxValue;i++){
			probability(number, number, i, probabilities);
		}
	}

	//总共original个骰子,当前第 current个骰子,当前的和,贯穿始终的数组
	public static void probability(int original, int current, int sum,int[] probabilities) {
		if(current == 1)
			probabilities[sum-original]++;
		else
			for(int i = 1;i<=maxValue;i++)
				probability(original, current-1, sum+i, probabilities);
	}
	
	//方法2, 基于循环,性能好
	public void printProbability_2(int number){
		if(number<1)
			return;
		int [][] probabilities = new int[2][maxValue* number+1];
		for(int i = 0;i<maxValue;i++){//初始化数组
			probabilities[0][i] = 0;
			probabilities[1][i] = 0;
		}
		int flag = 0;
		for(int i = 1;i<=maxValue;i++){//当第一次抛掷骰子时,有6种可能,每种可能出现一次
			probabilities[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!
				probabilities[1-flag][i] = 0;
			for(int i =k;i<= maxValue*k;i++){//第k次掷骰子,和最小为k,最大为g_maxValue*k
				probabilities[1-flag][i] = 0;//初始化,因为这个数组要重复使用,上一次的值要清0
				for(int j = 1;j<=i && j<=maxValue;j++)
					probabilities[1-flag][i] += probabilities[flag][i-j];
			}
			flag = 1-flag;
		}
		
		double total = Math.pow(maxValue, number);
		for(int i = number;i<=maxValue*number;i++){
			double ratio = probabilities[flag][i]/total;
			System.out.println("sum: "+i+"ratio: "+ratio);
		}
	}

	public static void main(String[] args) {
		
		Offer43 of43 = new Offer43();
		
		//功能测试,1,1个筛子的各点数的概率
		int number1 = 1;
		//of43.printProbability(number1);
		//of43.printProbability_2(number1);
		
		//功能测试,2,2个筛子的各点数的概率
		int number2 = 2;
		//of43.printProbability(number2);
		//of43.printProbability_2(number2);
		
		//功能测试,3,2个筛子的各点数的概率
		int number3 = 3;
		//of43.printProbability(number3);
		//of43.printProbability_2(number3);
		
		
		
		//功能测试,4,2个筛子的各点数的概率
		int number4 = 4;
		//of43.printProbability(number4);
		//of43.printProbability_2(number4);
		
		//特殊输入测试,5,输入0
		int number5 = 0;
		//of43.printProbability(number5);
		//of43.printProbability_2(number5);
		
		//性能测试,6,输入比较大的数字,如11
		int number6 = 11;
		//of43.printProbability(number6);
		of43.printProbability_2(number6);

	}

}

运行结果:

i: 1 ratio: 0.16666666666666666
i: 2 ratio: 0.16666666666666666
i: 3 ratio: 0.16666666666666666
i: 4 ratio: 0.16666666666666666
i: 5 ratio: 0.16666666666666666
i: 6 ratio: 0.16666666666666666

i: 2 ratio: 0.027777777777777776
i: 3 ratio: 0.05555555555555555
i: 4 ratio: 0.08333333333333333
i: 5 ratio: 0.1111111111111111
i: 6 ratio: 0.1388888888888889
i: 7 ratio: 0.16666666666666666
i: 8 ratio: 0.1388888888888889
i: 9 ratio: 0.1111111111111111
i: 10 ratio: 0.08333333333333333
i: 11 ratio: 0.05555555555555555
i: 12 ratio: 0.027777777777777776

i: 3 ratio: 0.004629629629629629
i: 4 ratio: 0.013888888888888888
i: 5 ratio: 0.027777777777777776
i: 6 ratio: 0.046296296296296294
i: 7 ratio: 0.06944444444444445
i: 8 ratio: 0.09722222222222222
i: 9 ratio: 0.11574074074074074
i: 10 ratio: 0.125
i: 11 ratio: 0.125
i: 12 ratio: 0.11574074074074074
i: 13 ratio: 0.09722222222222222
i: 14 ratio: 0.06944444444444445
i: 15 ratio: 0.046296296296296294
i: 16 ratio: 0.027777777777777776
i: 17 ratio: 0.013888888888888888
i: 18 ratio: 0.004629629629629629

i: 4 ratio: 7.716049382716049E-4
i: 5 ratio: 0.0030864197530864196
i: 6 ratio: 0.007716049382716049
i: 7 ratio: 0.015432098765432098
i: 8 ratio: 0.02700617283950617
i: 9 ratio: 0.043209876543209874
i: 10 ratio: 0.06172839506172839
i: 11 ratio: 0.08024691358024691
i: 12 ratio: 0.09645061728395062
i: 13 ratio: 0.10802469135802469
i: 14 ratio: 0.11265432098765432
i: 15 ratio: 0.10802469135802469
i: 16 ratio: 0.09645061728395062
i: 17 ratio: 0.08024691358024691
i: 18 ratio: 0.06172839506172839
i: 19 ratio: 0.043209876543209874
i: 20 ratio: 0.02700617283950617
i: 21 ratio: 0.015432098765432098
i: 22 ratio: 0.007716049382716049
i: 23 ratio: 0.0030864197530864196
i: 24 ratio: 7.716049382716049E-4

(输入0,没有输出)

i: 11 ratio: 2.7563619479867003E-9
i: 12 ratio: 3.0319981427853706E-8
i: 13 ratio: 1.8191988856712222E-7
i: 14 ratio: 7.883195171241963E-7
i: 15 ratio: 2.759118309934687E-6
i: 16 ratio: 8.27735492980406E-6
i: 17 ratio: 2.2042626498049643E-5
i: 18 ratio: 5.3272207368738955E-5
i: 19 ratio: 1.1861176734576369E-4
i: 20 ratio: 2.4595568934274924E-4
i: 21 ratio: 4.7890410665294927E-4
i: 22 ratio: 8.811620566182324E-4
i: 23 ratio: 0.0015397396168506947
i: 24 ratio: 0.0025654645885549855
i: 25 ratio: 0.004089195255211773
i: 26 ratio: 0.006252344010200568
i: 27 ratio: 0.009191017250151005
i: 28 ratio: 0.013014366908203356
i: 29 ratio: 0.017779303589497704
i: 30 ratio: 0.02346520970666311
i: 31 ratio: 0.029953382532409523
i: 32 ratio: 0.037016300926102336
i: 33 ratio: 0.044321114887988504
i: 34 ratio: 0.05144967328511067
i: 35 ratio: 0.05793451091290002
i: 36 ratio: 0.06330709034198999
i: 37 ratio: 0.06715181558694898
i: 38 ratio: 0.0691574823584015
i: 39 ratio: 0.0691574823584015
i: 40 ratio: 0.06715181558694898
i: 41 ratio: 0.06330709034198999
i: 42 ratio: 0.05793451091290002
i: 43 ratio: 0.05144967328511067
i: 44 ratio: 0.044321114887988504
i: 45 ratio: 0.037016300926102336
i: 46 ratio: 0.029953382532409523
i: 47 ratio: 0.02346520970666311
i: 48 ratio: 0.017779303589497704
i: 49 ratio: 0.013014366908203356
i: 50 ratio: 0.009191017250151005
i: 51 ratio: 0.006252344010200568
i: 52 ratio: 0.004089195255211773
i: 53 ratio: 0.0025654645885549855
i: 54 ratio: 0.0015397396168506947
i: 55 ratio: 8.811620566182324E-4
i: 56 ratio: 4.7890410665294927E-4
i: 57 ratio: 2.4595568934274924E-4
i: 58 ratio: 1.1861176734576369E-4
i: 59 ratio: 5.3272207368738955E-5
i: 60 ratio: 2.2042626498049643E-5
i: 61 ratio: 8.27735492980406E-6
i: 62 ratio: 2.759118309934687E-6
i: 63 ratio: 7.883195171241963E-7
i: 64 ratio: 1.8191988856712222E-7
i: 65 ratio: 3.0319981427853706E-8
i: 66 ratio: 2.7563619479867003E-9

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值