组合算法实现

13 篇文章 0 订阅

组合算法

组合:一个集的元素的组合是一个子集。S的一个k-组合是S的一个有k个元素的子集。组合具有无序性。若两个子集的元素完全相同仅顺序不同,看作同一个组合。

组合符号: 

这里介绍4中常见的组合形式:

1.完全组合。

2.不重复完全组合。

3.选择组合。

4.有重复选择组合。

1.完全组合

从n元素集S中取出所有的k个元素子集(0 <= k <= n)。规模数为nC0 + nC1 + ... + nCn = 2^n。

1)位运算_实现
//完全组合
typedef char ElemType;
void full_combination_bit(const ElemType *A, int N)
{
	//unsigned int mask = (1 << N) - 1;
	unsigned int mask = 0;
	for (int i = 0; i < N; ++i)
	{
		mask |= unsigned int(1<<i);
	}

	int count = 1;
	for (unsigned int i = mask; i > 0; i = (i - 1)&mask)
	{
		cout<<"["<<count<<"\t] = ";
		for (int j = 0; j < N; ++j)
		{
			if (1 == ((i >> j)&1))
			{
				cout<<A[j]<<" ";
			}
		}
		cout<<endl;
	}
	cout<<"["<<count<<"\t] = "<<endl; //输出空子集

	return;
}

输入:A B C

输出:


2)递归_实现
//完全组合
//l:表示上次已经选择出l个元素
//p:表示上次选择的元素为A[p-1],本次需要选择的元素为A[p]
typedef char ElemType;
void _full_combination_recursion(const ElemType *A, int N, ElemType *choosed, int l, int p)
{
	static int count  = 1;
	cout<<"["<<count<<"\t] = ";
	for (int i = 0; i < l; ++i)
	{
		cout<<choosed[i]<<" ";
	}
	cout<<endl;
	++count;

	for (int i = p; i < N; ++i)
	{
		choosed[l] = A[i];
		_full_combination_recursion(A, N, choosed, l + 1, i + 1);
	}
}
void full_combination_recursion(const ElemType *A, int N)
{
	ElemType *choosed = new ElemType[N];
	_full_combination_recursion(A, N, choosed, 0 , 0);

	delete [] choosed;
	return;
}

输入:A B C

输出:

2.不重复完全组合

由于输入的集合中可能会出现相同的元素,完全组合会出现相同的组合,需要过滤。规模数待分析。

1)递归_实现
typedef char ElemType;

typedef struct stElemNode
{
	ElemType value;
	unsigned char num;
} ElemTypeEx;

template< class T > void SafeDelete( T*& pVal )
{
	delete pVal;
	pVal = NULL;
}

template< class T > void SafeDeleteArray( T*& pVal )
{
	delete[] pVal;
	pVal = NULL;
}

//压缩数组ElemType A[N]为ElemTypeEx A_Compress[M]
void Compress(const ElemType *A, int N, ElemTypeEx *A_Compress, int &M)
{
	int i = 0, j = 0;
	M = 0;
	for (i = 0; i < N; ++i)
	{
		for (j = 0; j < M; ++j)
		{
			if (A_Compress[j].value == A[i])
			{
				++A_Compress[j].num;
				break;
			}
		}
		if (j == M)
		{
			A_Compress[M].value = A[i];
			A_Compress[M].num = 1;
			++M;
		}
	}
}

//统计不同元素的个数
int Count_unrepeat(const ElemType *A, int N)
{
	int count = 0;
	for (int i = 0; i < N; ++i)
	{
		int j = 0;
		for (j = 0; (A[j] != A[i]) && (j < i); ++j)
		{
		}

		if (j == i) //遍历完了还没有找到 +1
		{
			++count;
		}
	}

	return count;
}

