版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/buildcourage/article/details/79905550
Lucas 定理
该定理是用来求当
中的
很大而
为素数时,
的值。
Lucas 定理:令
那么:
则在编程时,只需继续对 应用 Lucas 定理即可。代码可以递归地完成这个过程,终止条件是 。时间复杂度为 。
大组合数取余
应用 Lucas 定理,可以将求
的问题转化为求
的问题,因为
可对
直接求组合数,而对
递归使用 Lucas 定理求值。
而对于小组合数有:
对于阶乘很大的情况,计算机编程时可能需要对分子分母分别取余,但是对于除法不能轻易使用同余定理(只在 三种运算下有效),所以希望可以将除法取余转换为等价的乘法取余,这时便需要另外重要的知识:逆元以及费马小定理。
逆元
逆元定义:对于正整数 和 ,如果有 ,那么把这个同余方程中 的最小正整数解叫做 的逆元。
费马小定理
定义:假如 是质数,且 ,即 互质,那么
由费马小定理可得:
因此当 互质且 为素数时,有:
带入组合数公式,即有:
这样便将除数求余转化为同余的乘法求余运算。
这里出现了求幂运算 ,可以使用快速求幂算法
快速求幂
//cpp
LL quickPow(LL n,LL m){
LL ans = 1;
n %= mod;
while(m){
if(m & 1)
ans = ans * n % mod;
n = n * n % mod;
m >>= 1;
}
return ans;
}
对于求阶乘,可以使用一个全局数组缓存结果,这样就不用每次求了
阶乘
//cpp
void getFac(int n){
fac[0]=fac[1]=1;
for(int i=2;i<=n;++i){
fac[i]=fac[i-1] * i % mod;
}
}
下面给出求组合数和Lucas定理的代码实现
求组合数
//cpp
LL C(LL n,LL m){
if(m>n)
return 0;
return fac[n]*quickPow(fac[m]*fac[n-m],mod-2) % mod;
}
Lucas 定理
//cpp
LL Lucas(LL n,LL m){
if(m == 0)
return 1;
return C(n%mod,m%mod)*Lucas(n/mod,m/mod) % mod;
}
参考资料
[1] https://blog.csdn.net/wyg1997/article/details/52152282 “Lucas定理 & 逆元学习小结”
[2] https://baike.baidu.com/item/%E8%B4%B9%E9%A9%AC%E5%B0%8F%E5%AE%9A%E7%90%86 “费马小定理”
[3] https://baike.baidu.com/item/lucas/4326261?fr=aladdin “Lucas 定理”