遍历各种组合
我一直在写一个 C++ 程序,有个地方需要遍历所有组合可能性:
m个整数,0,1,2,3,4,5,...,m-1
从中取 n 个,n<m,存在 m!/n!/(m-n)! 种可能性,遍历这些可能性:
假设在一种可能性中,取出的数分别为:i1, i2, i3, ..., in,可以让它对应一个整数
N = i1* m0 + i2 * m1 + ... + in*mn-1
那么任何一种组合,都唯一地对应一个 N 值,但不是每个 N 值都对应一种组合。
N 值个数约为 mn , 所以这种遍历的效率为
m(m-1)...(m-n+1)/n!/mn
当 n 值比较大时,效率会低一些,比如,m=12,n=6, 则效率为
12!/6!/6!/126 = 0.2228
m=20,n=8,则效率为
20!/8!/12!/208= 4.9207 * 10-6
非常低。所以很多时间都用来检查和排除不合格的情况:同一元素取了多次的情况。
================= 我是分割线 ====================================
后来在网上逛了逛,看了几排别人的代码,领会了意思,自己写了一个排列组合遍历:
void permutation(int n, int * a, int m, int k, int * b){
if(k==0){
for(int i=0;i<m;i++)cout<<b[i]<<"\t";
cout<<endl;
count_permutation++;
}
else{
for(int i=0;i<n;i++){
b[k-1]=a[i];
int temp = a[i]; a[i]=a[n-1]; a[n-1]=temp;
permutation(n-1, a, m, k-1, b);
temp = a[i]; a[i]=a[n-1]; a[n-1]=temp;
}
}
}
int count_combination=0;
void combination(int n, int * a, int m, int k, int * b){
if(k==0){
for(int i=0;i<m;i++)cout<<b[i]<<"\t";
cout<<endl;
count_combination++;
}
else{
for(int i=k-1;i<n;i++){
b[k-1]=a[i];
combination(i, a, m, k-1, b);
}
}
}
int main(){
int n,m;
cout<<"n="; cin>>n;
cout<<"m="; cin >>m;
int * a = new int [n];
for(int i=0;i<n;i++) a[i] = i+1;
int * b = new int [m];
clock_t t_start = clock();
permutation(n, a, m, m, b);
cout<<"count_permutation="<<count_permutation<<endl;
combination(n, a, m, m, b);
clock_t t_end = clock();
cout<<"It took me "<<(double)(t_end - t_start)/CLOCKS_PER_SEC<<"s to make permutations"<<endl;
t_start = t_end;
cout<<"count_combination="<<count_combination<<endl;
t_end = clock();
cout<<"It took me "<<(double)(t_end - t_start)/CLOCKS_PER_SEC<<"s to make combinations"<<endl;
return 0;
}
运行结果:
n=3
m=2
3 1
2 1
1 2
3 2
1 3
2 3
count_permutation=6
It took me 8.4e-05s to make permutations
1 2
1 3
2 3
count_combination=3
It took me 7.8e-05s to make combinations
本来挺害怕递归耗时,运行起来发现还可以,注掉cout行,跑 n=20,m=8 时,有 784,143,104 种排列,遍历用时 56 s,有 125970 种组合,遍历用时 1.266 ms。
鸣谢博主:https://blog.csdn.net/hf19931101/article/details/79452799