我正在研究关于位计数的不同方法,或给定整数的人口计数方法,在这段时间里,我试图弄清楚以下算法是如何工作的
pop(x)=-sum(x<<i) where i=0:31
我认为在计算 x 的每个值之后,我们会得到
x+2*x+4*x+8*x+16*x+..............+2^31*x =4294967294*x
如果我们将它乘以 -1,我们得到-4294967294*x
,但是它如何计算位数?请帮助我很好地理解这个方法。谢谢
我相信你的意思
正如Hacker's Delight一书的封面所示,该符号表示左旋转而不是左移,这将产生错误的结果和否决票。
此方法有效,因为旋转将导致x的所有二进制数字出现在所有术语中的每个可能的位中,并且因为 2 的补码。
举一个更简单的例子。考虑只有 4 个二进制数字的数字,其中数字可以表示为ABCD
,那么求和意味着:
ABCD // x <<rot 0
+ BCDA // x <<rot 1
+ CDAB // x <<rot 2
+ DABC // x <<rot 3
我们注意到每一列都有 A、B、C、D。现在,ABCD
实际上意味着“2³ A + 2² B + 2¹ C + 2⁰ D”,所以总和就是:
2³ A + 2² B + 2¹ C + 2⁰ D
+ 2³ B + 2² C + 2¹ D + 2⁰ A
+ 2³ C + 2² D + 2¹ A + 2⁰ B
+ 2³ D + 2² A + 2¹ B + 2⁰ C
——————————————————————————————————————————————————————
= 2³(A+B+C+D) + 2²(B+C+D+A) + 2¹(C+D+A+B) + 2⁰(D+A+B+C)
= (2³ + 2² + 2¹ + 2⁰) × (A + B + C + D)
(A + B + C + D) 是x的人口计数, (2³ + 2² + 2¹ + 2⁰) = 0b1111 在 2 的补码中是 -1,因此总和是人口计数的负数。
该参数可以很容易地扩展到 32 位数字。
#include <stdio.h>
#include <conio.h>
unsigned int f (unsigned int a , unsigned int b);
unsigned int f (unsigned int a , unsigned int b)
{
return a ? f ( (a&b) << 1, a ^b) : b;
}
int bitcount(int n) {
int tot = 0;
int i;
for (i = 1; i <= n; i = i<<1)
if (n & i)
++tot;
return tot;
}
int bitcount_sparse_ones(int n) {
int tot = 0;
while (n) {
++tot;
n &= n - 1;
}
return tot;
}
int main()
{
int a = 12;
int b = 18;
int c = f(a,b);
printf("Sum = %d\n", c);
int CountA = bitcount(a);
int CountB = bitcount(b);
int CntA = bitcount_sparse_ones(a);
int CntB = bitcount_sparse_ones(b);
printf("CountA = %d and CountB = %d\n", CountA, CountB);
printf("CntA = %d and CntB = %d\n", CntA, CntB);
getch();
return 0;
}