組合數的兩種計算方法(遞推,對數)
阿新 • • 發佈:2019-02-17
http://blog.csdn.net/Feynman1999/article/details/56679096
組合數
從m個不同元素中,任取n(n≤m)個元素併成一組,叫做從m個不同元素中取出n個元素的一個組合;所有可能的組合種數就是組合數。組合數的計算公式如下圖:
式子中出現了階乘,而20!=2.4329020081766 * 1018
這個數字已經和long long能表示的最大整數一個數量級了,而式子是個除式,所以要想辦法在過程中把數降下來。
方法一:想到一個組合數公式:
c(m,n)=c(m-1,n-1)+c(m-1,n)
這個式子可以這樣記憶:你從m個元素裡挑n個元素,針對第一個元素要麼是n個裡的要麼不是,如果是的,那麼就從剩下的m-1個裡挑n-1個 就是c(m-1,n-1);如果第一個元素不是n裡的,就從剩下的m-1個元素裡挑n個,就是c(m-1,n)。
利用這個公式,就能用遞迴的方法解決問題。注意遞迴的結束條件。
程式碼示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include<iostream>#include<cstdio>using namespace std;long long comb(int m,int n){ if(n==0) return 1; if(n==1) return m; if(n>m/2) return comb(m,m-n); if(n> |
方法二:
因為公式右邊均為乘法,相到可以取對數將其展開。於是對公式兩遍取對數,log(c(m,n))= log( m!/(m-n)!) -log n!
於是可以直接求 log(m-n+1)+log(m-n+2)+······+log(m) 和log(1)+log(2)+······+log(n)。
注意這裡的log()和exp()函式在標頭檔案#include<math.h>
程式碼示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include<iostream>#include<math.h>using namespace std;double comb_log(int m,int n)//對數求法 { int i; if(n>m-n) n=m-n; double s1=0.0,s2=0.0; for(i=m-n+1;i<=m;++i) s1+=log(i); //求 log( m!/(m-n)!) for(i=1;i<=n;++i) s2+=log(i); // 求log n! return exp(s1-s2);}int main(){ int m,n; while(cin>>m>>n){ cout<<(long long)(comb_log(m,n))<<endl; } return 0;} |