查找子集时,元素与元素数量之间的二进制比较背后的逻辑是什么?

时间:2019-05-23 04:03:44

标签: c++ bit-manipulation

请考虑一本书(https://cses.fi/book/book.pdf)中的以下问题: “我们首先考虑生成一组n个元素的所有子集的问题。例如,{0,1,2}的子集为φ,{0},{1},{2},{0,1} ,{0,2},{1,2}和{0,1,2}。有两种常见的生成子集的方法:我们可以执行递归搜索,也可以利用整数的位表示。”

本书中的解决方案编号2(第48页,PDF第58页)如下:

for (int b = 0; b < (1<<n); b++) {
    vector<int> subset;
    for (int i = 0; i < n; i++) {
        if (b&(1<<i)) subset.push_back(i);
    }
}

我的问题是:为什么(b&(1<<i))比较有效?到底在后台做什么?我已经手动测试了{0,1}的子集,它可以完美地工作,但是为什么它们之间的比较起作用呢? b是元素数量的计数器,(1<<i)从我的理解上基本上等于2 i 。为什么一切正常?好像魔术。

PS。我知道这本书是C ++的不好参考,我会读更多的书,以便进行算法解释。

3 个答案:

答案 0 :(得分:3)

为集合中的每个元素分配不同的2(1、2、4、8等)的幂作为ID。每个子集是集合中各种元素的组合,可以用ID的组合表示。在子集中添加ID会得到一个唯一的编号。

这也可以反过来进行:从0到2 n -1的每个数字都代表该集合的子集,其中0代表空集合(因为没有元素存在),而2代表 n -1是其中包含所有元素的子集。将数字从0递增到2 n -1将枚举所有可能的子集。

1 << i对应于其中一个ID,b & (1 << i)将检查当前子集以查看元素i是否属于其中。

答案 1 :(得分:3)

将整数视为位字符串,而不是数字值

在这里,b中的每一位代表相应的元素是否在子集中。从0到2 n 的循环遍历n位的所有可能状态,因为每个状态都是0到2 n 之间的整数。因此,b的值将代表所有可能的子集。

为了将b的值转换为相应的子集,我们在每个元素上循环并测试该元素是否在子集中。如果在b中设置了相应的位,则将元素添加到向量中。要测试i中的第b位是否已设置,我们计算b & (1 << i)

&是按位与的,因此,只有在两个操作数中都设置了该位的情况下,结果的一位才会被设置。 1 << i是位掩码,第i位被置位,所有其他位均未置位。当我们计算b1 << i的按位与时,第i位以外的所有其他位都不会被设置,因为在位掩码中未设置相应的位。仅当i中的第i位被置位时,第b位将被置位。因此,如果第i位被置1,我们将得到除0之外的值,否则置0。由于从int转换为bool会测试该值是否不为0,因此如果设置了i的第b位,则if语句的主体将执行。

答案 2 :(得分:0)

它使用整数作为二进制字符串。第n位为1表示此子集中存在第n个元素。类似地,为0表示它不存在于子集中。 & (1<<i)用于检查第i位是否为1(换句话说,是否设置了第i位)