天天看點

算法:combination in c++

 前幾天在論壇看到一個文章,計算組合數,有點數學基礎的都知道,求組合用公式Cnr(= n!/(n-r)!r!)可以得到組合數。怎樣通過程式實作得到每一個組合序列呢?以整型資料為例:

Ex:

N = {1,2,3,4,5}

從N中5個元素取2個得到

12 13 14 15 23 24 25 34 35 45 ——10個組合

這裡可以看到組合出現是有規律的,每個組合總是按N中元素順序出現(組合不是排列,12和21屬于同一組合),其次,相鄰組合後者在前者基礎上從最末元素開始按N中元素順序遞增。

這是個很有用的資訊,如果我們要輸出每個組合序列,我們可以很自然的聯想到最直覺的解決辦法——遞歸回溯。論壇上那位朋友也是用的這個思想設計解法。

現給出代碼,在長度為N順序整型資料中選取長度為R的組合序列:

算法:combination in c++

// 參數說明:a記錄組合序列,k1為組合坐标,k2為輔助參數

算法:combination in c++
算法:combination in c++

void  combination( int  a[], int  n, int  r, int  k1, int  k2)... ... {

算法:combination in c++
算法:combination in c++

if (k1 == r)......{ //輸出目前序列

算法:combination in c++

for (int i = 0;i < r;++i)

算法:combination in c++

cout << a[i] << " ";

算法:combination in c++

cout << endl;

算法:combination in c++

}

算法:combination in c++

else

算法:combination in c++
算法:combination in c++

for (int i = k2;i < n;++i)......{

算法:combination in c++

a[k1] = i+1; //子序列指派

算法:combination in c++

combination(a,n,r,k1+1,i+1); //遞歸回溯

算法:combination in c++

}

算法:combination in c++

}

代碼很簡單。這個問題現在可以算解決了吧?不然,由此我想到,如果目前集合N并非順序排列,如果要想對每次得到的R序列做特定處理。那麼僅僅這樣是不夠的。為了增加實用性,如果把它設計成一個泛型算法,是否效果更好。

有了初步設想,進一步考慮,N序列應該由使用者定義,用容器R來儲存組合序列,并由使用者設定對目前R中組合序列的操作。

下面是模闆函數:

算法:combination in c++

// 參數說明:begin,end分别為雙向疊代器,col為坐标,初始為0,loop循環次數,由N,R長度差決定,func 使用者定義函數

算法:combination in c++

template < class  BidIt, class  Func >

算法:combination in c++

void  combination_recursive(BidIt n_begin,BidIt n_end, int  n_col,

算法:combination in c++
算法:combination in c++

BidIt r_begin,BidIt r_end, int  r_col, int  loop,Func func) ... {

算法:combination in c++
算法:combination in c++

int r_size = r_end-r_begin; //擷取R長度

算法:combination in c++

int curr_n_col = n_col;

算法:combination in c++

int curr_loop = loop;

算法:combination in c++
算法:combination in c++

if (r_col == r_size) //産生組合序列

算法:combination in c++

func(r_begin,r_end);

算法:combination in c++
算法:combination in c++

else

算法:combination in c++
算法:combination in c++

for (int i = 0;i <= loop;++i)...{

算法:combination in c++
算法:combination in c++

BidIt r_it = r_begin; //擷取R疊代器位置

算法:combination in c++

for (int r_cnt = 0;r_cnt < r_col;++r_cnt)

算法:combination in c++

++r_it; 

算法:combination in c++

BidIt n_it = n_begin; //擷取N疊代器位置

算法:combination in c++

for (int n_cnt = 0;n_cnt < n_col+i;++n_cnt)

算法:combination in c++

++n_it;

算法:combination in c++
算法:combination in c++

*r_it = *n_it; //指派

算法:combination in c++
算法:combination in c++

++curr_n_col;

算法:combination in c++

//遞歸回溯

算法:combination in c++

combination_recursive(n_begin,n_end,curr_n_col,

算法:combination in c++

r_begin,r_end,r_col+1,curr_loop,func);

算法:combination in c++
算法:combination in c++

--curr_loop;

算法:combination in c++

}

算法:combination in c++

}

函數用法:

Ex:

到這裡,最初的設想得以實作。但是又存在一個問題,算法combination用到了遞歸,在大規模的輸入時,入棧的深度會消耗大量的記憶體空間,其次,盡管我們自定義的函數可以實作需要的操作,但是用途是很有限的,并且不能靈活的篩選需要的組合序列。

與“組合”相關的字眼就是“排列”了,在STL算法庫中,next_permutation是個很好的範例(參見blog中另一文章《stl算法:next_permutation剖析》),能否設計一個類似這樣的範型算法解決我們的問題?答案是肯定的。因為在最初的例子中得出組合的兩個性質,說明組合間是有序的,由一個組合序列是可以得出下一個組合序列的。

下面是combination的函數原型:

算法:combination in c++

template < class  BidIt >

算法:combination in c++

bool next_combination(BidIt n_begin,BidIt n_end,

算法:combination in c++

BidIt r_begin,BidIt r_end);

算法:combination in c++
算法:combination in c++

template < class  BidIt, class  Pred >

算法:combination in c++

bool next_combination(BidIt n_begin,BidIt n_end,

算法:combination in c++

BidIt r_begin,BidIt r_end

算法:combination in c++

Pred equal);

算法:combination in c++
算法:combination in c++

template < class  BidIt >

算法:combination in c++

bool prev_combination(BidIt n_begin,BidIt n_end,

算法:combination in c++

BidIt r_begin,BidIt r_end);

算法:combination in c++
算法:combination in c++

template < class  BidIt, class  Pred >

算法:combination in c++

bool prev_combination(BidIt n_begin,BidIt n_end,

算法:combination in c++

BidIt r_begin,BidIt r_end,

算法:combination in c++

Pred equal);

算法線上性時間内完成,并且采用疊,由于算法具體實作很繁雜,這裡隻做簡要說明。next_combination & prev_combination均采用從末端向前索引,根據比較N,R目前元素設定标記并産生下一組合序列。

函數用法:

Ex:

算法:combination in c++

#include  < iostream >

算法:combination in c++

#include  " combination.h "

算法:combination in c++
算法:combination in c++

using namespace std;

算法:combination in c++
算法:combination in c++
算法:combination in c++

int  main() ... {

算法:combination in c++

char n[] = "abcdefg";

算法:combination in c++

char r[] = "abc";

算法:combination in c++

int count = 0;

算法:combination in c++
算法:combination in c++

do...{

算法:combination in c++

cout << r << endl;

算法:combination in c++

++count;

算法:combination in c++

}

算法:combination in c++

while (next_combination(n,n+7,r,r+3));

算法:combination in c++

cout << count << endl;

算法:combination in c++

cout << "finished" << endl;

算法:combination in c++

system("pause");

算法:combination in c++

}

算法:combination in c++
算法:combination in c++

若采用prev_combination,則需替換r為最大組合序列char r[] = “efg”。

注:本文算法雖全由我書寫完成但并非全由我設計,借鑒于網絡和書籍,歡迎讨論。