| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 3561 人关注过本帖
标题:[原创]算法:combination in c++
只看楼主 加入收藏
aipb2007
Rank: 8Rank: 8
来 自:CQU
等 级:贵宾
威 望:40
帖 子:2879
专家分:7
注 册:2007-3-18
收藏
 问题点数:0 回复次数:10 
[原创]算法:combination in c++

*/ --------------------------------------------------------------------------------------
*/ 出自: 编程中国 http://www.bc-cn.net
*/ 作者: aipb2007 E-mail:aipb@163.com
*/ 时间: 2007-8-26 编程论坛首发
*/ 声明: 尊重作者劳动,转载请保留本段文字
*/ --------------------------------------------------------------------------------------

前几天在论坛看到一个帖子,计算组合数,有点数学基础的都知道,求组合用公式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的组合序列:

//参数说明:a记录组合序列,k1为组合坐标,k2为辅助参数
void combination(int a[],int n,int r,int k1,int k2){
if (k1 == r){ //输出当前序列
for (int i = 0;i < r;++i)
cout << a[i] << " ";
cout << endl;
}
else
for (int i = k2;i < n;++i){
a[k1] = i+1; //子序列赋值
combination(a,n,r,k1+1,i+1); //递归回溯
}
}


代码很简单。这个问题现在可以算解决了吧?不然,由此我想到,如果当前集合N并非顺序排列,如果要想对每次得到的R序列做特定处理。那么仅仅这样是不够的。为了增加实用性,如果把它设计成一个泛型算法,是否效果更好。
有了初步设想,进一步考虑,N序列应该由用户定义,用容器R来储存组合序列,并由用户设定对当前R中组合序列的操作。

下面是模板函数:

//参数说明:begin,end分别为双向迭代器,col为坐标,初始为0,loop循环次数,由N,R长度差决定,func 用户定义函数
template<class BidIt,class Func>
void combination_recursive(BidIt n_begin,BidIt n_end,int n_col,
BidIt r_begin,BidIt r_end,int r_col,int loop,Func func){

int r_size = r_end-r_begin; //获取R长度
int curr_n_col = n_col;
int curr_loop = loop;

if (r_col == r_size) //产生组合序列
func(r_begin,r_end);

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

BidIt r_it = r_begin; //获取R迭代器位置
for (int r_cnt = 0;r_cnt < r_col;++r_cnt)
++r_it;
BidIt n_it = n_begin; //获取N迭代器位置
for (int n_cnt = 0;n_cnt < n_col+i;++n_cnt)
++n_it;

*r_it = *n_it; //赋值

++curr_n_col;
//递归回溯
combination_recursive(n_begin,n_end,curr_n_col,
r_begin,r_end,r_col+1,curr_loop,func);

--curr_loop;
}
}

函数用法:
Ex:
//自定义函数,这里为输出
void display(char *b,char *e){
cout << b << endl;
}

int main(){
char n[] = "abcdef";
char r[] = "xxx";
combination_recursive(n,n+6,0,r,r+3,0,6-3,display);
system("pause");
}

到这里,最初的设想得以实现。但是又存在一个问题,算法combination用到了递归,在大规模的输入时,入栈的深度会消耗大量的内存空间,其次,尽管我们自定义的函数可以实现需要的操作,但是用途是很有限的,并且不能灵活的筛选需要的组合序列。
与“组合”相关的字眼就是“排列”了,在STL算法库中,next_permutation是个很好的范例(参见blog中另一文章《stl算法:next_permutation剖析》),能否设计一个类似这样的范型算法解决我们的问题?答案是肯定的。因为在最初的例子中得出组合的两个性质,说明组合间是有序的,由一个组合序列是可以得出下一个组合序列的。
下面是combination的函数原型:

template<class BidIt>
bool next_combination(BidIt n_begin,BidIt n_end,
BidIt r_begin,BidIt r_end);

template<class BidIt,class Pred>
bool next_combination(BidIt n_begin,BidIt n_end,
BidIt r_begin,BidIt r_end
Pred equal);

template<class BidIt>
bool prev_combination(BidIt n_begin,BidIt n_end,
BidIt r_begin,BidIt r_end);

template<class BidIt,class Pred>
bool prev_combination(BidIt n_begin,BidIt n_end,
BidIt r_begin,BidIt r_end,
Pred equal);

算法在线性时间内完成,并且采用迭,由于算法具体实现很繁杂,这里只做简要说明。next_combination & prev_combination均采用从末端向前索引,根据比较N,R当前元素设置标记并产生下一组合序列。

函数用法:
Ex:
#include <iostream>
#include "combination.h"

using namespace std;

int main(){
char n[] = "abcdefg";
char r[] = "abc";
int count = 0;
do{
cout << r << endl;
++count;
}
while (next_combination(n,n+7,r,r+3));
cout << count << endl;
cout << "finished" << endl;
system("pause");
}

若采用prev_combination,则需替换r为最大组合序列char r[] = “efg”。

注:本文算法虽全由我书写完成但并非全由我设计,借鉴于网络和书籍,欢迎讨论。

