[算法 18_001] Lucas 定理与大组合数取余

Lucas 定理

该定理是用来求当 (nm) ( n m ) 中的 m,n m , n 很大而 p p 为素数时,(nm)(modp) 的值。
Lucas 定理:令 n=sp+q,m=tp+r.(q,r<p) n = s p + q , m = t p + r . ( q , r < p )
那么:

(nm)(sp+qtp+r)(st)(qr)(modp) ( n m ) ≡ ( s p + q t p + r ) ≡ ( s t ) ( q r ) ( mod p )

则在编程时,只需继续对 (st) ( s t ) 应用 Lucas 定理即可。代码可以递归地完成这个过程,终止条件是 t=0 t = 0 。时间复杂度为 O(logpnp) O ( log p ⁡ n ∗ p )

大组合数取余

应用 Lucas 定理,可以将求 (nm)(modp) ( n m ) ( mod p ) 的问题转化为求 (qr)(st)(modp) ( q r ) ( s t ) ( mod p ) 的问题,因为 q,r<p q , r < p 可对 (qr)(modp) ( q r ) ( mod p ) 直接求组合数,而对 (st)(modp) ( s t ) ( mod p ) 递归使用 Lucas 定理求值。
而对于小组合数有:

(qr)q!r!(qr)!(modp) ( q r ) ≡ q ! r ! ( q − r ) ! ( mod p )

对于阶乘很大的情况,计算机编程时可能需要对分子分母分别取余,但是对于除法不能轻易使用同余定理(只在 +,, + , − , ∗ 三种运算下有效),所以希望可以将除法取余转换为等价的乘法取余,这时便需要另外重要的知识:逆元以及费马小定理。

逆元

逆元定义:对于正整数 a a p,如果有 ax1(modp) a x ≡ 1 ( mod p ) ,那么把这个同余方程中 x x 的最小正整数解叫做 a(modp) 的逆元。

费马小定理

定义:假如 p p 是质数,且gcd(a,p)=1,即 a,p a , p 互质,那么 ap11(modp) a p − 1 ≡ 1 ( mod p )

由费马小定理可得:

ap11(modp)ap21a(modp) a p − 1 ≡ 1 ( mod p ) ⇒ a p − 2 ≡ 1 a ( mod p )

因此当 a,p a , p 互质且 p p 为素数时,有:
babap2(modp)

带入组合数公式,即有:
(qr)q!r!(qr)!(q!)(r!(qr))p2(modp) ( q r ) ≡ q ! r ! ( q − r ) ! ≡ ( q ! ) ( r ! ( q − r ) ) p − 2 ( mod p )

这样便将除数求余转化为同余的乘法求余运算。
这里出现了求幂运算 ap2 a p − 2 ,可以使用快速求幂算法

快速求幂

//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.youkuaiyun.com/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 定理”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值