1. 程式人生 > >組合數的兩種計算方法(遞推,對數)

組合數的兩種計算方法(遞推,對數)

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>
1) return (comb(m-1,n-1)+comb(m-1,n));}int main(){ int m,n; while(cin>>m>>n) cout<<comb(m,n)<<endl; return 0;}
comb1.cpp


方法二:
因為公式右邊均為乘法,相到可以取對數將其展開。於是對公式兩遍取對數,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>
中,log()預設是以2為底,如果要求以a為底b的對數,可以用對數的性質轉換下
:log(a)b=logb/loga。

程式碼示例:
  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;}