CHAPTER_5 数学问题入门
5.8.1关于n!的问题
n!表示n的阶乘,即 。
我们来讨论一个问题:求n!中有多少个质因子p。例如 。于是6!中有4个质因子2,因为2、4、6中各有1个2、2个2、1个2。
对于这个问题,直观的想法是计算从1~n每个数各有多少个质因子。即从2枚举到n,统计每个数中质因子p的个数。但是这种做法对n很大的情况是无法承受的,我们下面探讨更优的算法。
我们来看一个例子:10!质因子2的个数。

上例中,10!中有因子 的数的个数为5,有因子
的数的个数为2,有因子
的数的个数为1。因此10!质因子2的个数为5+2+1=8。
仔细思考可以将上面过程推广为一个结论:n!中有 个质因子p,其中除法均为向下取整。于是得到了O(logn)的算法,代码如下:
int cal(int n,int p) {
int sum=0;
while(n) {
sum+=n/p;
n/=p;
}
return sum;
}
5.8.2组合数的计算(1)
组合数 是指从n个不同元素中选择m个元素的方案,也可写成C(n,m)。其计算式为
。
这里还要提及组合数的两个性质:(1) ;(2)
。
本节我们讨论组合数的第一个问题:如何计算 。下面给出3种方法。
方法1:通过计算式直接计算
,我们先计算n!,然后令其分别除以m!和(n-m)!。显然,由于阶乘的庞大,这种方式计算组合数能接受的数据范围会很小,即使用long long类型也只能接受n<=20的数据范围。此法很容易造成溢出。
方法2:通过递推公式计算
首先我们要介绍一个递推公式: 。有了递推式,我们还需要确定递归边界。从直观上看,这个公式总是把n减一,而把m保持原样或者也减一,这样这个递推式最终可以把n和m变得相同,或是把m变成0。根据上面介绍的性质
,我们得以确定边界。代码如下:
long long cal(long long n,long long m) {
if(n==m||m==0)
return 1;
else
return cal(n-1,m)+cal(n-1,m-1);
}
实际上,上面的算法还可以优化。 在上面的递归时,会有很多C(n,m)是重复计算过的。我们用一个数组来记录C(n,m)计算的结果,如果下一次碰到就不用再重复计算了。
long long res[500][500]={0};
long long cal(long long n,long long m) {
if(n==m||m==0)
return 1;
else if(res[n][m])
return res[n][m];
else {
res[n][m]=cal(n-1,m)+cal(n-1,m-1);
return res[n][m];
}
}
方法3:通过定义式的变形来计算

该算法时间复杂度为O(m),代码如下:
long long cal(long long n,long long m) {
long long sum=1;
for(long long i=1;i<=m;i++) {
sum=sum*(n-m+i)/i; //一定要先乘后除,才能保证结果为整数
}
return sum;
}
这篇博客探讨了计算阶乘和组合数的优化算法。对于阶乘问题,提出了利用质因数p的个数来计算n!中质因子p的个数,实现了O(logn)的时间复杂度。对于组合数C(n,m)的计算,提供了三种方法:直接计算(易溢出)、递推公式(动态规划优化)和定义式变形。这些算法对于大数值的计算具有更高的效率。
226

被折叠的 条评论
为什么被折叠?



