1. 程式人生 > >組合數C(n,m)的四種求解方法

組合數C(n,m)的四種求解方法

ons 避免 art main src vector 記錄 display ace

轉自:文章

1、暴力求解

C(n,m)=n*(n-1)*...*(n-m+1)/m!,(n<=15);

int CF(int n,int m)
{
    int ans=1,i,j;
    for(i=n;i>=n-m+1;i--)
    ans*=i;
    for(i=m;i>=2;i--) 
    ans/=i;
    return ans;
}

2、打表

C(n,m)=C(n-1,m-1)+C(n-1,m),(n<=10000);

const int maxn = 10010;
const int MOD = 100007;
void CF(int n,int m)
{
    
int i,j; for(i=0;i<=maxn;i++) { c[0][i]=0;c[i][0]=1; } for(i=1;i<=maxn;i++) for(j=1;j<=maxn;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD; }

3、質因數分解

C(n,m)=n!/(m!*(n-m)!),C(n,m)=p1a1-b1-c1p2a2-b2-c2…pkak-bk-ck,(n<=10000000)

技術分享圖片
#include<iostream>
#include<cstdio>
#include
<vector> using namespace std; const int MOD = 100007; const int maxn = 1000001; bool a[maxn]={false}; vector <int> prim_produce() //生成素數序列 { vector <int> vc; vc.push_back(2); int i,j; for(i=3;i*i<=maxn;i+=2) { if(!a[i]) { vc.push_back(i);
for(j=i*i;j<=maxn;j+=i) { a[j]=true; } } } while(i<maxn) { if(!a[i]) vc.push_back(i); i+=2; } return vc; } //計算n!素數p的指數 int cal(int x,int p) { int ans=0; long long re=p; while(x>=re) { ans+=x/re; re*=p; } return ans; } int Pow(long long n,int k) //二分求n的k次方 { long long ans=1; while(k) { if(k&1) ans=ans*n%MOD; n=(n*n)%MOD; k>>=1; } return ans; } int comb(int n,int m) //計算公式 { vector <int> prim=prim_produce(); long long ans=1; int num; for(int i=0;i<prim.size()&&prim[i]<=n;i++) { num=cal(n,prim[i])-cal(m,prim[i])-cal(n-m,prim[i]); ans=(ans*Pow(prim[i],num))%MOD; } return ans; } int main(void) { int n,m; while(~scanf("%d%d",&n,&m)) { printf("%d\n",comb(n,m)); } return 0; }
View Code

4、Lucas定理

將m,n化為p進制,有:C(n,m)=C(n0,m0)*C(n1,m1)...(mod p),算一個不是很大的C(n,m)%p,p為素數,化為線性同余方程,用擴展的歐幾裏德定理求解,n在int範圍內,修改一下可以滿足long long範圍內。

技術分享圖片
#include <stdio.h>
const int M = 2013;
int ff[M+5];  //打表,記錄n!,避免重復計算
 
//求最大公因數
int gcd(int a,int b)
{
    if(b==0)
        return a;
    else
        return gcd(b,a%b);
}
 
//解線性同余方程,擴展歐幾裏德定理
int x,y;
void Extended_gcd(int a,int b)
{
    if(b==0)
    {
        x=1;
        y=0;
    }
    else
    {
        Extended_gcd(b,a%b);
        long t=x;
        x=y;
        y=t-(a/b)*y;
    }
}
 
//計算不大的C(n,m)
int C(int a,int b)
{
    if(b>a)
        return 0;
    b=(ff[a-b]*ff[b])%M;
    a=ff[a];
    int c=gcd(a,b);
    a/=c;
    b/=c;
    Extended_gcd(b,M);
    x=(x+M)%M;
    x=(x*a)%M;
    return x;
}
 
//Lucas定理
int Combination(int n, int m)
{
    int ans=1;
    int a,b;
    while(m||n)
    {
        a=n%M;
        b=m%M;
        n/=M;
        m/=M;
        ans=(ans*C(a,b))%M;
    }
    return ans;
}
 
int main()
{
    int i,m,n;
    ff[0]=1;
    for(i=1; i<=M; i++) //預計算n!
        ff[i]=(ff[i-1]*i)%M;
    while(~scanf("%d%d",&n, &m))
    {
        printf("%d\n",Combination(n,m));
    }
    return 0;
}
View Code

組合數C(n,m)的四種求解方法