简体   繁体   English

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

[英]Why does this function count the number of set bits in an integer

I was asked the following question in an interview: 我在接受采访时被问到以下问题:

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

I was asked to tell what does this function do and I was able to find that it counts the number of set bits in an unsigned int value, but I was not able to prove that, maybe someone can? 我被要求告诉这个函数做了什么,我能够发现它计算unsigned int值中的设置位数,但我无法证明,也许有人可以?

I was able to find that it counts the number of set bits in an unsigned int value, but I was not able to prove that, maybe someone can? 我能够发现它计算了unsigned int值中的设置位数,但是我无法证明这一点,也许有人可以?

Look at a single bit m inside U . 看一个比特mU That bit contributes to the value of U like: 这一点有助于U的价值:

U = ...... + U m 2^m + ..... (where the .... represents other bits). U = ...... + U m 2 ^ m + .....(其中......表示其他位)。

In each loop the bit is divived by 2 and then subtracted from U . 在每个循环中,该位被除以2,然后从U减去。 When the loop is complete, this looks like the line below for bit m : 当循环完成时,这看起来像位m下面一行:

U m 2^m - U m 2^(m-1) - U m 2^(m-2) - .... - U m 2^0 U m 2 ^ m - U m 2 ^(m-1) - U m 2 ^(m-2) - .... - U m 2 ^ 0

This can be rewritten like: 这可以改写如下:

