數學知識 --- 組合數


什么是組合數?

  • 組合數公式是指從n個不同元素中,任取m(m≤n)個元素並成一組,叫做從n個不同元素中取出m個元素的一個組合;
  • 從n個不同元素中取出m(m≤n)個元素的所有組合的個數,叫做n個不同元素中取出m個元素的組合數。用符號c(n,m) 表示

前置知識:排列公式

  • 排列公式是建立一個模型,從n個不相同元素中取出m個排成一列(有序)
  • 第一個位置可以有n個選擇,第二個位置可以有n-1個選擇(已經有1個放在前一個位置),則同理可知第三個位置可以有n-2個選擇,以此類推第m個位置可以有n-m+1個選擇

組合數公式

  • 組合公式的推導是由排列公式去掉重復的部分而來的
  • 組合公式對應另一個模型,取出m個成為一組(無序)
  • 排列公式建立有序,而組合公式建立為無序,有序情況當然比無序多

通過遞推來求組合數,時間復雜度為O(N^2)

c(n,m)=c(n-1,m-1)+c(n-1,m)

  • 從n中選m個元素可以選取某元素的包含與不包含,分成兩類情況,即m個被選擇元素包含了這個元素和m個被選擇元素不包含該元素。
  • 前者相當於從n-1個元素中選出m-1個元素的組合,即c(n-1,m-1)
  • 后者相當於從n-1個元素中選出m個元素的組合,即c(n-1,m)

代碼如下:

  • 預處理所有的情況,a,b范圍在[1,2000]
#include<iostream>

using namespace std;

const int N = 2e3 + 10,mod = 1e9 + 7;

int n;
int a,b;
int c[N][N];

void init(){
    for(int i = 0; i < N; i ++ ){
        for(int j = 0; j <= i; j ++ ){
            if(!j) c[i][j] = 1;
            else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
        }
     }
}
int main(){
    init();
    cin >> n;
    while(n -- ){
        cin >> a >> b;
        cout<<c[a][b]<<endl;
    }
    return 0;
}

通過組合數公式求組合數,時間復雜度為O(nlogn)

  • 用fact[]數組來存放(n!)mod(1e9 + 7)
  • 用infact[]數組來存放(n!)的逆元mod(1e9 + 7)
  • 求逆元用快速冪和費馬小定理
  • 組合數為:fact[a] * infact[b - a] * infact[b]
  • 防止超出范圍:fact[a] * infact[b] % mod * infact[a - b] % mod

代碼如下:

  • 預處理,a,b范圍在[1,1e5]
#include<iostream>

using namespace std;

#define ll long long
const int N = 1e5 + 10,mod = 1e9 + 7;

int n;
ll fact[N],infact[N];

ll qmi(ll a,ll b,ll p){
	ll res = 1%p;
	while(b){
		if(b & 1) res = res * a % p;
		a = a * a % p;
		b >>= 1; 
	}
	return res;
}

void init(){
	fact[0] = infact[0] = 1;
	for(int i = 1; i < N; i ++ ){
		fact[i] = fact[i - 1] * i % mod;
		infact[i] = infact[i - 1] * qmi(i,mod - 2,mod) % mod;
	}
}
int main(){
	cin >> n;
	while(n -- ){
		int a,b;
		cin >> a >> b;
		printf("%lld\n",fact[a] * infact[b] % mod * infact[a - b] % mod);
	}    
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM