计算组合数的算法
来源:互联网 发布:使命召唤4 mac 下载 编辑:程序博客网 时间:2024/05/15 20:43
概率论是统计分析的基础,而统计分析在很多领域比如人工智能,生物信息学中作为理论基础,具有广泛的应用场景。组合数在概率论中常用,本文就对组合数的数值计算给出一个C++求解的算法。通过实现该算法,对数值计算上的某些方法加深的认识。
首先,来看一下组合数的公式:
通过该公式,首先会想到使用暴力求解法来计算。但阶乘的计算,很容易就会造成数值的溢出,因此暴力求解法在数值稍大的情况下就无法适用。
紧接着,想到常用的处理溢出的方法,利用转换成对数进行运算,使用该种方法可将连乘转换成连加,溢出问题是解决了,但精度上却又不能满足要求。
于是,需要探索其他的方法来解决这一问题。考虑到分子分母均为阶乘,如果对分子分母分别分解质因数,然后对共同的质因数进行约分,最终剩下的质因数的乘积就是我们最终要求的结果。使用这一方法的好处,还在于我们可以通过记录质因数及质因数存在的次数来记录最终的结果,而不必直接记录最终的结果,这样就可以同时解决溢出问题和精度问题。
在本文最后,我附上了具体的C++实现的代码。在看代码之前,我们先将其中的核心部分进行一下解读与分析。
1.初始化素数动态数组(C++的动态数组可以用STL中的向量来实现)。在判断某个数是否素数时,我们只需要轮循到该数的平方根即可,这将大大减少内层循环的算法复杂度。
2.n的阶乘里面含有素数的次数。因为n!表示n(n-1)(n-2)…1,因此如果n>=x(素数),则必然至少存在n/x次素数x,并且这n/x个数除以素数x之后得到的新的排列中,又会又(n/x)/x次素数x。换句话说,素数x的所有次数,均会包含在第一次循环能够整除x的这些数中,而这些数中有的可能包括多次的素数x。因此,通过getPrimePow方法,则得到了n!里面含有素数x的所有次数,记为pow返回。
3.计算组合数。根据组合数的公式,可以看到,通过计算n!中含有某个素数的次数,m!中含有这个素数的次数和(n-m)!中含有素数的次数,就可以得到最终结果中应该含有几个该素数。
4.将结果保存在C++ STL中的Map结构中。Map的key使用素数做下标即可,值即为该素数在最终结果中的次数。为防止溢出,最终结果用该种方式保存即可。
具体的算法如下:
#ifndef _COMBINE_CALCULATOR_H_#define _COMBINE_CALCULATOR_H_#include <vector>#include <map>#include <iostream>using namespace std;class CombineCalculator{public: CombineCalculator(); ~CombineCalculator(); //计算组合数,C(n,m) bool calculateCombine(int n, int m); //打印结果 void printResult();private: //计算n!中含有素数x的次数 int getPrimePow(int n, int x); //生成素数 void generatePrime(); //存放素数动态数组 vector<int> primeVec; //结果Map,key为素数,value为该素数的次数 map<int, int> resultMap; const int MAXN = 1000; const int MAXPRIME = 1000;};#endif
#include "CombineCalculator.h"CombineCalculator::CombineCalculator(){ generatePrime();}CombineCalculator::~CombineCalculator(){}void CombineCalculator::generatePrime(){ for (int i = 2; i < MAXPRIME; i++) { bool isPrime = true; int s = sqrt(i); for (int j = 2; j <= s; j++) { if (i%j == 0) { isPrime = false; break; } } if (isPrime) { primeVec.push_back(i); } } return;}int CombineCalculator::getPrimePow(int n, int x){ int pow = 0; while (n >= x) { int temp = n / x; pow += temp; n = temp; } return pow;}bool CombineCalculator::calculateCombine(int n, int m){ bool result = true; if (n > MAXN) { result = false; } else { for (int i = 0; i < primeVec.size(); i++) { int pow = getPrimePow(n, primeVec[i]) - getPrimePow(m, primeVec[i]) - getPrimePow(n - m, primeVec[i]); if (pow != 0) resultMap.insert(std::pair<int,int>(primeVec[i], pow)); } } return result;}void CombineCalculator::printResult(){ map<int, int>::const_iterator iter; for (iter = resultMap.begin(); iter != resultMap.end(); iter++) { cout << "Key:" << iter->first << "Value:" << iter->second << endl; }}
- 计算组合数的算法
- 组合数的计算
- 组合数的计算
- 组合数的计算
- Knuth的计算组合数的较高精度算法
- 计算组合的算法
- 编程算法 - 计算一个数的所有组合数 代码(C++)
- 编程算法 - 计算一个数的所有组合数 代码(C++)
- 组合数计算问题的总结
- [Java] 计算组合数的代码
- 组合数C(n,m)的计算
- 2种计算组合数的方法
- 计算组合数的几种方法
- C# 计算排列组合数,及列出所有组合形式的算法
- 组合数计算技巧
- 计算组合数
- 3836. 计算组合数
- 3836. 计算组合数
- c和c++区别(一)——默认值、inline、cosnt和引用
- TensorFlow 人脸识别网络与对抗网络搭建
- vi编辑器常用的操作
- Git Cheat Sheet
- codeforces#839 B Game of the Rows 思维,贪心
- 计算组合数的算法
- const的巧用
- DB2书籍推荐!
- 开始前端-----第五篇
- BZOJ1492: [NOI2007]货币兑换Cash 【dp + CDQ分治】
- 浅析extern“C”
- 自旋锁和互斥锁的区别
- c语言 简单数组的输出代码
- Python中线程的创建方法:派生Thread子类