我找了一些方法来做popcount(设置位数)。我找到了这个,这里
http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
unsigned int v; // count the number of bits set in v
unsigned int c; // c accumulates the total bits set in v
for(c = 0; v; c++)
{
v &= v - 1; // clear the least significant bit set
}
尝试一些例子,它确实有效。二进制操作/表示的哪些属性使其有效?
你能否暗示一些关于popcount和二进制表示的进一步阅读?
答案 0 :(得分:4)
你开始时的初始v
最初设置了n位。
游戏的要点是在循环的每次迭代中减少1位数。这样,我们可以计算在我们到达n = 0的点之前所需的循环迭代次数,以计算出初始n的值。
请注意,如果n = 0,则v = 0,因此循环将在此时停止。但只要v> 0,我们将至少运行一次循环体。在每次迭代中,我们最终得到一个具有少1位设置的v。
这就是原因。我们需要的第一个属性是v && v == v
。现在v
是一个位序列(确切的位数取决于您的机器/操作系统),您可以从最重要到最不重要的顺序排序。当您递减v
时,我们可以注意以下内容:
v
时,所有比v [k]更重要的位将不更改。因此,ANDing v
及其递减将保留所有更重要的位,但将v [k]设置为0.并且根据定义,所有位都不如v [k],即v [ k-1] ... v [0]已经为0,因为v [k]是“最低有效位为1”。因此,在ANDing之后,所有不太重要的位将保持为0.结果是v && (v - 1)
包含的设置比v
少1位。
答案 1 :(得分:0)
从1
位减去0
位会将该位转换为1
并导致从左侧的下一位借位,从而导致减去1
那里也是。这会继续向左级联,直到您到达1
位,从1
减去1
为0
。此时减法结束。您已将所有0
转换为1
直至第一个设置位,并将该位从1
转换为0
。
当您and
之前和之后的值时,before
在第一位右侧有零,而after
在该位有零。由于任何以零为零的值都为零,因此您将原始值保留为零,并将单个位设置为零。