计算C(n,m)
组合数的计算,由于格式问题Cnm(排列组合的那个符号)展示不出来,只能放入截图介绍
书中介绍了三种方法,第一种太简单了就是上面的定义式用代码写三个for,此处略过,记录后两种:
直接给出思想和算法
思想:
方法一:
通过递推公式计算:
图中末尾公式即算法核心可以用递归算法计算出来。
代码:
方法一(1):
long long C(long long n,long long m){
if(m == 0 || m == n) return 1;
return C(n - 1,m) + C(n - 1,m - 1);
}
但是方法一会产生一个问题:重复计算,所以有了改进的方法一。
代码:
方法一(2):
long long res[67][67] = {0};
long long C(long long n,long long m){
if(m == 0 || m == n) return 1;
if(res[n][m] != 0) return res[n][m];
return res[n][m] = C(n-1,m) + C(n-1,m-1);//赋值给res[n][m]并返回
}
介绍完了方法一再来看
方法二
通过定义式变形来计算
思想:
由此可以写出代码,显然时间复杂度是O(m):
//方法二:通过定义式的变形来计算
long long C(long long n,long long m){
long long ans = 1;
for(long long i=1;i <= m;i++){
ans = ans * (n-m+i) / i;//注意一定要先乘再除
}
return ans;
}
不过,这两个算法都有不足的地方,方法一在n=67、m=33时开始溢出,方法二在n=62、m=31时开始溢出,但是不管怎样,这两种方法足够代替蛮力求解。
计算C(n,m) % p
这里只介绍一种方法,基于第一个问题的方法一,是最容易,最实际的一种。只要在原先的代码中适当的地方取模就好。在这种做法下,算法可以很好的支持m<=n<=1000的情况,并且对p的大小和素性没有额外的限制(p<=109都可以)
代码:
int res[1010][1010] = {0};
int C(int n,int m,int p){
if(m == 0 || m == n) return 1;//C(n,0) = C(n,n)=1
if(res[n][m] != 0) return res[n][m];//已经有值
return res[n][m] = (C(n-1,m) + C(n-1,m-1)) % p;//赋值给res[n][m]并返回
}