遍历各种组合

我一直在写一个 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

posted on 2018-12-26 10:53  luyi07  阅读(1609)  评论(0编辑  收藏  举报

导航