C++:排列组合算法

转载请注明出处

1 介绍

排列(Permutation)和组合(Combination)是两个基础的数学概念。

计算排列与组合可以解决一些实际的工程问题,掌握排列组合计算的方法是十分重要的。

目前,网上已经有一些计算排列组合的算法,比如[1]。

这里我也给出一个组合计算方法。该计算方法采用了分治的思想,代码实现采用了递归的方式。


2 组合算法

2.1 设计思路

组合问题:在序列An={1,2,3,4,5,6,...,n}中选择m个数一共有C(n,m)种组合,求解所有的组合。

例如,C(3,2)=3. 所有的组合分别是{1,2},{1,3},{2,3}。

思路:

我的算法采用了分治的思想:将一个大的问题拆分成很多个子问题,先解决子问题,所有子问题的解共同组成了大问题的解。

接下来我以求组合C(5,3)为例进行说明:

假设C(5,3)的所有组合形成的集合是E。我们可以将组合结果E分成两类:A类是含有5的组合,B类是不含5的组合;

同理,我们可以将B类分成B1和B2两类:B1类是含有4的组合(且不含5的组合),B2类是不含4的组合(且不含5的组合);

同理,我们可以将B2类分成B21和B22两类:B21类是含有3的组合(且不含5、4的组合),B22类是不含3的组合(且不含5、4的组合)。注意,此时B22类是不成立的(应为不含3、4、5,只剩下1、2两个元素。而例子要求解C(5,3)至少需要3个元素)。

以上分类方法可以保证E=B21 U B1 U A

其中,

对于A类组合,我们只需要求解子问题C(4,2),之后再在子问题的结果中加入5即可得到A类组合;

对于B1类组合,我们只需要求解子问题C(3,2),之后再在子问题的结果中加入4即可得到B1类组合;

对于B21类组合,我们只需要求解子问题C(2,2),之后再在子问题的结果中加入3即可得到B21类组合;

A类组合、B1类组合、B21组合组成了C(5,3)的所有组合。

可以发现,问题C(5,3)已经降维成三个子问题:C(4,2),C(3,2) 和C(2,2)。利用递归的方法即可实现最终结果的求解。


2.2 算法复杂度

2.2.1 时间复杂度

所有的组合算法的时间复杂度都至少是C(n,m)=n!/[m!*(n-m!)]。本算法的时间复杂度也是阶乘量级的。

2.2.2 空间复杂度

由于采用了递归的实现方式,本算法的空间复杂度很高,很有可能造成内存溢出。建议采用非递归的方法实现组合计算。


2.3 源代码

下面是源代码:

/*****************************************************************************************************************************
        时间复杂度:
        空间复杂度:
        功能:求排列组合Cij
        输入参数:
                int i                :        总数
                int j                :          组合数
                vector<int>r:        用于存储临时结果的向量,大小必须等于num 
                int num                :        组合数
                vector<vector<int> > & result        :        用于存储最终所有结果的二维向量 
        返回参数:
                void
        注意: 
                首先建立两个向量作为函数的输入参数                
                vector<int> r(num);                                //num为组合数 
                vector<vector<int> > result;        //存储最终结果 
        使用样例:
                vector<int> resulttemp(3);
                vector<vector<int> > result;
                Cij(6,3,resulttemp,3,result); 
*****************************************************************************************************************************/


void Cij(int i, int j,vector<int> &r,int num,vector<vector<int> > & result)
{
        //排列组合公式Cij
        //cout << n << ' ' << i << ' ' << j << endl;
        if (j == 1)
        {
                for (int k = 0; k < i; k++)
                {
                        vector<int> temp(num);
                        r[num - 1] = k;
                        for (int i = 0; i < num;i++)
                        {
                                temp[i]=r[i];
                                //cout << r[i] << ' ';
                        }
                        result.push_back(temp);
                        //cout << endl;
                }
        }
        else if (j == 0)
        {
                //do nothing!
        }
        else
        {
                for (int k = i; k >= j; k--)
                {
                        r[j-2] = k-1;
                        Cij(k - 1, j - 1,r,num,result);
                }
        }
}

2.4 测试结果

下面是测试结果:


3 排列算法

排列算法可以采用STL中的next_permutation函数。


4 参考

[1]  http://www.cnblogs.com/shuaiwhu/archive/2012/04/27/2473788.html  
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值