如何计算1的数字将具有二进制数?

时间:2012-03-08 11:39:26

标签: java performance algorithm

  

可能重复:
  Best algorithm to count the number of set bits in a 32-bit integer?

如何计算二进制数字中1的数量?

所以假设我有一个数字45,它等于二进制101101并且其中有1个。编写算法来实现此目的的最有效方法是什么?

8 个答案:

答案 0 :(得分:63)

而不是编写算法来做到这一点,最好使用内置函数。 Integer.bitCount()

这使得特别高效的是JVM可以将其视为内在的。即,在支持它的平台上用单个机器代码指令识别和替换整个事物,例如,英特尔/ AMD


展示此优化的有效性

public static void main(String... args) {
    perfTestIntrinsic();

    perfTestACopy();
}

private static void perfTestIntrinsic() {
    long start = System.nanoTime();
    long countBits = 0;
    for (int i = 0; i < Integer.MAX_VALUE; i++)
        countBits += Integer.bitCount(i);
    long time = System.nanoTime() - start;
    System.out.printf("Intrinsic: Each bit count took %.1f ns, countBits=%d%n", (double) time / Integer.MAX_VALUE, countBits);
}

private static void perfTestACopy() {
    long start2 = System.nanoTime();
    long countBits2 = 0;
    for (int i = 0; i < Integer.MAX_VALUE; i++)
        countBits2 += myBitCount(i);
    long time2 = System.nanoTime() - start2;
    System.out.printf("Copy of same code: Each bit count took %.1f ns, countBits=%d%n", (double) time2 / Integer.MAX_VALUE, countBits2);
}

// Copied from Integer.bitCount()
public static int myBitCount(int i) {
    // HD, Figure 5-2
    i = i - ((i >>> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
    i = (i + (i >>> 4)) & 0x0f0f0f0f;
    i = i + (i >>> 8);
    i = i + (i >>> 16);
    return i & 0x3f;
}

打印

Intrinsic: Each bit count took 0.4 ns, countBits=33285996513
Copy of same code: Each bit count took 2.4 ns, countBits=33285996513

使用内在版本和循环的每个位计数平均仅需0.4纳秒。使用相同代码的副本需要6倍的时间(获得相同的结果)

答案 1 :(得分:36)

计算32位变量v中1的数量的最有效方法我知道的是:

v = v - ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // c is the result

更新:我想说明这不是我的代码,实际上它比我年长。根据{{​​3}}(Donald Knuth第四卷第11页),该代码首先出现在第一本关于编程的教科书中,The Art of Computer Programming由Wilkes,Wheeler和Gill编写(第二版,1957年,重印1984年) 。本书第2版第191-193页介绍了D B Gillies和J C P Miller的 Nifty Parallel Count

答案 2 :(得分:15)

参见Bit Twidling Hacks并研究所有'计数位集'算法。特别是,如果你期望一个小答案,Brian Kernighan的方式很简单,也很快。如果您期望均匀分布的答案,查找表可能会更好。

答案 3 :(得分:5)

这称为Hamming weight。它也称为population countpopcountsideways sum

答案 4 :(得分:2)

以下是“Bit Twiddling Hacks”页面或Knuth's books(我不记得了)。它适用于无符号64位整数,适用于C#。我不知道Java中缺少无符号值是否会产生问题。

顺便说一下,我只编写代码供参考; @Lawrey说,最好的答案是使用Integer.bitCount();因为在某些(但不是全部)CPU中有针对此操作的特定机器代码操作。

  const UInt64 m1 = 0x5555555555555555;
  const UInt64 m2 = 0x3333333333333333;
  const UInt64 m4 = 0x0f0f0f0f0f0f0f0f;
  const UInt64 h01 = 0x0101010101010101;

  public int Count(UInt64 x)
  {
      x -= (x >> 1) & m1;
      x = (x & m2) + ((x >> 2) & m2);
      x = (x + (x >> 4)) & m4;
      return (int) ((x * h01) >> 56);
  }

答案 5 :(得分:0)

public int f(int n) 
{
    int result = 0;
    for(;n > 0; n = n >> 1)
        result += ((n & 1) == 1 ? 1 : 0);

    return result;
}

答案 6 :(得分:0)

我使用过的最快且在实际实现中(在开源Sphinx Search Engine中)也看到MIT HAKMEM algorithm。它在1和0的非常大的流中运行超快。

答案 7 :(得分:0)

以下Ruby代码适用于正数。

count = 0
while num > 1
    count = (num % 2 == 1) ? count + 1 : count
    num = num >> 1
end
count += 1
return count