快速幂和求组合数

Posted lixyuan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速幂和求组合数相关的知识,希望对你有一定的参考价值。

  1. 预备知识快速幂

计算

[a^kmod p ]

快速幂就是快速算底数的k次幂,时间复杂度为O(logk)与朴素算法相比效率极大提升。

原理

k转化成二进制数

11的二进制数为1011

则十进制11写成

[11=1 imes2^3+0 imes2^2+1 imes2^1+1 imes2^0 ]

k=11时,

(a^k=a^{11})写成

[a^{2^0} imes a^{2^1} imes a^{2^3} ]

代码实现:

int qmi(int a, int k, int p) {
    long res = 1, base = a;
    while(k != 0) {
        if((k & 1) == 1) res = res * a % p;
        k >>= 1;
        a = a * a % p;
    }
    return (int) res;
}

求组合数

  1. 定义法

[C_{n}^{m}=cfrac {n imes(n-1).. imes(n-m+1)}{m!}=cfrac{n!}{m!(n-m)!}=prod_{i=1}^{m}cfrac{n-m+i}{i} ]

int comb01(int n, int m) {
    long res = 1;
    for(int i=1; i <= m; i++) {
        res = res * (n-m+i) / i;
    }
    return (int) res;
}

2.性质法

[C_n^m=C_{n-1}^m+C_{n-1}^{m-1} ]

一个物品有两种选择,要么选要么不选,n个中选m个的方案数((C_n^m))等于当前一个物品不选的方案数((C_{n-1}^{m}))和选当前物品的方案数((C_{n-1}^{m-1}))的和。

int comb02(int n, int m, int p) {
    int[][] c = new int[n+1][m+1];
    for(int i=0; i <= n; i++) {
        for(int j=0; j <= i && j <= m; j++) {
            if(j==0) c[i][j] = 1;
            else c[i][j] = (c[i-1][j-1] + c[i-1][j]) % p;
        }
    }
    return c[n][m];
}

3.预处理逆元法

预处理出阶乘取模的余数fact[N],和阶乘取模的逆元infact[N]

如果取模的数是质数,可以使用费马小定理求逆元

费马小定理:若p为素数,则有(a^{p-1}equiv1 (mod p))

即:

[a imes a^{p-2} equiv 1 (mod p) ]

(a^{p-2})就是a在(mod p)意义下的逆元。

如果多次计算

long[] fact, infact;
int N;
int p = 1000000007;//必须为素数
void init(int n, int p){
    N = 1001;// 必须N大于恒大于n
    fact = new long[N];
    infact = new long[N];
    fact[0] = infact[0] = 1;
    for(int i=1; i < N; i++) {
        fact[i] = fact[i-1]*i % p;
        infact[i] = qmi((int)fact[i], p-2, p);
    }
    //System.out.println(Arrays.toString(fact));
    //System.out.println(Arrays.toString(infact));
}
int comb03(int n, int m, int p) {
    init(p);
    long res = fact[n] * infact[n-m] % p * infact[m] % p;
    return (int)res;
}

或者计算一次

int p = 1000000007;//必须为素数
long ans1 = 1L, ans2 = 1L;//组合数的分子和分母
for(int i=n; i >= n-m+1; i--)
    ans1 = ans1 * i % p;
for(int i=1; i <= s; i++)
    ans2 = ans2 * i % p; 
ans2 = qmi((int)ans2, p-2, p);
int comb = ans1*ans2%p;

以上是关于快速幂和求组合数的主要内容,如果未能解决你的问题,请参考以下文章

自然数幂和

求自然数幂和

快速幂和快速幂取模

快速幂和慢速乘

快速幂和快速乘

快速幂和快速乘