抽象建模的能力
- 建模的第一步是选择合理的数据结构来表述问题。即建立模型。
- 建模的第二步是分析模型中的内在规律,并用编程语言表示这种规律。
- 我们只有对现实问题进行深入细致的观察分析之后,才能找到模型中的规律,才有可能编程解决问题。
题目描述:把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。
解题思路:
- n个骰子的点数和的最小值为n,最大值为6n;
- n个骰子的所有点数的排列为6n;
- 先统计每一个点数出现的次数,然后把每一个点数出现的次数除以6^n,就能求出每个点出现的概率。
解法一:基于递归求骰子点数,时间效率不够高
//解法一,递归
void RecursiveProbability(int original, int current, int sum, int *pProbabilities){
if(current == 1)
pProbabilities[sum - original]++;
else{
for(int i = 1; i <= g_maxValue; ++i)
RecursiveProbability(original, current - 1, i + sum, pProbabilities);
}
}
void RecursiveProbability(int number, int *pProbabilities){
for(int i = 1; i <= g_maxValue; ++i)
RecursiveProbability(number, number, i, pProbabilities);
}
void PrintRecursiveProbability(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;
//先统计每一个点数出现的次数,
RecursiveProbability(number, pProbabilities);
int total = pow((double)g_maxValue, number);
for(int i = number; i <= maxSum; ++i){
//然后把每一个点数出现的次数除以6^n,就能求出每个点出现的概率
double ratio = (double)pProbabilities[ i - number] / total;
printf("%d: %e\n", i, ratio);
}
delete []pProbabilities;
}
解法二:基于循环求骰子点数,时间性能好
//解法二,循环,时间性能好
void PrintLoopProbability(int number){
if(number < 1)
return;
//用两个数组来存储骰子点数的每一个总数出现的次数
int *pProbabilities[2];
pProbabilities[0] = new int[g_maxValue * number + 1];
pProbabilities[1] = new int[g_maxValue * number + 1];
//全部初始化为0
for(int i = 0; i < g_maxValue * number + 1; ++i){
pProbabilities[0][i] = 0;
pProbabilities[1][i] = 0;
}
int flag = 0;
for(int i = 1; i <= g_maxValue; ++i)
pProbabilities[flag][i] = 1;
for(int k = 2; k <= number; ++k){
for(int i = 0; i < k; ++ i)
pProbabilities[1 - flag][i] = 0;
for(int i = k; i <= g_maxValue * k; ++i){
pProbabilities[1 - flag][i] = 0;
for(int j = 1; j <= i && j <= g_maxValue; ++j)
pProbabilities[1 - flag][i] += pProbabilities[flag][i - j];
}
flag = 1 - flag;
}
double total = pow((double)g_maxValue, number);
for(int i = number; i <= g_maxValue * number; ++i){
//然后把每一个点数出现的次数除以6^n,就能求出每个点出现的概率
double ratio = (double)pProbabilities[flag][i] / total;
printf("%d: %e\n", i, ratio);
}
delete []pProbabilities[0];
delete []pProbabilities[1];
}
测试用例:
//测试用例
int main(){
//解法一,递归
PrintRecursiveProbability(6);
std::cout << std::endl;
//解法二,循环
PrintLoopProbability(6);
return 0;
}