子集迭代的子集如何工作?

时间:2018-01-13 17:07:32

标签: algorithm bit-manipulation bitmask

我读了
for ( x = y; x > 0; x = ( y & (x-1) ) )

生成位掩码y的所有子集。

此迭代如何工作?有任何直观的解释吗?

来源:http://codeforces.com/blog/entry/45223

请参阅次优解决方案部分。

2 个答案:

答案 0 :(得分:0)

Intuition:作为一个数字,位掩码y不能超过y个子集。因此,通过倒计时x,您可以保证通过位掩码命中y的每个子集。但是这会产生很多重复。想想1101。如果你从那里算起来并用y掩盖,序列就会消失。 110111001001100010011000等。通过为x分配掩蔽操作的结果,可以跳到最后一次出现。

证明:这可以通过归纳进行简单的证明。显然,对于长度为1的位串,此过程有效。只有两个子集10按此顺序发出。

现在假设此过程适用于长度为N的位串。假设Z是长度为N的位串。如果你创建了bitstring 0Z,那么你遵循与Z相同的顺序,因为减法不会打开更高阶的位。如果您创建了bitstring 1Z,则会发生以下情况:对于第一个2^nnz(Z)步骤,将遵循原始Z序列,前缀为1。对于最后的2^nnz(Z)步骤,将遵循原始的Z序列,前缀为0。由于该过程访问较小序列的每个元素两次,第一次前置1,第二次前置0,我们得出结论,该过程会发出1Z的每个子集。

总之,我们看到该过程适用于所有位串。

答案 1 :(得分:0)

这里使用的第一个简单事实是,如果我们说,取值7(二进制111)并开始重复递减(一直到0)我们将通过二进制表示

111, 110, 101, 100, 011, 010, 001, 000

以相当明显的方式表示原始3组的所有可能子集。

第二个事实是二进制“递减x”(“从x减去1”)意味着:从最低有效位开始反转x的所有位(最右边)一个和左边一直到(并包括)1表示中的第一个x。 “向左”在这里意味着“在增加位重要性的方向上”。

E.g。

00001000 - 1 = 00000111, i.e. we invert the `1000` tail
01010101 - 1 = 01010100, i.e. we invert just the `1` tail
10000000 - 1 = 01111111, i.e. we invert the whole thing
and so on

递减操作“关闭”二进制表示中的最低有效1位,并“打开”其右侧的所有零位。

现在,第三个事实是,在您的情况下,1 x位始终是1y的一部分,因为我们以{{1}开头并且在每次迭代时执行x = y

当我们执行x = (whatever) & y时,我们“关闭”(设置为x - 10中的最低1和“开启”(设为x } 1中距离最低0右侧的所有x

当我们在1中使用& y时,我们会关闭{{1> x = (x - 1) & yy的一些 原始位1}}和“打开”xy的所有低原始位。

此时已经很明显,这个操作只是x的“掩盖减量”:通过执行x我们只是在假设x = (x - 1) & y的情况下递减x的值。只有y掩盖的位形成x的值,而所有其他位只是可忽略的“填充位”。

要在递减7的情况下绘制与上述示例并行的内容,y的初始值可能会显示为10010100。操作x = (x - 1) & y将此值视为“分布式7”(非正式地说)。 x将继续执行以下值

1..1.1.., 1..1.0.., 1..0.1.., 1..0.0.., 0..1.1.., 0..1.0.., 0..0.1.., 0..0.0..

其中.指定x的“pading”位,它们并不真正参与这个“屏蔽的减量”操作(实际上它们将是0)。注意与7的原始示例相似。

相关问题