我读了
for ( x = y; x > 0; x = ( y & (x-1) ) )
生成位掩码y的所有子集。
此迭代如何工作?有任何直观的解释吗?
来源:http://codeforces.com/blog/entry/45223
请参阅次优解决方案部分。
答案 0 :(得分:0)
Intuition:作为一个数字,位掩码y
不能超过y
个子集。因此,通过倒计时x
,您可以保证通过位掩码命中y
的每个子集。但是这会产生很多重复。想想1101
。如果你从那里算起来并用y
掩盖,序列就会消失。 1101
,1100
,1001
,1000
,1001
,1000
等。通过为x
分配掩蔽操作的结果,可以跳到最后一次出现。
证明:这可以通过归纳进行简单的证明。显然,对于长度为1的位串,此过程有效。只有两个子集1
和0
按此顺序发出。
现在假设此过程适用于长度为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
位始终是1
位y
的一部分,因为我们以{{1}开头并且在每次迭代时执行x = y
。
当我们执行x = (whatever) & y
时,我们“关闭”(设置为x - 1
)0
中的最低1
和“开启”(设为x
} 1
中距离最低0
右侧的所有x
。
当我们在1
中使用& y
时,我们会关闭{{1> x = (x - 1) & y
中y
的一些 原始位1}}和“打开”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
的原始示例相似。