数论-组合数的求法探究

组合数的简述

  • 组合数一般表示为 C n m C_n^m Cnm,也表示为
    ( m n ) \left( \begin{matrix}m \\n\\\end{matrix}\right) (mn)
  • 在数论中,一般需要求的是组合数取模,即 C n m   %   p C_n^m \ \% \ p Cnm % p,其中大多数时候p=1e9+7
  • 组合数的计算公式为
    C n m = n ! m ! ( n − m ) ! C_n^m=\dfrac{n!}{m!(n-m)!} Cnm=m!(nm)!n!
  • 由于要求取模,这时直接得出的结果往往过于巨大(这也是取模的原因),所以不可以算出结果再取模。此外,没有模除法性质(即(a/b)%p=((a%p)/(b%p)%p不成立),所以要用到逆元

杨辉三角法

我们先考虑一种不用逆元的方法:杨辉三角法

原理分析

杨辉三角具有如下性质:

  1. 杨辉三角上的每一个数字都等于它的左上方和右上方的和(除了边界)
  2. 第n行,第m个就是,就是C(n, m) (从0开始)

image

实现代码(复杂度O(N^2))

#include<cstdio>
const int N = 2000 + 5;
const int MOD = (int)1e9 + 7;
int comb[N][N];//comb[n][m]就是C(n,m)
void init(){
    for(int i = 0; i < N; i ++){
        comb[i][0] = comb[i][i] = 1;
        for(int j = 1; j < i; j ++){
            comb[i][j] = comb[i-1][j] + comb[i-1][j-1];
            comb[i][j] %= MOD;
        }
    }
}
int main(){
    init();
}

逆元法

原理分析

  1. 什么是逆元

    • 定义:对于a和p(a和p互素),若a * b % p ≡ 1,则称b为a % p的逆元,记作 b = inv(a) mod p 或 b = inv(a, p)
    • 性质:(a / b) % p ⇔ \Leftrightarrow (a * inv(b)) % p = (a % p + inv(b) % p) % p
  2. 如何求逆元

    • 费马小定理:对于a和素数p,满足a ^ ( p - 1) ≡ 1 (mod p)
    • 上式等价于 a ^ (p - 2) * a ≡ 1 (mod p),所以inv(a) = a ^ (p-2)
    • 逆元还有其他的求法,此处不一一列出。
  3. 利用逆元求组合数取模

    预先迭代求出所有阶乘,然后求出对应的逆元,最后利用逆元乘法取模性质即可

实现代码(复杂度O(N))

#include<cstdio>
const int N = 200000 + 5;
const int MOD = (int)1e9 + 7;
int F[N], Finv[N], inv[N];//F是阶乘,Finv是逆元的阶乘 
void init(){
    inv[1] = 1;
    for(int i = 2; i < N; i ++){
        inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD;
    }
    F[0] = Finv[0] = 1;
    for(int i = 1; i < N; i ++){
        F[i] = F[i-1] * 1ll * i % MOD;
        Finv[i] = Finv[i-1] * 1ll * inv[i] % MOD;
    }
}
int comb(int n, int m){//comb(n, m)就是C(n, m) 
    if(m < 0 || m > n) return 0;
    return F[n] * 1ll * Finv[n - m] % MOD * Finv[m] % MOD;
}
int main(){
    init();
}

卢卡斯法

原理分析

  1. 适用情景: n<=1e18,m<=1e18,p<=1e5 , 即n、m很大但p很小
  2. 卢卡斯定理:C(n, m) % p = C(n / p, m / p) * C(n % p, m % p) % p
  3. 实现思路:递归求取即可

实现代码

LL Lucas(LL n, LL m, int p){
        return m ? Lucas(n/p, m/p, p) * comb(n%p, m%p, p) % p : 1;
}

参考博客

  1. https://www.cnblogs.com/linyujun/p/5194189.html
  2. https://blog.csdn.net/ajaxlt/article/details/86544074
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值