用于获取向量元素的所有组合的递归与位掩码

时间:2014-05-07 11:34:35

标签: c++ algorithm recursion combinations bitmask

在为编程比赛(如ACM,Code Jam等)练习时,我遇到了一些问题,需要我生成一些矢量元素的所有可能组合。

假设我有向量{1,2,3},我需要生成以下组合(顺序并不重要):

1
2
3
1 2
1 3
2 3
1 2 3

到目前为止,我已经使用以下代码完成了它:

void getCombinations(int a)
{
    printCombination();
    for(int j=a;j<vec.size();j++)
    {
        combination.pb(vec.at(j));
        getCombinations(j+1);
        combination.pop_back();
    }
}

调用getCombinations(0);为我做的工作。但是有更好(更快)的方式吗?我最近听说过bitmasking。据我所知,它仅仅适用于1到2 ^ N-1之间的所有数字,我将该数字转换为二进制数,其中1和0表示该元素是否包含在组合中。

我如何有效地实现这一点?如果我以标准方式将每个数字转换为二进制(通过一直除以2)然后检查所有数字,这似乎浪费了很多时间。有没有更快的方法?我应该继续使用递归(除非我遇到一些递归无法完成工作的大数字(堆栈限制))?

1 个答案:

答案 0 :(得分:3)

您可以获得的组合数量为2 ^ n,其中n是您的元素数量。您可以将0到2 ^ n -1之间的每个整数解释为掩码。在您的示例(元素1,2,3)中,您有3个元素,因此掩码将是000,001,010,011,100,101,110和111.让掩码中的每个位置代表您的一个元素。对于具有1的位置,取相应的元素,否则如果该位置包含0,则将该元素保留。例如,数字5将是掩模101,并且它将生成该组合:1,3。

如果你想拥有一个快速且相对较短的代码,你可以这样做:

#include <cstdio>
#include <vector>

using namespace std;

int main(){

    vector<int> elements;

    elements.push_back(1);
    elements.push_back(2);
    elements.push_back(3);

    //  1<<n is essentially pow(2, n), but much faster and only for integers
    //  the iterator i will be our mask i.e. its binary form will tell us which elements to use and which not
    for (int i=0;i<(1<<elements.size());++i){
        printf("Combination #%d:", i+1);
        for (int j=0;j<elements.size();++j){
            //  1<<j shifts the 1 for j places and then we check j-th binary digit of i
            if (i&(1<<j)){
                printf(" %d", elements[j]);
            }
        }
        printf("\n");
    }

    return 0;
}