源代码下载:
RndZnhlb.rar (2.05 KB) [原创]算法:combination in c++


[此贴子已经被作者于2007-8-26 1:21:24编辑过]

搜索更多相关主题的帖子: combination 算法 
2007-08-26 01:20
aipb2007
Rank: 8Rank: 8
来 自:CQU
等 级:贵宾
威 望:40
帖 子:2879
专家分:7
注 册:2007-3-18
收藏
得分:0 
自己支持一个,就在我自己bccn博客上发表过,可以算原创吧?
老大觉得不妥先知会一声,我有原创恐惧症啊!

Fight  to win  or  die...
2007-08-26 01:23
HJin
Rank: 6Rank: 6
等 级:贵宾
威 望:27
帖 子:401
专家分:0
注 册:2007-6-9
收藏
得分:0 
absoulutely original work!

+1.

By the way, you may also consider repeatable Combination and Permutation:

for example, {1, 2, 3} has 3^3=27 repeatable permutations.




I am working on a system which has no Chinese input. Please don\'t blame me for typing English.
2007-08-26 07:08
aipb2007
Rank: 8Rank: 8
来 自:CQU
等 级:贵宾
威 望:40
帖 子:2879
专家分:7
注 册:2007-3-18
收藏
得分:0 
回复:(HJin)absoulutely original work!+1.By the ...
呵呵`,对,我没考虑到“重复”的这点。

但是我想了下,实现机制都是一样的,只是在赋值上的微小不同。

就难的写了,hoho~~~~~

Fight  to win  or  die...
2007-08-26 09:55
冰的热度
Rank: 2
等 级:禁止访问
威 望:5
帖 子:404
专家分:0
注 册:2006-12-2
收藏
得分:0 
楼主居然不提我的名字,只是说"论坛的朋友",太说不过去了吧

太伤自尊了

不过你还真有时间来给大家详细分析这个算法,佩服! 我就没那么都时间了.

最好的方法是大家把代码考下来,放到VC++6.0里运行,单步调试,

就知道每一步的意思和每一细节的作用了.

然后自己细细品位,理解算法讲究的就是一个字"悟"!

悟性不好别人说在多也没用.

回来说说楼主的贴子,好像在CSDN上见过,其他网上也有类似的贴子,都是没有人机交互能力,

也就是说都是在程序代码中把要参加排列组合的数定义好,然后一按运行键程序一下就运行完了.

用户根本没有输入任何东西,或者说没有对外接口.也就是不能与外界勾通,它是孤立的.所以没有什么使用价值.

也就是因为这一点,我才写了那个算法,可以人机交互,用户可以输入他想要参加排列组和的数.

大家可以去我的贴子<<排列组合算法>>看看

科学是永恒之迷...... 我的博客http://blog..cn/u/1267727974 阅读我的blog,懂与不懂都是收获!
2007-08-26 16:09
aipb2007
Rank: 8Rank: 8
来 自:CQU
等 级:贵宾
威 望:40
帖 子:2879
专家分:7
注 册:2007-3-18
收藏
得分:0 
回复:(冰的热度)楼主居然不提我的名字,只是说
呵呵,由于写在blog里的,我写"冰的热度",别人也不知道吧,别介意。

文章提及的算法肯定不是我首创,我只是做了比较全的整理而已。
你提的用户定义数据,从我第2个范型算法开始就可以实现,并且操作都是可以自定义的,不是仅仅输出而已。

至于后面的迭代版本,是统一stl中排列算法next_permutation来设计的,灵活性就更大了。

ps:指出一点,组合和排列是不同的概念,不能混为一谈,呵呵~

Fight  to win  or  die...
2007-08-26 17:02
冰的热度
Rank: 2
等 级:禁止访问
威 望:5
帖 子:404
专家分:0
注 册:2006-12-2
收藏
得分:0 

对,排列和组合是不同


科学是永恒之迷...... 我的博客http://blog..cn/u/1267727974 阅读我的blog,懂与不懂都是收获!
2007-08-26 19:23
yuyunliuhen
Rank: 6Rank: 6
等 级:贵宾
威 望:20
帖 子:1435
专家分:0
注 册:2005-12-12
收藏
得分:0 
一个先
在网吧就不看了,到学校再慢慢看吧

Go confidently in the  directions of your dreams,live the life you have imagined!Just do it!
It is no use learning without thinking!
2007-08-27 09:48
aipb2007
Rank: 8Rank: 8
来 自:CQU
等 级:贵宾
威 望:40
帖 子:2879
专家分:7
注 册:2007-3-18
收藏
得分:0 


快回学校了吧,我也是!还要补考呢~郁闷的很~

Fight  to win  or  die...
2007-08-27 11:51
上杉冰枫
Rank: 1
等 级:新手上路
帖 子:108
专家分:0
注 册:2006-6-24
收藏
得分:0 
这个东西貌似好像在哪见过?

因为→№頦§縎£銘→訫‰ 所以→№從此々吥£唁→愛‰
2007-10-05 03:03
快速回复:[原创]算法:combination in c++
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.019723 second(s), 10 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved