为什么此函数计算整数中的设置位数

时间:2017-05-18 06:07:40

标签: c bit bits

在接受采访时我被问到以下问题:

int foofoo(unsigned int u) {
    unsigned int foo = u;
    do{
        u = u/2;
        foo -= u;
    }while(u > 0);
    return foo;
}

我被要求告诉这个函数做了什么,我能够发现它计算了unsigned int值中的设置位数,但是我无法证明这一点,也许有人可以吗?

4 个答案:

答案 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

ufoo中存储的原始值中减去结果值(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。