【问题标题】:How to implement Bitcount using only Bitwise operators?如何仅使用按位运算符实现位计数?
【发布时间】:2010-09-28 16:57:28
【问题描述】:

任务是仅使用位运算符来实现位计数逻辑。我让它工作正常,但我想知道是否有人可以提出更优雅的方法。

只允许按位运算。没有“如果”、“为”等

int x = 4;

printf("%d\n", x & 0x1);
printf("%d\n", (x >> 1) & 0x1);
printf("%d\n", (x >> 2) & 0x1);
printf("%d\n", (x >> 3) & 0x1);

谢谢。

【问题讨论】:

    标签: c bit-manipulation


    【解决方案1】:

    来自http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel

    unsigned int v; // count bits set in this (32-bit value)
    unsigned int c; // store the total here
    
    c = v - ((v >> 1) & 0x55555555);
    c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
    c = ((c >> 4) + c) & 0x0F0F0F0F;
    c = ((c >> 8) + c) & 0x00FF00FF;
    c = ((c >> 16) + c) & 0x0000FFFF;
    

    编辑:诚然,它进行了一些优化,使其更难阅读。读起来更容易:

    c = (v & 0x55555555) + ((v >> 1) & 0x55555555);
    c = (c & 0x33333333) + ((c >> 2) & 0x33333333);
    c = (c & 0x0F0F0F0F) + ((c >> 4) & 0x0F0F0F0F);
    c = (c & 0x00FF00FF) + ((c >> 8) & 0x00FF00FF);
    c = (c & 0x0000FFFF) + ((c >> 16)& 0x0000FFFF);
    

    这五个步骤中的每一步都将相邻的位以 1、2、4 为一组相加,以此类推。 该方法基于分而治之。

    在第一步中,我们将位 0 ​​和 1 相加并将结果放入两位段 0-1,将位 2 和位 3 相加并将结果放入两位段 2-3 等...

    在第二步中,我们将两位 0-1 和 2-3 相加并将结果放入四位 0-3,将两位 4-5 和 6-7 相加并将结果放入四位 4-7 等...

    例子:

    So if I have number 395 in binary 0000000110001011 (0 0 0 0 0 0 0 1 1 0 0 0 1 0 1 1)
    After the first step I have:      0000000101000110 (0+0 0+0 0+0 0+1 1+0 0+0 1+0 1+1) = 00 00 00 01 01 00 01 10
    In the second step I have:        0000000100010011 ( 00+00   00+01   01+00   01+10 ) = 0000 0001 0001 0011
    In the fourth step I have:        0000000100000100 (   0000+0001       0001+0011   ) = 00000001 00000100
    In the last step I have:          0000000000000101 (       00000001+00000100       )
    

    等于5,就是正确的结果

    【讨论】:

    • 谢谢。对不起,我不完全理解为什么会这样。你能解释一下吗
    • 我添加了一个解释,这需要一段时间,因为我必须自己弄清楚发生了什么。 +1 你的问题迫使我理解:P
    • 也可以看看这个答案,一步一步的遍历这个函数:stackoverflow.com/a/15979139/31818
    【解决方案2】:

    我会使用预先计算好的数组

    uint8_t set_bits_in_byte_table[ 256 ];
    

    此表中的i-th 条目存储字节i 中设置的位数,例如set_bits_in_byte_table[ 100 ] = 3 因为在十进制 100 (=0x64 = 0110-0100) 的二进制表示中有 3 个 1 位。

    那我试试

    size_t count_set_bits( uint32_t const x ) {
        size_t count = 0;
        uint8_t const * byte_ptr = (uint8_t const *) &x;
        count += set_bits_in_byte_table[ *byte_ptr++ ];
        count += set_bits_in_byte_table[ *byte_ptr++ ];
        count += set_bits_in_byte_table[ *byte_ptr++ ];
        count += set_bits_in_byte_table[ *byte_ptr++ ];
        return count;
    }
    

    【讨论】:

      【解决方案3】:

      这是answer的简单说明:

      a b c d       0 a b c       0 b 0 d    
      &             &             +
      0 1 0 1       0 1 0 1       0 a 0 c
      -------       -------       -------
      0 b 0 d       0 a 0 c       a+b c+d
      

      所以我们正好有 2 位来存储 a + b 和 2 位来存储 c + d。 a = 0、1 等等,所以我们需要 2 位来存储它们的总和。在下一步中,我们将有 4 位来存储 2 位值之和等。

      【讨论】:

        【解决方案4】:

        几个有趣的解决方案here

        如果上面的解决方案太无聊,这里有一个免条件测试或循环的 C 递归版本:

          int z(unsigned n, int count);
          int f(unsigned n, int count);
        
          int (*pf[2])(unsigned n, int count) = { z,f };
        
          int f(unsigned n, int count)
          {
             return (*pf[n > 0])(n >> 1, count+(n & 1));
          }
        
          int z(unsigned n, int count)
          {
             return count;
          }
        
          ...
          printf("%d\n", f(my_number, 0));
        

        【讨论】:

          猜你喜欢
          • 2020-01-23
          • 2015-11-27
          • 1970-01-01
          • 2011-07-14
          • 1970-01-01
          • 1970-01-01
          • 2013-05-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多