如何在不使用任何班次的情况下计算正位数?

时间:2011-11-20 10:42:45

标签: algorithm bit-manipulation bitvector

在一次求职面试中,我曾被要求计算位向量结构中的正数(即设置为“1”)位数(如无符号整数或长整数)。我的解决方案在C#中非常简单:

int CountBits(uint input)
{
   int reply = 0;
   uint dirac = 1; 
   while(input != 0)
   {
      if ((input & dirac) > 0) reply++;
      input &= ~dirac;
      dirac<<=1;
   }
   return reply;
}

然后我被要求在不使用任何轮班的情况下解决任务:既不明确(如“&lt;&lt;”或“&gt;&gt;”)也不隐式(如乘以2)。使用2的潜在行(如0,1,2,4,8,16等)的“强力”解决方案也不会这样做。

有人知道这样的算法吗?

据我所知,它应该是一种或多或少的通用算法,它不依赖于输入位向量的大小。允许所有其他按位操作和任何数学函数。

3 个答案:

答案 0 :(得分:13)

有这个x & (x-1)黑客,如果你暂时考虑一下,就会在整数中清除最后1。休息是微不足道的。

答案 1 :(得分:1)

某些处理器具有人口计数指令。如果没有,我相信这是最快的方法(对于32位):

int NumberOfSetBits(int i) {
  i = i - ((i >> 1) & 0x55555555);
  i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
  return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}

有关完整说明,请参阅此链接:http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel

至于没有轮班的情况,我认为使用查找表将是最好的答案:

int NumberOfSetBits(int i) {
  unsigned char * p = (unsigned char *) &i;
  return BitsSetTable256[p[0]] + BitsSetTable256[p[1]] + 
     BitsSetTable256[p[2]] + BitsSetTable256[p[3]];
}

// To initially generate the table algorithmically:
BitsSetTable256[0] = 0;
for (int i = 0; i < 256; i++) {
  BitsSetTable256[i] = (i & 1) + BitsSetTable256[i / 2];
}

答案 2 :(得分:1)

就像安东尼布莱克所描述的那样,但我猜是有点可读。

uint32_t bitsum(uint32_t x)
{
    // leafs (0101 vs 1010)
    x = (x & 0x55555555) + ((x >> 1) & 0x55555555);

    // 2nd level (0011 vs 1100)
    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);

    // 3rd level (nybbles)
    //x = (x & 0x0f0f0f0f) + ((x >> 4) & 0x0f0f0f0f);
    x = (x & 0x07070707) + ((x >> 4) & 0x07070707);

    /*
    // 4th level (bytes)
    //x = (x & 0x00ff00ff) + ((x >> 8) & 0x00ff00ff);
    x = (x & 0x000f000f) + ((x >> 8) & 0x000f000f);

    // 5th level (16bit words)
    //return (x & 0x0000ffff) + ((x >> 16) & 0x0000ffff);
    return (x & 0x0000001f) + ((x >> 16) & 0x0000001f);
    */
    // since current mask of bits 0x0f0f0f0f
    // result of summing 0f + 0f = 1f
    // x * 0x01010101 will produce
    // sum of all current and lower octets in
    // each octet
    return (x * 0x01010101) >> 24;
}