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

问题描述 投票:-3回答:2

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

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

这个迭代如何工作?任何直观的解释?

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

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

algorithm bit-manipulation bitmask
2个回答
0
投票

直觉:作为一个数字,位掩码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。由于该过程访问较小序列的每个元素两次,第一次前置10第二次,我们得出结论,该过程发出1Z的每个子集。

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


0
投票

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

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

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

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

EG

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位,并“打开”其右侧的所有零位。

现在,第三个事实是,在你的情况下,1x位总是1y位的子集,因为我们从x = y开始并在每次迭代时执行x = (whatever) & y

当我们做x - 1时,我们“关闭”(设置为01中最低的x,并且“从1开始”(设置为0)所有x从最低的1到右边。

当我们在& y跟随x = (x - 1) & y时,我们在y“关闭”x的一些原始位并在y“打开”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的原始示例的相似性。

© www.soinside.com 2019 - 2024. All rights reserved.