• 全部
  • 问答

请问高手们:怎么才可以直接输出组合啊??

mis98ZB 日电卓越软件科技(北京)有限公司 开发总监  2002-01-31 02:36:43
有两种情况:
1。不放回抽样,从N个数中任选M个的所有组合。(M<=N)
2。放回抽样,每次从N个数中选一个,选M次的所有组合。(M=N好象是一个突变点)

唉,数学学的太菜了,老大们,帮忙补补课啊!
...全文
52 点赞 收藏 5
写回复
5 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
mis98ZB 2002-02-01
万分感谢版主starfish(海星)的代码!!!
先仔细研究一下,有不懂的地方还望指教。
也感谢intfree()和shshsh_0510()的指点。

资料一下子多了起来,先看看,
想清楚了再提问。
回复
starfish 2002-02-01
现在是不是流行用计算机算体育彩票呀~~
怎么这么多人问这个问题,sigh~~

下面的代码是我写的用来计算组合数的一个类:

#ifndef _COMBINATION_H_
#define _COMBINATION_H_

/*
combination.h
这个头文件定义了一个Combinatin类
利用该类可以生成从m个数中取出n个数的组合数
并且过滤出满足一定条件的组合数
注意,这里的m, n 都必须是0~255之间的整数
*/

#include <iostream>
#include <iomanip>
#include <vector>
#include <list>
using namespace std;
typedef unsigned char byte;
typedef vector<byte> ByteArray;

class Combination : public list<ByteArray>
{
protected:
byte m, n; // 生成从m个数中取出n个数的组合
vector<bool> enabled; // 用来标记1~m中哪些数字可以使用
ByteArray LowBound, HighBound; // 记录组合数中每一位的可取值范围
bool repeatable; // 标记生成的组合数中的数字是否可以重复出现

protected:

// 回溯搜索组合数的第i位
void SearchComb(int i, ByteArray& L);

public:

/*
功能: 类Combination的构造器
参数: m 组合数中每一位可取得最大整数
n 组合数的位数
说明:
例如,如果要生成从整数1~30中取出7个整数的组合数,
应该声明如下:
Combination combs(30, 7);
*/
Combination(byte m, byte n);

/*
功能:设置组合数中第i位的取值范围
参数:i 设置组合数中第i位数的取值范围,1 <= i <= n
lbound 取值下界, 1 <= lbound <= m
hbound 取值上界, 1 <= hbound <= m
说明:
例如,如果要设置组合数的1位取值范围为3~5
则应该调用:
SetBound(1, 3, 5);
如果要固定组合数的第一位为4,则应该调用
SetBound(1, 4, 4);

注意:
1. 这里的i从1开始计数
2. 如果参数i, lbound, hbound超出了允许的范围,
则调用该方法不会有任何结果
3. 组合数中每一位的默认取值范围是1~m,如果原来设置了
第i位的取值范围,现在想取消原来的设置,则应该调用:
SetBound(i, 1, m);
*/
void SetBound(byte i, byte lbound, byte hbound);

// 清除原来设置的所有的取值范围
void ResetAllBound();

// 在组合数中禁用数字x
void Disable(byte x);

// 在组合数中禁用从a到b所有的数字
void Disable(byte a, byte b);

// 在组合数中重新允许使用数字x
void Enable(byte x);

// 在组合数中重新允许使用从a到b所有的数字
void Enable(byte a, byte b);

// 生成组合数
void GenerateCombination();

// 设置组合中的数字是否可以重复
void SetRepeatable(bool repeatable);

// 这是一个虚函数
// 可以在派生类中加入你自己的过滤器
virtual void Filter() { }
};



// 回溯搜索组合数的第i位
// 注意,搜索出的组合数是从小到大排列的
void Combination::SearchComb(int i, ByteArray& L) {
if (i == n) {
this->push_back(L);
} else {
int begin;
if (i == 0) {
begin = LowBound[i];
} else {
begin = LowBound[i];
if ( (repeatable == false) && (LowBound[i] < L[i-1] + 1) ) {
begin = L[i-1] + 1;
}
}
for (int j = begin; j <= HighBound[i]; j++) {
if (enabled[j]) {
if (repeatable == false) enabled[j] = false;
L[i] = j;
SearchComb(i+1, L);
if (repeatable == false) enabled[j] = true;
}
}
}
}


/*
功能: 类Combination的构造器
参数: m 组合数中每一位可取得最大整数
n 组合数的位数
说明:
例如,如果要生成从整数1~30中取出7个整数的组合数,
应该声明如下:
Combination combs(30, 7);
*/
Combination::Combination(byte m, byte n) {
this->m = m;
this->n = n;
this->repeatable = false;
LowBound.resize(n);
HighBound.resize(n);
ResetAllBound();
enabled.resize(m + 1);
Enable(1, m);
}


