注册 登录
编程论坛 C++教室

[分享][经验][开源]排列组合算法

冰的热度 发布于 2007-08-22 18:05, 4617 次点击
//从M个数中取出N个数组合
#include <iostream>
using namespace std;
void jisuan(int a[],int m,int n,int k,int temp);
int num(int m,int n);
int jiecheng(int a);
void main()
{
int m,n;
cout<<"请输入M和N:"<<endl;
cin>>m>>n;
cout<<"一共 "<<num(m,n)<<" 种组合"<<endl;
int a[100];
jisuan(a,m,n,-1,-1);
}
void jisuan(int a[],int m,int n,int k,int temp)
{
temp++;
if(temp<n)
for(int i=k+1;i<m;i++)
{
a[temp]=i+1; //赋值
jisuan(a,m,n,i,temp); //递归调用
}
else
{
for(int j=0;j<n;j++)
{
cout<<"\t"<<a[j]; //输出
}
cout<<endl;
}
}
//计算个数
int num(int m,int n)
{
int sum;
sum=jiecheng(m)/(jiecheng(n)*jiecheng(m-n));
return sum;
}
//求阶乘
int jiecheng(int a)
{
int h=1;
for(int i=1;i<=a;i++)
h=h*i;
return h;
}

25 回复
#2
HJin2007-08-22 20:41
回复:(冰的热度)[分享][经验][开源]排列组合算法

very nice algorithm for combinations.

1. There are "standard" combination algorithms which uses iteration out there. You may search codeproject.com for "next_combination".
2. Your "num(m, n)" of calculating m choose n could be improved --- num(17, 2) won't work, numOfComb(17, 2) does work. Overflow, overflow, overflow!


int numOfComb(int m, int n)
{
int r=1;
int i;

for(i=1; i<=n; ++i)
{
r *= (m-i+1);
r /= i;
}

return r;
}

#3
冰的热度2007-08-23 21:28

版主说的没错,果然是高手,我收益非浅,谢谢拉

不过以后请用中文!

现改正如下
#include <iostream>
using namespace std;
void jisuan(int a[],int m,int n,int k,int temp);
int numOfComb(int m, int n);
void main()
{
int m,n;
cout<<"请输入M和N:"<<endl;
cin>>m>>n;
cout<<"一共 "<<numOfComb(m,n)<<" 种组合"<<endl;
int a[100];
jisuan(a,m,n,-1,-1);
}
void jisuan(int a[],int m,int n,int k,int temp)
{
temp++;
if(temp<n)
for(int i=k+1;i<m;i++)
{
a[temp]=i+1; //赋值
jisuan(a,m,n,i,temp); //递归调用
}
else
{
for(int j=0;j<n;j++)
{
cout<<"\t"<<a[j]; //输出
}
cout<<endl;
}
}
//计算个数
int numOfComb(int m, int n)
{
int r=1;
int i;

for(i=1; i<=n; ++i)
{
r *= (m-i+1);
r /= i;
}

return r;
}

#4
aipb20072007-08-23 23:15
[QUOTE]不过以后请用中文![/QUOTE]

[QUOTE]I am working on a system which has no Chinese input. Please don't blame me for typing English.[/QUOTE]

他用不了的。
#5
maoguoqing2007-08-24 15:00
英文不错的,LS你六级过了没?
#6
aipb20072007-08-24 15:33
没查,证在寝室的!

估计没有!嘿嘿!
#7
maoguoqing2007-08-24 16:31
我下学期报,好贵哦六级,我们学校最贵
#8
HJin2007-08-24 17:01

/*---------------------------------------------------------------------------
File name: Perm-noLexic.cpp
Author: HJin (email: fish_sea_bird [at] yahoo [dot] com )
Created on: 8/24/2007 01:53:37
Environment: Windows XP Professional SP2 English +
Visual Studio 2005 v8.0.50727.762


Modification history:
===========================================================================

Here is what I wrote a long long time ago. I used one buffer, but the output
is NOT in lexicographic order.

If you want to the lexicographic order, you can use an extra buffer.

冰的热度's algorithm is one of the best if the numbers are 1..n; if the numbers
are not 1..n, say {1, 3, 4, 6, 9}, you have to modify his algorithm.

C++'s next_permutation() and prev_permutation() work for containers and thus,
in general, you may want to use these two "library" functions instead.

Sample output:
---------------------------------------------------------------------------
1: 1 3
2: 1 4
3: 1 6
4: 1 9
5: 3 4
6: 3 6
7: 3 9
8: 3 1
9: 4 6
10: 4 9
11: 4 1
12: 4 3
13: 6 9
14: 6 1
15: 6 3
16: 6 4
17: 9 1
18: 9 3
19: 9 4
20: 9 6
Press any key to continue . . .
*/

