来自剑指offer
方法一:
n个色子有6^n个组合,只要求每种点数和的组合数,那么概率就迎仍而解了。问题转化成求每种点数和的组合个数。
如何求每一个点数出现的次数呢?我们可以采用递归方法。首先,我们把n个穀子分成两堆,第一堆只有一个,另一堆有n-1个。将第一个堆中的唯一穀子的任意点数与后面n-1穀子的点数和相加。接下来处理第二个堆中的n-1个穀子的点数和。递归处理,直到只有一个穀子结束。最后将每个点数和保存到数组中。
代码:
#include "stdafx.h"
#include <math.h>
#include <iostream>
using namespace std;
//总共original个色子,还有current个色子没有加,当前已经加完的和为sum,色子最大点数为maxValue,将结果放入pProbabilities
void Probability(int original, int current, int sum, int* pProbabilities, int maxValue)
{
if ( current == 1 )
pProbabilities[sum-original]++;
else
{
for ( int j = 1; j <= maxValue; j++ )
Probability(original, current-1, j+sum, pProbabilities, maxValue);
}
}
//有num个点数为manValue的色子,求每种点数和出现的次数
void Probability(int num, int maxValued, int* pProbabilities)
{
int curSum = 0;
for ( int i = 1; i <= maxValued; i++ )
Probability(num, num, i, pProbabilities, maxValued);
}
//有num个点数为maxValue的穀子
void PrintProbability(int num, int maxValue)
{
if ( num < 1 )
return;
int maxSum = num * maxValue;
int* pProbabilities = new int[maxSum-num+1]; //一共有这么多种点数和,将每种点数的组合个数放入该数组中
memset(pProbabilities, 0, sizeof(int)*(maxSum-num+1));
Probability(num, maxValue, pProbabilities);
int total = pow((double)maxValue,num);
double ratio = 0.0;
cout << "每种点数和的概率如下:"<<endl;
for ( int k = num; k <= maxSum; k++ )
{
ratio = (double)pProbabilities[k-num]/total;
cout << k <<"'s ratio " <<ratio<<endl;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
PrintProbability(2,6);
system("pause");
return 0;
}
方法二:
由于上述方法用递归实现,会涉及到很多重复计算,从而导致当num变大时性能无法让我接受。因此,有了方法2.
我们可以考虑用两个数组来存储穀子点数的每一个总数出现的次数。在一次循环中,第一个数组中的第n个数字表示穀子和为n出现的次数。在下一次循环中,我们加上一个新的穀子,此时和为n个穀子出现的次数应该等于上一次循环中穀子点数和为n-1,n-2.....n-6的次数的总和,所以我们把另一个数组的第n个数字设为前一个数组对应的第n-1,n-2.....n-6之和。
代码如下:
#include "stdafx.h"
#include <math.h>
#include <iostream>
using namespace std;
//有num个点数为maxValue的穀子
void PrintProbability(int num, int maxValue)
{
if ( num < 1 )
return;
int* pProbabilities[2];
pProbabilities[0] = new int[num*maxValue+1];
pProbabilities[1] = new int[num*maxValue+1];
memset(pProbabilities[0], 0, sizeof(int)*(num*maxValue+1));
memset(pProbabilities[1], 0, sizeof(int)*(num*maxValue+1));
int flag = 0;
//先将仅有一个色子的情况初始化
for ( int i = 1; i <= maxValue; i++ )
pProbabilities[flag][i] = 1;
//将循环num-1个色子,计算每加一个色子点数分布
for ( int k = 2; k <= num; k++ )
{
//初始化数组,将这次的统计次数放在该数组中
memset(pProbabilities[1-flag], 0, sizeof(int)*k);
for ( int i = k; i <= maxValue*k; i++ )
{
pProbabilities[1-flag][i] = 0;
for ( int j=1; j<=i && j<=maxValue; j++ )
pProbabilities[1-flag][i] += pProbabilities[flag][i-j];
}
flag = 1-flag;
}
int total = pow((double)maxValue,num);
double ratio = 0.0;
cout << "每种点数和的概率如下:"<<endl;
for ( int i = num; i <= maxValue*num; i++ )
{
ratio = (double)pProbabilities[flag][i]/total;
cout << i <<"'s ratio is "<< ratio <<endl;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
PrintProbability(2,6);
system("pause");
return 0;
}