组合数C(n,m)的四种求解方法
Posted 2018zxy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了组合数C(n,m)的四种求解方法相关的知识,希望对你有一定的参考价值。
转自:文章
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 ",comb(n,m)); } return 0; }
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 ",Combination(n,m)); } return 0; }
以上是关于组合数C(n,m)的四种求解方法的主要内容,如果未能解决你的问题,请参考以下文章