剑指offer--43.n个骰子的点数

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

能力解读:抽象建模,第一步是选择合理的数据结构表述问题,第二步分析模型中的内在规律,并用编程语言表达这种规律

分析:n个骰子点数和的最小值为n,最大值为6n,所有点数的排列数为6^n,因此要解决这个问题,可以先统计求出每一个点数出现的次数,然后把每一个点数出现的次数除以6^n,就能求出每个点数出现的概率

方法一:递归,求得n个骰子的点数和,把n个骰子分为两堆:第一堆只有1个,第二堆有n-1个,第一堆有可能出现1到6的点数,我们需要计算从1到6的每一种点数和剩下的n-1个骰子来计算点数和,接下来把剩下的n-1个骰子分成两堆:第一堆只有1个,第二堆有n-2个,以此类推,递归结束的条件是最后只剩下一个骰子。但是递归实现有很多计算是重复的,从而导致当number变大时性能很慢

方法二:循环,利用两个数组存储骰子点数的每一个总数出现的次数,在一次循环中,第一个数组中的第n个数字表示骰子和为n出现的次数,在下一次循环中,加上新的骰子,则此时和为n的骰子出现的次数等于上一次循环中骰子点数和为n-1,n-2,n-3,n-4,n-5,n-6次数的总和,即f(n)=f(n-1)+f(n-2)+f(n-3)+f(n-4)+f(n-5)+f(n-6)

public class wr43printProbability {
//	递归
	private static final int maxValue=6;
	
	public static void printProbability(int number){//有n个骰子
		if(number<1){
			return ;
		}
		int maxSum=number*maxValue;//和的最大值为6n,最小值为n
		int [] prob=new int[maxSum-number+1];//建立一个长为6n-n+1的数组,和为s的点数出现的次数保存到数组的第s-n个元素里
		for(int i=number;i<=maxSum;i++){
			prob[i-number]=0;
		}
		
		double total=Math.pow(maxValue, number);//n个骰子,每个有6个面,共有6^n个组合
		probability(number,prob);//计算n到6n每种情况出现的次数
		for(int i=number;i<=maxSum;i++){
			double ratio=prob[i-number]/total;
			System.out.println("i: "+i+" ratio: "+ratio);
		}
	}
	
	public static void probability(int number,int []prob){
		for(int i=1;i<=maxValue;i++){//从第一个骰子开始
			probability(number,number-1,i,prob);
		}
	}
//	求得n个骰子的点数和,把n个骰子分为两堆:第一堆只有1个,第二堆有n-1个
//	第一堆有可能出现1到6的点数,我们需要计算从1到6的每一种点数和剩下的n-1个骰子来计算点数和
//	接下来把剩下的n-1个骰子分成两堆:第一堆只有1个,第二堆有n-2个
//	以此类推,递归思路,递归结束的条件是最后只剩下一个骰子
	public static void probability(int original,int current,int sum,int []prob){
//		参数original骰子的总个数,current为当前筛子,sum当前之和,prob是贯穿始终的数组
		if(current==0){
			prob[sum-original]++;
		}
		else{
			for(int i=1;i<=maxValue;i++){
				probability(original,current-1,sum+i,prob);
			}
		}
	}
	
//	循环
	public static void printProbabilityCicle(int number){
		if(number<1){
			return ;
		}
		int maxSum=number*maxValue;
		int [][]prob=new int[2][maxSum+1];
		
		int flag=0;
		for(int i=1;i<=maxValue;i++){
			prob[flag][i]=1;
		}
		for(int k=2;k<=number;k++){
			flag=1-flag;
			for(int i=1;i<k;i++){
				prob[flag][i]=0;
			}
			for(int i=k;i<=maxValue*k;i++){
				int count=1;
				prob[flag][i]=0;
				while(i-count>0 && count<= 6){
					prob[flag][i]+=prob[1-flag][i-count];
					count++;
				}
			}
		}
		double total=Math.pow(maxValue, number);
		for(int i=number;i<=maxSum;i++){
			double ratio=prob[flag][i]/total;
			System.out.println("i: "+i+" ratio: "+ratio);
		}
	}
	public static void main(String []args){
//		printProbability(2);
		printProbabilityCicle(2);
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值