生成比特组合而不重复(不是弹药)

时间:2015-07-30 13:51:37

标签: c++ algorithm graph bit-manipulation combinations

这是我之前的question关于找到下一位排列的问题。在我看来,我必须修改我的代码以实现与下一位排列类似的东西,但却完全不同。

我在int的位表示中编码有关顶点邻居的信息。例如,如果n = 4(n - 图形顶点)和图形已满,我的顶点数组看起来像:

vertices[0]=14 // 1110 - it means vertex no. 1 is connected with vertices no. 2, 3, and 4
vertices[1]=13 // 1101 - it means vertex no. 2 is connected with vertices no. 1, 3, and 4
vertices[2]=11 // 1011 - it means vertex no. 3 is connected with vertices no. 1, 2, and 4
vertices[3]=7  // 0111 - it means vertex no. 4 is connected with vertices no. 1, 2, and 3

第一个(主要)for循环是从0到2 ^ n(因为2 ^ n是一组子集的数量)。

因此,如果n = 4,则有16个子集:

{empty}, {1}, ..., {4}, {0,1}, {0,2}, ..., {3,4}, {0,1,2}, ..., {1,2,3}, {1,2,3,4}

这些子集由for循环

中的索引值表示
for(int i=0; i < 2^n; ++i) // i - represents value of subset

我们说n = 4,实际上是i = 5 //0101。我想检查一下这个子集的子集,所以我想查一下:

0000
0001
0100
0101

现在我正在生成1位集的所有位置换,然后置换2位置换...依此类推(直到我达到BitCount(5)= 2)并且我只采取我想要的置换(通过if语句) )。这是太多不必要的计算。

所以我的问题是,如何生成所有可能的组合而没有重复(n,k),其中n - 图形顶点和k - i (stated above)中的位数

我的实际代码(生成所有位排列并选择错误):

for (int i = 0; i < PowerNumber; i++) 
    {
        int independentSetsSum = 0;
        int bc = BitCount(i);

        if(bc == 1) independentSetsSum = 1;
        else if (bc > 1)
        {           
            for(int j = 1; j <= bc; ++j)
            {
                unsigned int v = (1 << j) - 1; // current permutation of bits 
                int bc2 = BitCount(j);
                while(v <= i)
                {
                    if((i & v) == v)
                        for(int neigh = 1; neigh <= bc2; neigh++)
                            if((v & vertices[GetBitPositionByNr(v, neigh) - 1]) == 0)
                                independentSetsSum ++;

                    unsigned int t = (v | (v - 1)) + 1;  
                    v = t | ((((t & -t) / (v & -v)) >> 1) - 1);     
                } 
            }
        }
    }

所有这一切都是因为我必须计算n的每个子集的独立集合数。

修改

我想在不创建任何数组的情况下这样做,或者通常我想避免分配任何内存(两个都没有向量)。

一点点解释: n=5 //00101 - it is bit count of a number i - stated abovek=3,集合中的数字(数字表示位位置设置为1)

{
1, // 0000001
2, // 0000010
4, // 0001000
6, // 0100000
7  // 1000000
}

正确的组合是{1,2,6} // 0100011,但{1,3,6} // 0100101是错误的组合。在我的代码中,有许多错误的组合,我必须过滤。

2 个答案:

答案 0 :(得分:3)

我不确定我是否正确理解了您的确切需求,但根据您的示例(i==5),您希望获得给定子集的所有子集。

如果是这种情况,您可以直接生成所有这些子集。

int subset = 5;
int x = subset;
while(x) {
    //at this point x is a valid subset
    doStuff(x);
    x = (x-1)&subset;
}
doStuff(0) //0 is always valid

希望这有帮助。

答案 1 :(得分:0)

我生成所有可能组合的第一个猜测是以下规则(抱歉,如果它有点难以阅读)

start from the combination where all the 1s are on the left, all the 0s are on the right

move the leftmost 1 with a 0 on its immediate right to the right
if that bit had a 1 on its immediate left then
  move all the 1s on its left all the way to the left

you're finished when you reach the combination with all the 1s on the right, and all the 0s on the left

对n = 5和k = 3应用这些规则可以得到:

  

11100
  11010个
  10110个
  01110
  11001个
  10101个
  01101
  10011个
  01011
  00111

但这并不会让我感到非常高效(和/或优雅) 更好的方法是通过仅翻转有限数量的位来找到迭代这些数字的方法(我的意思是,你总是需要翻转O(1)位来达到下一个组合,而不是O(n) ),这可能允许更有效的迭代(有点像https://en.wikipedia.org/wiki/Gray_code) 如果我找到更好的话,我会编辑或发布另一个和。