组合算法
组合:一个集的元素的组合是一个子集。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。
组合算法就结束了,后面有增加再补充。