U m (2^m - (2^(m-1) + 2^(m-2) + .... + 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 (2^m - 2^m + 1) U m (2 ^ m - 2 ^ m + 1)

U m U m

So from this we can see, that bit m contributes to the final result with its bit-value (ie 0 or 1). 因此我们可以看出,位m以其位值(即0或1)对最终结果做出贡献。 This, of cause, holds true for all bit in U . 这个原因适用于U所有位。 Therefore: 因此:

foo = U n-1 + U n-2 + ... + U m + ... + U 1 + U 0 foo = U n-1 + U n-2 + ... + U m + ... + U 1 + U 0

Consequently, the result will be equal to the number of bits set in U . 因此,结果将等于U设置的位数。

ps I tried to use superscript to make the formulas look better. ps我尝试使用上标使公式看起来更好。 But I couldn't make <sup> work. 但我不能让<sup>工作。 Feel free to edit the formulas if you know how. 如果您知道如何,请随意编辑公式。

The Idea 想法

It is known that an unsigned integer u can be represented as a sum of powers of two: 众所周知 ,无符号整数u可以表示为2的幂之和:

u = A * 2ª + ... + N * 2ⁿ,

where 0 <= a < ... < n are integers, and A, ..., N are either zeroes, or ones. 其中0 <= a < ... < n是整数, A, ..., N是零或1。 If we remove the zero-product terms, the number of terms will be equal to the number of set bits (ones). 如果我们删除零积项,则项的数量将等于设置位数(1)。 For example, 1101 b = 2⁰ + 2² + 2³ consists of three terms (powers of two), and the number of set bits is also equal to three. 例如, 1101 b =2⁰+2²+2³由三个项(2的幂)组成,并且设置位的数量也等于3。

The idea is to find the number of non-zero terms in this representation. 这个想法是在这个表示中找到非零项的数量

Algorithm 算法

If we express u as a sum of powers of two: 如果我们表达u作为两个大国的总和:

u = 2ª + ... + 2ⁿ

where 0 <= a < ... < n , then the integer division u = u / 2 can be expressed as: 其中0 <= a < ... < n ,则整数除法u = u / 2可表示为:

u = ( 2ª + ... + 2ⁿ ) / 2

or, equivalently: 或者,等效地:

u = ( 2ª / 2 ) + ... + ( 2ⁿ / 2 )

(Since, generally, (x + y) / 2 = (x / 2) + (y / 2) .) (因为,通常, (x + y) / 2 = (x / 2) + (y / 2) 。)

The process is repeated while u is positive. u是积极的时候,重复这个过程。 So each term eventually becomes equal to one . 所以每个术语最终都等于一个 In the next iteration it becomes equal to zero due to the rules of integer division : 1 / 2 = 0 . 在下一次迭代中, 由于整数除法的规则,变为等于零1 / 2 = 0

The resulting value ( u ) is subtracted from the original value stored in foo : foo -= u . 从存储在foofoo -= u的原始值中减去结果值( u )。

Thus, we eventually subtract everything from the original value foo , except the "ones divided by two" ( 1 / 2 ) . 因此,我们最终从原始值foo减去所有内容,除了“除以2”( 1 / 2 )之外

Obviously, the number of occurrences of 1 / 2 is equal to the number of non-zero terms in the original expression of u which is equal to the number of set bits (as we found out above). 显然,出现的次数1 / 2等于u的原始表达式中的非零项的数量,其等于设置位的数量(如上所述)。 Hence, the value remaining ìn foo is a sum of " 1 / 2 " leftovers, ie the number of terms which is also the number of set bits. 因此,剩余的值'n foo是“ 1 / 2 ”剩余的总和,即也是设置位数的项数。

Example

Let's visualize the process on example of u = 13 . 让我们在u = 13例子中可视化过程。

Binary representation of the decimal number 13 is 1101 . 十进制数13二进制表示是1101 So the sum of powers of two in decimal notation is: 因此,十进制表示法中的两个幂的总和是:

u = 2⁰ + 2² + 2³

First iteration: 第一次迭代:

u = u / 2
  = (2⁰ + 2² + 2³) / 2
  = (2⁰ / 2) + (2² / 2) + (2³ / 2)
  = 0        + 2        + 2²

We have one zero so far: 2⁰ / 2 = 1 / 2 = 0 . 到目前为止我们有一个零: 2⁰ / 2 = 1 / 2 = 0

Second iteration: 第二次迭代:

u = u / 2
  = (2      + 2²) / 2
  = (2 / 2) + (2² / 2)
  = 1       + 2

No more zeroes in this iteration. 在此迭代中不再有零。 Third iteration: 第三次迭代:

u = u / 2
  = (1      + 2) / 2
  = (1 / 2) + (2 / 2)
  = 0       + 1

We have got the second zero in this iteration: 1 / 2 = 0 . 在这次迭代中我们得到了第二个零: 1 / 2 = 0

Fourth iteration gives the third zero: 第四次迭代给出第三个零:

u = u / 2
  = 1 / 2 = 0.

The number of zeroes is equal to the number of set bits, ie 3 . 零的数量等于设置的比特数,即3

Suppose when the input is N , the output is M . 假设当输入为N ,输出为M Then when the input is 2*N the output is still M , and when the input is 2*N+1 the output must be M+1 (both statements follow from the analysis of the first iteration of the loop which reduces the input to N ). 然后当输入为2*N ,输出仍为M ,当输入为2*N+1 ,输出必须为M+1 (两个语句都来自循环的第一次迭代的分析,这减少了输入到N )。

I am a bit late answering. 我回答得有点迟了。

I would answer by rewriting the program in an equivalent form... 我会以同等形式重写程序来回答......

#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));
}

As an example, let us see what happens for U=10110 (the 1s propagate on diagonal while shifting right): 作为一个例子,让我们看看U=10110会发生什么(1s在向右移动时在对角线上传播):

10110
 1011
  101
   10
    1

As you can see, for each bit 1 on position N, representing the number 2^N (so charging u with 2^N), U0 will be charged with 2^0+2^1+ ... + 2^(N-1) which equals 2^N-1 . 正如您所看到的,对于位置N上的每个位1,表示数字2^N (因此用2 ^ N充电u),U0将被充电2^0+2^1+ ... + 2^(N-1)等于2^N-1

So, for each bit set to 1, U0 will loose 1. Writing U as a binary sum, we loose 1 for each bit set to 1. 因此,对于设置为1的每个位, U0将松散1.将U写为二进制和,对于设置为1的每个位,我们松开1。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM