n个骰子的点数

 题目:把n个骰子扔在地上,所有骰子朝上一面的点数之和为s,输入n,打印出s的所有可能的值出现的概率。
解法一:基于递归求骰子点数,时间效率不高。
 现在我们考虑如何统计每一个点数出现的次数.要想求出n个骰子的点数和,可以先把n个骰子分为两堆:第一堆只有一个,另一个有n-1个,单独的那一个有可能出现从1到6的点数.我们需要计算从1到6的每一种点数和剩下的n-1个骰子来计算点数和.接下来把剩下的n-1个骰子还是分成两堆,第一堆只有一个,第二堆有n-2个。我们把上一轮那个单独骰子的点数和这一轮单独筛子的点数相加,再和剩下的n-2个骰子来计算点数和。递归结束的条件就是最后只剩下一个骰子。
基于这种思路,代码参考如下:
 1 int g_maxValue=6;
 2 void PrintProbability(int number)
 3 {
 4   if(number<1)
 5   return;
 6   int maxSum=num*g_maxValue;
 7   int* pProbabilities=new int[maxSum-number+1];
 8   for(int i=number;i<=maxSum;++i)
 9   pProbabilities[i-number]=0;
10   Probability(number,pProbabilities);
11   int total=pow((double)g_maxValue,number);
12   for(int i=number;i<=maxSum;++i)
13   {
14    double ratio=(double)pProbabilities[i-number]/total;
15    printf("%d:%e\n",i,ratio);
16   }
17   delete []probabilities;
18  }
19 
20 void Probability(int number,int* pProbabilities)
21 {
22  for(int i=1;i<=g_maxValue;++i)
23  Probability(number,number,i,pProbabilities);
24  }
25 
26 void Probability(int original,int current,int sum,int* pProbabilities)
27 {
28   if(current==1)
29   { pProbabilities[sum-original]++; }
30  else
31    {
32    for(int i=1;i<=g_maxValue;++i)
33     {Probabilitiey(original,current-1,i+sum,pProbabilities);}
34    }
35  }

上述思路很简洁,实现起来很容易。但由于是基于递归实现,它有很多的计算是重复的,从而导致当number变大时性能慢得让人不能接受.

 

解法二:基于循环求骰子点数,时间性能好
可以换一种思路来解决这个问题。我们可以考虑用两个数组来存储骰子的每一个总数出现的次数。在一次循环中,第一个数组中的第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之和。基于这个思路,参考代码如下:
 1 int g_maxValue=6;
 2 void PrintProbability(int number)
 3 {
 4  if(number<1) return;
 5  int *pProbabilities[2];
 6  pProbabilities[0]= new int[g_maxValue*number+1];
 7  pProbabilities[1]=new int[g_maxValue*number+1];
 8  for(int i=0;i<g_maxValue*number+1;++i)
 9  {
10   pProbabilities[0][i]=0;
11   pProbabilities[1][i]=0;
12  }
13 
14  int flag=0;
15  for(int i=1;i<=g_maxValue;++i)
16   pProbabilities[flag][i]=1;
17 
18  for(int k=2;k<=number;++k)
19  {
20    for(int i=0;i<k;++i)
21     pProbabilities[1-flag][i]=0; 
22    
23     for(int i=k;i<=g_maxValue*k;++i)
24   {
25     pProbabilities[1-flag][i]=0;
26     for(int j=1;j<=i&&j<=g_maxValue;++j)
27      pProbabilities[1-flag][i] +=pProbabilities[flag][i-j];
28   }
29  flag=1-flag;
30  }
31 
32 double total=pow((double)g_maxValue,number);
33   for(int i=number;i<=g_maxValue*number;++i)
34   {
35    double ratio=(double)pProbabilities[flag][i]/total;
36    printf("%d:%e\n",i,ratio);
37   }
38 delete []pProbabilities[0];
39 delete []pProbabilities[1];
40 }
在上述代码中,我们定义了两个数组pProbabilities[0]和pProbabilities[1]来存储骰子的点数之和。在一轮循环中,一个数组的第n项等于另一个数组的第n-1,n-2,n-3,n-4,n-5,n-6项的和。在下一轮循环中,我们交换这两个数组(通过改变变量flag实现)再重复这一计算过程。
值得注意的是,上述代码没有在函数里把一个骰子的最大点数硬编码为6,而是用一个变量g_maxValue来表示。这样做的好处是如果某个厂家生产了其他点数的骰子,我们只需要在代码中修改一个地方,扩展起来很方便。

posted on 2016-06-04 19:57  wxdjss  阅读(368)  评论(0编辑  收藏  举报

导航