在接受采访时我被问到以下问题:
int foofoo(unsigned int u) {
unsigned int foo = u;
do{
u = u/2;
foo -= u;
}while(u > 0);
return foo;
}
我被要求告诉这个函数做了什么,我能够发现它计算了unsigned int值中的设置位数,但是我无法证明这一点,也许有人可以吗?
答案 0 :(得分:1)
我能够发现它计算了unsigned int值中的设置位数,但我无法证明这一点,也许有人可以吗?
查看m
内的单个位U
。该位有助于U
的价值:
U = ...... + U m 2 ^ m + .....(其中......表示其他位)。
在每个循环中,该位被除以2,然后从U
中减去。当循环完成时,这看起来像位m
下面的行:
U m 2 ^ m - U m 2 ^(m-1) - U m 2 ^(m-2) - .... - U m 2 ^ 0
这可以改写如下:
U m (2 ^ m - (2 ^(m-1)+ 2 ^(m-2)+ .... + 2 ^ 0))
U m (2 ^ m - (2 ^ m -1))
U m (2 ^ m - 2 ^ m + 1)
U m
因此我们可以看出,该位m
以其位值(即0或1)对最终结果做出贡献。这是因为U
中的所有位都适用。因此:
foo
= U n-1 + U n-2 + ... + U m + ... + U 1 + U 0
因此,结果将等于U
中设置的位数。
P.S。我尝试使用上标使公式看起来更好。但是我无法让<sup>
工作。如果您知道如何,请随意编辑公式。
答案 1 :(得分:1)
It is known无符号整数u
可以表示为2的幂之和:
u = A * 2ª + ... + N * 2ⁿ,
其中0 <= a < ... < n
是整数,A, ..., N
是零,或者
那些。如果我们删除零产品条款,则条款数量将相等
到设置位数(1)。例如,1101
b =2⁰+
2²+2³由三个项(2的幂)组成,设置位数也等于3。
这个想法是在这个表示中找到非零项的数量。
如果我们将u
表示为两个权力的总和:
u = 2ª + ... + 2ⁿ
其中0 <= a < ... < n
,则整数除法u = u / 2
可表示为:
u = ( 2ª + ... + 2ⁿ ) / 2
或等同于:
u = ( 2ª / 2 ) + ... + ( 2ⁿ / 2 )
(一般来说,(x + y) / 2 = (x / 2) + (y / 2)
。)
当u
为正时,重复该过程。所以每个术语最终都等于一个。在下一次迭代中,由于整数除法的规则,变为等于零:1 / 2 = 0
。
从u
:foo
中存储的原始值中减去结果值(foo -= u
)。
因此,我们最终会从原始值foo
中减去所有内容,但“除以2”(1 / 2
)除外。
显然,1 / 2
的出现次数等于u
的原始表达式中非零项的数量,它等于设定位的数量(正如我们在上面发现的那样) )。因此,剩余的值“n foo
是”1 / 2
“剩余的总和,即术语的数量,也是设置的位数。
让我们通过u = 13
。
十进制数13
的二进制表示为1101
。因此,十进制表示法中的两个幂的总和是:
u = 2⁰ + 2² + 2³
第一次迭代:
u = u / 2
= (2⁰ + 2² + 2³) / 2
= (2⁰ / 2) + (2² / 2) + (2³ / 2)
= 0 + 2 + 2²
到目前为止我们有一个零:2⁰ / 2 = 1 / 2 = 0
。
第二次迭代:
u = u / 2
= (2 + 2²) / 2
= (2 / 2) + (2² / 2)
= 1 + 2
此迭代中不再有零。第三次迭代:
u = u / 2
= (1 + 2) / 2
= (1 / 2) + (2 / 2)
= 0 + 1
我们在此次迭代中得到了第二个零:1 / 2 = 0
。
第四次迭代给出第三个零:
u = u / 2
= 1 / 2 = 0.
零的数量等于设置的位数,即3
。
答案 2 :(得分:0)
假设输入为N
时输出为M
。然后,当输入为2*N
时,输出仍为M
,当输入为2*N+1
时,输出必须为M+1
(两个语句都来自第一次迭代的分析将输入减少到N
)的循环。
答案 3 :(得分:0)
我回答得有点迟了。
我会以同等形式重写程序来回答......
#include <stdio.h>
unsigned
count(unsigned u) {
unsigned foo=u, u0=0;
do
u0+=u>>=1;
while(u);
return foo-u0;
}
void
main()
{
printf("%u\n", count(7));
}
作为一个例子,让我们看看U=10110
会发生什么(1s在向右移动时在对角线上传播):
10110
1011
101
10
1
如您所见,对于位置N上的每个位1代表数字2^N
(因此用2 ^ N充电),U0将收取2^0+2^1+ ... + 2^(N-1)
,等于{{1} }。
因此,对于设置为1的每个位,2^N-1
将松散1.将U写为二进制和,对于设置为1的每个位,我们将松散1。