【问题标题】:Generating bit combination without repetitions (not permunation)生成不重复排列的位组合)
【发布时间】:2015-07-30 13:51:37
【问题描述】:

这是我之前的 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 是错误的组合。在我的代码中有很多错误的组合需要过滤。

【问题讨论】:

  • 你需要按位运算的效率吗?如果不是这样,您可以通过抽象一点来省去很多麻烦(wrt 操纵图表)。
  • 我的目标是编写最有效的函数,所以如果可能的话 - 我想像在我的代码中那样使用按位运算。

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


【解决方案1】:

不确定我是否正确理解您的确切需求,但根据您的示例(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

希望这会有所帮助。

【讨论】:

  • 是的,这正是我想要实现的!非常感谢!
  • 我认为使用do while() 循环可以避免两次调用doStuff(),甚至更好地使用while(true)break 在里面
  • 我不明白,如果x==0 在您更新它时,这将终止停止条件。我发现 while(true) break 丑陋,但它是一个解决方案。
  • 或者(纠正 Slava),使用unsigned intdo while 循环,从x = 0 开始,并在x=(x-1)&amp;subset; 之后移动doStuff 调用(第一次迭代将设置@987654333 @ 到(2^32 - 1) &amp; subset 如果sizeof(unsigned int)=4)。但是谁在乎代码已经足够简单和高效了。 (顺便说一句,非常优雅的解决方案)
【解决方案2】:

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

从所有 1 都在左边,所有 0 都在右边的组合开始 将最左边的 1 和 0 移到右边 如果那个位在它的左边有一个 1 那么 将其左侧的所有 1 一直向左移动 当你到达右边所有 1 和左边所有 0 的组合时,你就完成了

对 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 )。
如果我找到更好的,我会编辑或发布另一个安德烈。

【讨论】:

  • 好的,但是这个解决方案要求其中n=5 集合包含数字(或位位置)1,2,3,4,5。但是n=5 可以包含数字(或位位置)1,2,4,7,12。你写的其实在我的代码里——太多不需要的组合。
  • 查看我帖子的编辑,也许现在解释得更清楚了。
  • 啊,我以为 k 是您想要在每个子集中的元素数。它实际上是每个子集中可能存在的元素。 (关于你的代码,老实说,我没有详细阅读它,我应该有我的错误)
  • 我想我的解释有点令人困惑(很多信息),但我认为现在很清楚我想要实现的目标。通常集合中的所有元素组合(不一定是以下数字)但使用 int 的位表示。那么集合的最大数量显然是32。
  • 如果是这种情况,您可以简单地将您的集合包含n 元素的情况(例如1,2,4,7,123,17,23,42,134n=5)简化为您的集合完全包含的情况位置1,2,...,n 通过在这些位置之间插入 0(例如,如果您有 1,2,3,4,5 的子集 01101 和 (n=5, k=3)但您实际上想要 1,2,4,7,12 的子集,将此子集映射到 0()1(0 )1(00)0(0000)1 = 0101000000001 其中括号中的 0 被插入 0) 在 bitset 中“插入 0”不是很自然,但它可能是最简单的
猜你喜欢
  • 2016-10-01
  • 2014-03-15
  • 2022-08-18
  • 1970-01-01
  • 2011-03-07
  • 1970-01-01
  • 2014-09-29
  • 2018-12-16
相关资源
最近更新 更多