#include <iostream>
using namespace std;

// Permutation of n numbers choose k numbers
// k=1..n.
int a[]={1, 3, 4, 6, 9};
int n=5;
int k=2;

void permute();
void do_permute(int m);
void rotate(int m);

int main()
{
permute();

return 0;
}

void permute()
{
do_permute(n);
}

void do_permute(int m)
{
static int count=0;
if(m==n-k)
{
++count;
cout<<count<<": ";
for(int i=0; i<k; ++i)
cout<<a[i]<<" ";
cout<<endl;

return;
}

for(int i=0; i<m; ++i)
{
do_permute(m-1);
rotate(m);
}
}

void rotate(int m)
{
int t=a[n-m];

for(int i=n-m; i<n; ++i)
a[i]=a[i+1];

a[n-1]=t;
}

#9
aipb20072007-08-24 18:09

我看了那个 求组合的泛型算法 ,果然很强大。

用迭代到看懂了,递归那个还是不明白。

有空我整理出来,大家一起研究。嘿嘿~

#10
HJin2007-08-24 18:19
回复:(aipb2007)我看了那个 求组合的泛型算法 ,果...
that is good.

The one with iteration is hard to understand.

I encourage you to write an article about Permutation and Combination and sticky it so that every beginner can benefit from your work.


#11
冰的热度2007-08-24 20:17
哎哎哎,别跑题呀,怎么说到英语6级上去了!

上面 KJin 的话是跟我说的吗?

大概意思是不是让我对这个算法的核心也就是递归写个说明.让初学者理解它是怎么运行的?

其实最好的办法就是在VC++6.0中运行,设置断点,单步调试,这样你就可以知道每一步是怎么回事了!

而且每个变量在每一步是什么状态什么值都一清二楚.

如果我用文字来描述的话,可能会把初学者绕的更晕,不过我还是很愿意写一写

过两天贴出来吧.

另外,KJin在8楼指出的问题,我想可以这样理解,1...n不要理解为具体参加排列组合的数,

而应理解为"第1个"..."第n个"数

如从4个数中取2个,有一种情况是 1 2

不要理解为数就是1和2,而要理解为第1个位置上的数和第二个位置上的数.

这样你可以定义一个数组,把真正要参加排列组合的数放到里面,

然后根据上面得出的下标就可以输出真正要参加排列组合的数了!!

这个任务就交给我吧!我写写看!!都别跟我抢!!!



#12
HJin2007-08-24 20:47
回复:(冰的热度)哎哎哎,别跑题呀,怎么说到英语6级上...

To 冰的热度:


Very good!

Keep your work going and open a new post with a title such as

"An introduction to algorithms for permutation and combination"

For your reference, I've zipped all algorithms/simulations I've written or collected over years. Hope it helps.


只有本站会员才能查看附件,请 登录

#13
冰的热度2007-08-24 20:55

我已经写出来了,见下面:
不过参加排列组合的数我默认为正数

#include <iostream>
using namespace std;
void jisuan(int a[],int m,int n,int k,int temp);
int numOfComb(int m, int n);
int comb[100];
void main()
{
int m,n;
int i=0;
cout<<"请输入要参加排列组合的数(以 -1 结束):"<<endl; //注意参加排列组合的数不能有-1,如果非要有-1
cin>>comb[i]; //可以把这个条件改一改
while(comb[i]!=-1)
{
i++;
cin>>comb[i];
}
m=i;
if(comb[0]==-1)
{
exit(0);
}
cout<<"请输入要提取的个数 n:"<<endl;
cin>>n;
cout<<"一共 "<<numOfComb(m,n)<<" 种组合"<<endl;
int a[100];
jisuan(a,m,n,-1,-1);
}
void jisuan(int a[],int m,int n,int k,int temp)
{
temp++;
if(temp<n)
for(int i=k+1;i<m;i++)
{
a[temp]=i+1; //赋值
jisuan(a,m,n,i,temp); //递归调用
}
else
{
for(int j=0;j<n;j++)
{
cout<<"\t"<<comb[a[j]-1]; //输出
}
cout<<endl;
}
}
//计算个数
int numOfComb(int m, int n)
{
int r=1;
int i;

for(i=1; i<=n; ++i)
{
r *= (m-i+1);
r /= i;
}

return r;
}

#14
野比2007-08-24 21:00

Precious package, HJin..

#15
冰的热度2007-08-24 21:01

不好意思,在看你的资料之前我就写出来了,而且有两个版本

