组合算法 C++高效实现 (二进制辅助法)

1.什么是数学中的组合?


排列不同的是,在组合中取出元素的顺序则不在考虑之中。从n个元素中取出k个元素,这k个元素可能出现的组合数的总数量为:

以1,2,3,4,5中选2个数为例,总共的组合为:

1,2

1,3

1,4

1,5

2,3

2,4

2,5

3,4

3,5

4,5


2.在计算机中如何高效的实现排列算法?


乍一看,这个问题并不简单。有递归的实现方式,效率较低,还比较难,先不考虑。我们注意到一种以二进制的思想的实现方式,比较高效,来讲讲它。

首先还是需要讲下上次的排列算法中比较高效的实现方式,就是把1,2,3的全排列,换算成求一个三位数,由1,2,3组成,从小到大所有的可能性。

123 < 132 < 213 < 231 < 312 < 321


我们还是以1,2,3,4,5中选2个数为例。

我们这次的排列非常的像,我们用一个五个数的数组表示一个5位数的二进制数字,(其中1表示选中该数字,0则不是)这样用一个二进制数来表示一个排列。如果这个二进制遍历所有的可能性(0~31),且只有两个1组成的话,就是一个我们想要的排列结果。这里我们换成十进制从左往右换算,发现刚好是从小到大。

1,2 (1,1,0,0,0) -- 3(十进制)

1,3 (1,0,1,0,0) -- 5

2,3 (0,1,1,0,0) -- 6

1,4 (1,0,0,1,0) -- 9

2,4 (0,1,0,1,0) -- 10

3,4 (0,0,1,1,0) -- 12

1,5 (1,0,0,0,1) -- 17

2,5 (0,1,0,0,1) -- 18

3,5 (0,0,1,0,1) -- 20

4,5 (0,0,0,1,1) -- 24


如何用代码实现呢?

需要用以下策略:

1.m 选 n, 初始化m个数,它们都是0,前n个都变成1,表示最小的二进制。

2.如何找到下一个较大的数呢?因为我们这里的二进制是从左往右,所以,当发现一个1,0时,我们把它改成0,1的时候,这个数就变大了!

3.根据策略2的话(0,1,1,0,0)--6下一个二进制数应该是(0,1,0,1,0)--10,但是比(0,1,1,0,0)要大的下一个数应该是(1,0,0,1,0)--9。所以

我们要把1,0换成0,1的时候,还要把0,1中0的前面所有1都移到最前面,这样才是最小的数,大家理解下这句。因为我们的二进制是从左往右的。

代码如下,非常简短。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // MyCombination.cpp : Defines the entry point for the console application.  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include <iostream>  
  6. #include <vector>  
  7. #include <algorithm>  
  8.   
  9. using namespace std;  
  10.   
  11.   
  12. void printResult(vector<int>& vecInt, int t[]){  
  13.     for(int i = 0; i < vecInt.size(); ++i){  
  14.         if(vecInt[i] == 1){  
  15.             cout << t[i] << " ";  
  16.         }  
  17.     }  
  18.     cout << endl;  
  19. }  
  20.   
  21. bool compare(int a, int b){  
  22.     if(a > b){  
  23.         return true;  
  24.     }else{  
  25.         return false;  
  26.     }  
  27. }  
  28.   
  29. void combination(int t[], int c, int total){  
  30.     //initial first combination like:1,1,0,0,0  
  31.     vector<int> vecInt(total,0);  
  32.     for(int i = 0; i < c; ++i){  
  33.         vecInt[i] = 1;  
  34.     }  
  35.   
  36.     printResult(vecInt, t);  
  37.   
  38.     for(int i = 0; i < total - 1; ++i){  
  39.         if(vecInt[i] == 1 && vecInt[i+1] == 0){  
  40.             //1. first exchange 1 and 0 to 0 1  
  41.             swap(vecInt[i], vecInt[i+1]);  
  42.   
  43.             //2.move all 1 before vecInt[i] to left  
  44.             sort(vecInt.begin(),vecInt.begin() + i, compare);  
  45.   
  46.             //after step 1 and 2, a new combination is exist  
  47.             printResult(vecInt, t);  
  48.   
  49.             //try do step 1 and 2 from front  
  50.             i = -1;  
  51.         }  
  52.     }  
  53.       
  54. }  
  55.   
  56.   
  57. int _tmain(int argc, _TCHAR* argv[])  
  58. {  
  59.     const int N = 5;   
  60.     int t[N] = {1, 2, 3, 4, 5};  
  61.     combination(t, 2, N);  
  62.     system("pause");  
  63.     return 0;  
  64. }  



3.如何求所有的组合呢?


如果你理解了上面的东西,我们再来思考一个简单的问题,如何求1,2,3 的所有组合结果:



别害怕,这个问题比上面的还要简单,也是二进制的思想,我们用一个3位的二进制来表示一个结果,刚好是从0~7

{}        000

1        001

2        010

1,2     011

3        100

2,3     110

1,2,3  111


代码如下(位运算不懂的话,看这篇文章《来谈谈C++ 位运算 & | << >> ^ ~ %》):

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // MyCombination.cpp : Defines the entry point for the console application.  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include <iostream>  
  6. #include <vector>  
  7. #include <algorithm>  
  8.   
  9. using namespace std;  
  10.   
  11.   
  12. void printEachResult(int t[], int index, int total){  
  13.   
  14.     for(int i = 0; i < total; ++i){  
  15.         if((index>>i)&1 == 1){  
  16.             cout << t[i] << " ";  
  17.         }  
  18.     }  
  19.     cout << endl;  
  20. }  
  21.   
  22. void combination(int t[],int count){  
  23.     for(int i = 0; i < (1<<count); ++i){  
  24.         printEachResult(t, i, count);  
  25.     }  
  26. }  
  27.   
  28.   
  29. int _tmain(int argc, _TCHAR* argv[])  
  30. {  
  31.     const int N = 3;   
  32.     int t[N] = {1, 2, 3};  
  33.     combination(t,N);  
  34.     system("pause");  
  35.     return 0;  
  36. }  

组合算法 C++高效实现 (二进制辅助法)


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值