Lucas定理——定义、证明、实现、运用

Lucas定理是一个用于分解组合数并解决模数小、数字大的计算问题的数学定理。该定理说明,对于给定的质数p和整数n,m,满足特定条件的组合数C(n,m)模p可以被分解为更小的组合数的乘积。文章还提供了一个C++实现,通过预处理阶乘逆元来高效地计算Lucas定理的结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是Lucas定理

这是一个有助于分解组合数来求解的定理,适合模数小,数字大的问题。

有质数 p p p,对于 n , m n,m n,m,如果 n = k 1 p + b 1 , m = k 2 p + b 2 n=k_1p+b_1,m=k_2p+b_2 n=k1p+b1,m=k2p+b2,有

C n m ≡ C k 1 k 2 C b 1 b 2 ( m o d p ) C_n^m\equiv C_{k_1}^{k_2}C_{b_1}^{b_2} \pmod p CnmCk1k2Cb1b2(modp)

由此可以分解成较小的问题求解。

证明Lucas定理

这个证明利用了二项式定理的思路,前所未闻,真的很有趣。

根据二项式定理可以得到 ( 1 + x ) n (1+x)^n (1+x)n x m x^m xm的系数为 C n m C_n^m Cnm

我们用这一点作为突破口,对于 ( 1 + x ) n (1+x)^n (1+x)n,我们有

( 1 + x ) n ≡ ( 1 + x ) k 1 p + b 1 ≡ ( 1 + x ) k 1 p ( 1 + x ) b 1 ≡ ( ( 1 + x ) p ) k 1 ( 1 + x ) b 1 ( m o d p ) (1+x)^n\equiv (1+x)^{k_1p+b_1}\equiv (1+x)^{k_1p}(1+x)^{b_1}\equiv ((1+x)^p)^{k_1}(1+x)^{b_1}\pmod p (1+x)n(1+x)k1p+b1(1+x)k1p(1+x)b1((1+x)p)k1(1+x)b1(modp)

然后有一个很有意思的东西

( 1 + x ) p ≡ 1 + x p ( m o d p ) (1+x)^p\equiv 1+x^p \pmod p (1+x)p1+xp(modp)

为什么呢?将式子拆开后,显然除了第一项与最后一项,其他项是 p p p的倍数,自然就会抹掉了。

继续进行推导,我们有
( 1 + x ) n ≡ ( 1 + x p ) k 1 ( 1 + x ) b 1 ( m o d p ) (1+x)^n\equiv(1+x^p)^{k_1}(1+x)^{b_1}\pmod p (1+x)n(1+xp)k1(1+x)b1(modp)

于是右边的式子拆开可以得到

( ∑ i = 0 k 1 C k 1 i x p i ) ( ∑ j = 0 b 1 C b 1 j x j ) ( m o d p ) (\sum_{i=0}^{k_1} C_{k_1}^i x^{pi})(\sum_{j=0}^{b_1} C_{b_1}^j x^{j})\pmod p (i=0k1Ck1ixpi)(j=0b1Cb1jxj)(modp)

我们要求 x m x^m xm的系数,也就是 x k 2 p + b 2 x^{k_2p+b_2} xk2p+b2的系数。

由于 b 1 < p b_1<p b1<p,所以 k 2 p k_2p k2p只能让 ∑ i = 0 k 1 C k 1 i x p i \sum_{i=0}^{k_1} C_{k_1}^i x^{pi} i=0k1Ck1ixpi负责了,当 i = k 2 i=k_2 i=k2时符合条件,此时系数为 C k 1 k 2 C_{k_1}^{k_2} Ck1k2

剩下部分由 ∑ j = 0 b 1 C b 1 j x j \sum_{j=0}^{b_1} C_{b_1}^j x^{j} j=0b1Cb1jxj负责,当 j = b 2 j=b_2 j=b2时符合条件,此时系数为 C b 1 b 2 C_{b_1}^{b_2} Cb1b2

因此 x m x^m xm的系数为 C k 1 k 2 C b 1 b 2 C_{k_1}^{k_2}C_{b_1}^{b_2} Ck1k2Cb1b2,前文已知根据二项式定理 x m x^m xm的系数为 C n m C_n^m Cnm,我们得到

C n m ≡ C k 1 k 2 C b 1 b 2 ( m o d p ) C_n^m\equiv C_{k_1}^{k_2}C_{b_1}^{b_2} \pmod p CnmCk1k2Cb1b2(modp)

由此,完成了Lucas定理的证明。

Lucas定理求解组合数的C++实现

代码上没什么难点,首先基础的组合数求解还是要有的,也就是我们需要预处理阶乘逆元,然后使用Lucas将组合数拆开再用基础的组合数求解即可。

long long ksm(long long x,long long y)
{
    long long sum=1;
    while(y)
    {
        if(y&1)
        {
            sum*=x;
            sum%=mod;
        }
        x*=x;
        x%=mod;
        y>>=1;
    }
    return sum;
}
long long C(long long n,long long m)
{
    if(m>n)return 0;
    if(m==0||n==m)return 1;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
long long lucas(long long n,long long m)
{
    if(m>n)return 0;
    if(n<mod)return C(n,m);
    return lucas(n/mod,m/mod)*lucas(n%mod,m%mod)%mod;
}
void init()
{    
    fac[0]=1;
    for(int i=1;i<=mod-1;i++)
    {
        fac[i]=fac[i-1]*i%mod;
    }
    inv[mod-1]=ksm(fac[mod-1],mod-2);
    for(int i=mod-2;i>=0;i--)
    {
        inv[i]=inv[i+1]*(i+1)%mod;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值