上面那个是定义了一个全局数组comb[100],这样不用改变jisuan()的参数

下面这个是定义一个局部数组,要改变一下jisuan()的参数

#include <iostream>
using namespace std;
void jisuan(int a[],int m,int n,int k,int temp,int com[]);
int numOfComb(int m, int n);
void main()
{
int m,n;
int i=0;
int comb[100];
cout<<"请输入要参加排列组合的数(以 -1 结束):"<<endl;
cin>>comb[i];
while(comb[i]!=-1)
{
i++;
cin>>comb[i];
}
m=i;
if(comb[0]==-1)
{
exit(0);
}
cout<<"请输入要提取的个数 n:"<<endl;
cin>>n;
cout<<"一共 "<<numOfComb(m,n)<<" 种组合"<<endl;
int a[100];
jisuan(a,m,n,-1,-1,comb);
}
void jisuan(int a[],int m,int n,int k,int temp,int com[])
{
temp++;
if(temp<n)
for(int i=k+1;i<m;i++)
{
a[temp]=i+1; //赋值
jisuan(a,m,n,i,temp,com); //递归调用
}
else
{
for(int j=0;j<n;j++)
{
cout<<"\t"<<com[a[j]-1]; //输出
}
cout<<endl;
}
}
//计算个数
int numOfComb(int m, int n)
{
int r=1;
int i;

for(i=1; i<=n; ++i)
{
r *= (m-i+1);
r /= i;
}

return r;
}

#16
aipb20072007-08-24 21:06
to 冰的热度:

你那个能出结果,但是入栈太深,消耗很大的空间,这些空间其实是不必要的。
用递归回溯来解决问题一般在确定输入规模(较小)的情况下比较实用。

其次,你给出组合,仅仅就给出了组合,不能在具体每一个组合上进行操作,使你的
程序实用性降低。
#17
aipb20072007-08-24 21:08
HJin这么好的东西都共享了,呵呵,下下来研究啦!

谢谢!
#18
冰的热度2007-08-24 21:10
哇,这么一会就又有人回贴了,动作太快了吧

怎么样,我厉害吧

低调低调,很想和HJin,aipb2007等人交个朋友

可以把你们的站内邮箱告诉我吗?

然后我把我的QQ号和MSN一邮件形式告诉你们

我不想以公开的方式公布我的QQ,MSN和邮箱,所以辛苦你们一下喽.

告诉我你们的站内邮箱就行,就是本站内的邮箱
#19
冰的热度2007-08-24 21:16
不是吧,在我写上一贴的时候又有人回贴了.我不得不再写这一贴

aipb2007考虑问题挺深的,佩服佩服.

"不能在具体每一个组合上进行操作"是什么意思,具体说说
#20
aipb20072007-08-24 21:26
回复:(冰的热度)不是吧,在我写上一贴的时候又有人回...

ex:
m = 4,n = 2;

12是其中一个组合,另一个问题中我要分别对每个组合,这里12,或者34做些处理。
就需要保存这个组合的信息。

这个意思。

#21
冰的热度2007-08-24 22:02

明白你的意思,一开始我也打算这样来着.

#22
冰的热度2007-08-26 16:43

这个算法我在CSDN的论坛上的C++版块上也发表了,发贴人叫yonghengzhimi,其实就是我.

大家不要大惊小怪,yonghengzhimi和冰的热度是一个人,就是我

其实是汉字"永恒之迷",但是CSDN不让用汉字,只好用拼音了

#23
冰的热度2007-08-29 17:20
谁给我加个精,

这都不算精品吗?
#24
ioriliao2007-09-04 16:03
以下是引用冰的热度在2007-8-23 21:28:47的发言:

版主说的没错,果然是高手,我收益非浅,谢谢拉

不过以后请用中文!


I am working on a system which has no Chinese input. Please don't blame me for typing English.

#25
xjlsgcjdtc2007-09-04 23:40
回复:(冰的热度)[分享][经验][开源]排列组合算法
阶乘算法结果如果大于16位就存不下了啊,下面有一个算法:
#define max 200
void ditui(int a[],int k)
{
int m=a[0];
int *b=new int[m+1];
int i,j;
int r;
int carry;
for(i=1;i<=m;i++) b[i]=a[i];
for(j=1;j<k;j++)
{
for(carry=0,i=1;i<=m;i++)
{
r=(i<=a[0]?a[i]+b[i]:a[i])+carry;
a[i]=r%10;carry=r/10;
}
if(carry) a[++m]=carry;
}
delete [] b;
a[0]=m;
}
#26
冰的热度2007-10-08 18:17
1