//不重复完全组合
//l:表示上次已经选择出l个元素
//p:表示上次选择的元素为A[p-1],本次需要选择的元素为A[p]
void _unrepeat_combination_recursion(ElemTypeEx *A, int N, ElemType *choosed, int l, int p)
{
	static int count  = 1;
	cout<<"["<<count<<"\t] = ";
	for (int i = 0; i < l; ++i)
	{
		cout<<choosed[i]<<" ";
	}
	cout<<endl;
	++count;

	for (int i = p; i < N; ++i)
	{
		if (A[i].num > 0)
		{
			--A[i].num; //可用次数减1
			choosed[l] = A[i].value;
			_unrepeat_combination_recursion(A, N, choosed, l + 1, i); //此处为i 与前面的i+1 区别
			++A[i].num; //可用次数恢复
		}
	}

	return;
}
void unrepeat_combination_recursion(const ElemType *A, int N)
{
	ElemTypeEx *A_Ex = new ElemTypeEx[Count_unrepeat(A,N)];
	int M = 0;
	Compress(A, N, A_Ex, M);

	ElemType *choosed = new ElemType[N];

	_unrepeat_combination_recursion(A_Ex, M, choosed, 0, 0);

	SafeDeleteArray(A_Ex);
	SafeDeleteArray(choosed);
}

完全组合与不重复完全组合比较

完全组合输入:A A C

输出:

不重复完全组合输入:A A C

输出:

可以看出:后者将重复的组合 A 、A C 过滤了。

3.选择组合

从n元素集S中取出k元素子集。规模数为nCk。

1)位运算_实现

代码中调用的函数Hamming_weight,功能为:统计二进制数字中1的个数

//选择组合
typedef char ElemType;
void select_combination_bit(const ElemType *A, int N, int M)
{
	//unsigned int mask = (1 << N) - 1;
	unsigned int mask = 0;
	for (int i = 0; i < N; ++i)
	{
		mask |= unsigned int(1<<i);
	}
	int count = 1;
	for (unsigned int i = mask; i > 0; i = (i - 1)&mask)
	{
		if (Hamming_weight(i, 3) == M)
		{
			cout<<"["<<count<<"\t] = ";
			for (int j = 0; j < N; ++j)
			{
				if (1 == ((i >> j)&1))
				{
					cout<<A[j]<<" ";
				}
			}
			cout<<endl;
			++count;
		}
	}

	if (M == 0)
	{
		cout<<"["<<count<<"\t] = "<<endl; //输出空子集
	}

	return;
}

输入:A B C D k = 2

输出:


2)递归_实现
//选择组合 C(N,M)
//l:表示上次已经选择出l个元素
//p:表示上次选择的元素为A[p-1],本次需要选择的元素为A[p]
typedef char ElemType;
void _select_combination_recursion(const ElemType *A, int N, int M, ElemType *choosed, int l, int p)
{
	if (l == M)
	{
		static int count  = 1;
		cout<<"["<<count<<"\t] = ";
		for (int i = 0; i < l; ++i)
		{
			cout<<choosed[i]<<" ";
		}
		cout<<endl;
		++count;
	}

	for (int i = p; i < N; ++i)
	{
		choosed[l] = A[i];
		_select_combination_recursion(A, N, M, choosed, l + 1, i + 1);
	}
}
void select_combination_recursion(const ElemType *A, int N,  int M)
{
	ElemType *choosed = new ElemType[N];
	_select_combination_recursion(A, N, M, choosed, 0 , 0);

	delete [] choosed;
	return;
}

输入:A B C D k = 2

输出:

4.有重复选择组合

 从n 个不同元素中每次取一个,放回后再取下一个,如此连续取k次所得的k个元素子集。规模数为(n+k-1)Ck。

1)递归_实现
//重复选择组合C(N+M-1,M)
//l:表示上次已经选择出l个元素
//p:表示上次选择的元素为A[p-1],本次需要选择的元素为A[p]
typedef char ElemType;
void _repeat_combination_recursion(const ElemType *A, int N, int M, ElemType *choosed, int l, int p)
{
	if (l == M)
	{
		static int count  = 1;
		cout<<"["<<count<<"\t] = ";
		for (int i = 0; i < l; ++i)
		{
			cout<<choosed[i]<<" ";
		}
		cout<<endl;
		++count;
		return;
	}

	for (int i = p; i < N; ++i)
	{
		choosed[l] = A[i];
		_repeat_combination_recursion(A, N, M, choosed, l + 1, i);
	}
}

void repeat_combination_recursion(const ElemType *A, int N,  int M)
{
  	ElemType *choosed = new ElemType[M];
	_repeat_combination_recursion(A, N, M, choosed, 0 , 0);

	SafeDeleteArray(choosed);
	return;
}

输入:A B C  k = 3

输出:

验证规模数:(n+k-1)Ck = (3+3-1)C3 = 5C3 = 10。

 

组合算法就结束了,后面有增加再补充。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值