zoukankan      html  css  js  c++  java
  • [组合数]求组合数的几种方法总结

    求C(n,m)%mod的方法总结

    1.当n,m都非常小的时候能够利用杨辉三角直接求。
    C(n,m)=C(n-1,m)+C(n-1,m-1)。

    2.利用乘法逆元。
    乘法逆元:(a/b)%mod=a*(b^(mod-2)) mod为素数。
    逆元能够利用扩展欧几里德或欧拉函数求得:

     1).扩展欧几里德:b*x+p*y=1 有解,x就是所求
    
     2).费马小定理:b^(p-1)=1(mod p),故b*b^(p-2)=1(mod p),所以x=b^(p-2)
    

    1. n!/(m!*(n-m)! =x%p ,先对算出n!、m!

    、(n-m)!对p取模的余数。就转换为a/b=x%p;由于p为素数。所以等价于bx+py=a;然后用扩展的欧几里得定理算出 bx’+py’=1的解。x=x’*a,就得到了终于的x的值,即C(m,n)%p得值。

    2.逆元 事实上假设mod是素数 则b的逆元事实上就是b^(mod-2)
    也就是 (m!(n-m)!)的逆元为 (m!(n-m)!)p-2 ;

    int inv(int a) {  
        //return fpow(a, MOD-2, MOD);  
        return a == 1 ?

    1 : (long long)(MOD - MOD / a) * inv(MOD % a) % MOD; } LL C(LL n,LL m) { if(m < 0)return 0; if(n < m)return 0; if(m > n-m) m = n-m; LL up = 1, down = 1; for(LL i = 0 ; i < m ; i ++){ up = up * (n-i) % MOD; down = down * (i+1) % MOD; } return up * inv(down) % MOD; }

    3.当n和m比較大,mod是素数且比較小的时候(10^5左右)。通过Lucas定理计算

    Lucas定理:A、B是非负整数,p是质数。A B写成p进制:A=a[n]a[n-1]…a[0]。B=b[n]b[n-1]…b[0]。


    则组合数C(A,B)与C(a[n],b[n])C(a[n-1],b[n-1])…*C(a[0],b[0]) mod p同余
    即:Lucas(n,m,p)=C(n%p,m%p)*Lucas(n/p,m/p,p)

    #include<iostream>
    //#include<algorithm>
    using namespace std;
    typedef long long ll;
    int quick_power_mod(int a,int b,int m){//pow(a,b)%m
        int result = 1;
        int base = a;
        while(b>0){
             if(b & 1==1){
                result = (result*base) % m;
             }
             base = (base*base) %m;
             b>>=1;
        }
        return result;
    }
    //计算组合数取模
    ll comp(ll a, ll b, int p) {//composite num C(a,b)%p
        if(a < b)   return 0;
        if(a == b)  return 1;
        if(b > a - b)   b = a - b;
    
        int ans = 1, ca = 1, cb = 1;
        for(ll i = 0; i < b; ++i) {
            ca = (ca * (a - i))%p;
            cb = (cb * (b - i))%p;
        }
        ans = (ca*quick_power_mod(cb, p - 2, p)) % p;
        return ans;
    }
    ll lucas(ll n, ll m, ll p) {
         ll ans = 1;
    
         while(n&&m&&ans) {
            ans = (ans*comp(n%p, m%p, p)) % p;//also can be recusive
            n /= p;
            m /= p;
         }
         return ans;
    }
    int main(){
        ll m,n;
        while(cin>>n>>m){
            cout<<lucas(n,m,10007)<<endl;
        }
        return 0;
    }

    C(n % mod, m % mod) % mod; 假设太大还是利用上面的逆元来处理。

    半预处理
    由于Lucas定理保证了阶乘的数均小于p,所以能够讲全部的阶乘先预处理,优化C(n,m)
    mod的要求:p<10^6,且为素数
    有效范围:1<=n,m<=10^9

    //半预处理  
    const ll MAXN = 100000;  
    ll fac[MAXN+100];  
    void init(int mod)  
    {  
        fac[0] = 1;  
        for (int i=1; i<mod; i++){  
            fac[i] = fac[i-1] * i % mod;  
        }  
    }  
    
    //半预处理逆元求C(n。m)%mod  
    ll C(ll n, ll m)  
    {  
        if ( m>n ) return 0;  
        return fac[n] * (GetInverse(fac[m]*fac[n-m], mod)) % mod;  
    }  

    4.另一种就是分解质因子。这个比較麻烦。
    http://www.mamicode.com/info-detail-621758.html

  • 相关阅读:
    基于.NET CORE微服务框架 -谈谈Cache中间件和缓存降级
    基于.NET CORE微服务框架 -谈谈surging的服务容错降级
    基于.NET CORE微服务框架 -surging的介绍和简单示例 (开源)
    Git学习记录-基本命令篇
    一个实例搞懂二重指针
    不能将X*类型的值分配到X*类型的实体问题的解决方法
    如何将idea工程打包成jar文件
    windows10环境下安装深度学习环境anaconda+pytorch+CUDA+cuDDN
    指针、地址和引用学习笔记
    几行代码实现cookie的盗取
  • 原文地址:https://www.cnblogs.com/yfceshi/p/7122009.html
Copyright © 2011-2022 走看看