/*
功能:设置组合数中第i位的取值范围
参数:i 设置组合数中第i位数的取值范围,1 <= i <= n
lbound 取值下界, 1 <= lbound <= m
hbound 取值上界, 1 <= hbound <= m
说明:
例如,如果要设置组合数的1位取值范围为3~5
则应该调用:
SetBound(1, 3, 5);
如果要固定组合数的第一位为4,则应该调用
SetBound(1, 4, 4);

注意:
1. 这里的i从1开始计数
2. 如果参数i, lbound, hbound超出了允许的范围,
则调用该方法不会有任何结果
3. 组合数中每一位的默认取值范围是1~m,如果原来设置了
第i位的取值范围,现在想取消原来的设置,则应该调用:
SetBound(i, 1, m);
*/
void Combination::SetBound(byte i, byte lbound, byte hbound) {
if( lbound >= 1 && hbound <= m && i >= 1 && i <= n) {
i--;
LowBound[i] = lbound;
HighBound[i] = hbound;
}
}


// 清除原来设置的所有的取值范围
void Combination::ResetAllBound() {
for (int i = 0; i < n; i++) {
LowBound[i] = 1;
HighBound[i] = m;
}
}

void Combination::Disable(byte x) {
if (x >=1 && x <= m) {
enabled[x] = false;
}
}

void Combination::Disable(byte a, byte b) {
for (byte i = a; i <= b; i++) {
Disable(i);
}
}

void Combination::Enable(byte x) {
if (x >=1 && x <= m) {
enabled[x] = true;
}
}

void Combination::Enable(byte a, byte b) {
for (byte i = a; i <= b; i++) {
Enable(i);
}
}

// 设置组合中的数字是否可以重复
void Combination::SetRepeatable(bool repeatable) {
this->repeatable = repeatable;
}


// 生成组合数
void Combination::GenerateCombination() {
ByteArray L(n); // 用来存储每一组的组合数
this->clear();
SearchComb(0, L); // 搜索组合数
Filter(); // 调用过滤器进行过滤
}




// 重载输出运算符

ostream operator<<(ostream& out, const ByteArray& array) {
for (int i = 0; i < array.size(); i++) {
out<< setw(4) << int( array[i] );
}
return out;
}

ostream operator<<(ostream& out, const Combination& combs) {
list<ByteArray>::const_iterator iter;
for (iter = combs.begin(); iter != combs.end(); iter++) {
out << *iter << endl;
}
out << "Total count is : " << combs.size() << endl;
return out;
}

#endif

下面是一个测试用的例子 test.cpp:

#include "combination.h"
#include <vector>
#include <algorithm>
using namespace std;

class MyCombs : public Combination
{
private:
// 这个过滤器将求出组合数中两两相减的值
// 如果所有不重复的差值总数等于一个常数X则保留该组合
// 否则删除该组合
void Filter_A() {
const int X = 18;

list<ByteArray>::iterator iter;
for( iter = this->begin(); iter != this->end(); )
{
ByteArray& array = *iter;

vector<int> temp; // 注意,temp一定要在这个for循环里面声明

// 下面这个二重for循环用来求两辆相减的绝对值,并且把不重复的元素放在temp中
for(int i = 0; i < array.size(); i++ ) {
for( int j = i+1; j < array.size(); j++ ) {
int d = abs(int(array[i]) - int(array[j]));

// 如果在temp中找不到该差值
if( find( temp.begin(), temp.end(), d) == temp.end() ) {
temp.push_back(d);
}
}
}

// 直接判断temp中的元素的数目就可以了,因为原来temp中的值都是互不相同的
if( temp.size() != 18 ) {
list<ByteArray>::iterator old_iter = iter;
iter++;
this->erase(old_iter);
} else {
iter++;
}

}

}

void Filter_B() {
// 可以在这里加入你自己的过滤器
}

public:

MyCombs(byte m, byte n) :
Combination(m, n)
{

}

// 覆盖父类的虚方法,
// 在这个函数中填入你自己的过滤器即可
// 如果有多个过滤器,将其写成函数,
// 在这个函数中一一调用即可
void Filter() {
//Filter_A();
// 你也可以加上Filter_B(); Filter_C()...., etc
}
};


int main()
{
MyCombs combs(4, 3);
combs.SetBound(1, 1, 4);
combs.SetBound(2, 1, 4);
combs.SetBound(3, 1, 4);
combs.SetRepeatable(true);

combs.GenerateCombination();

cout << "result is : " << endl;
cout << combs << endl;

return 0;
}


回复
intfree 2002-01-31
我记得starfish的主页上就有,
而且这个问题在csdn上出现过多次了,
你可以搜索前面的帖子。

good luck!
回复
shshsh_0510 2002-01-31
卢开澄的《组合数学》中有好几种生成算法:
回复
mis98ZB 2002-01-31
除了生成排列、并检测重复以外,有什么别的妙门吗?
回复
相关推荐
发帖
数据结构与算法
创建于2007-08-27

3.2w+

社区成员

数据结构与算法相关内容讨论专区
申请成为版主
帖子事件
创建了帖子
2002-01-31 02:36
社区公告
暂无公告