代表数字7的8位像这样:

00000111

设置了三个比特。

确定32位整数中设置位数的算法是什么?


当前回答

我使用下面更直观的代码。

int countSetBits(int n) {
    return !n ? 0 : 1 + countSetBits(n & (n-1));
}

逻辑:n & (n-1)重置n的最后一个集合位。

附注:我知道这不是O(1)解,尽管这是一个有趣的解。

其他回答

"最佳算法"是什么意思?短码还是长码?您的代码看起来非常优雅,并且具有恒定的执行时间。代码也很短。

但如果速度是主要因素,而不是代码大小,那么我认为以下方法可以更快:

       static final int[] BIT_COUNT = { 0, 1, 1, ... 256 values with a bitsize of a byte ... };
        static int bitCountOfByte( int value ){
            return BIT_COUNT[ value & 0xFF ];
        }

        static int bitCountOfInt( int value ){
            return bitCountOfByte( value ) 
                 + bitCountOfByte( value >> 8 ) 
                 + bitCountOfByte( value >> 16 ) 
                 + bitCountOfByte( value >> 24 );
        }

我认为这不会更快的64位值,但32位值可以更快。

对于JavaScript,你可以使用一个查找表来计算一个32位值的设置位的数量(这段代码可以很容易地翻译成C语言)。此外,添加了8位和16位版本,以供通过网络搜索查找的人使用。

const COUNT_BITS_TABLE = makeLookupTable() function makeLookupTable() { const table = new Uint8Array(256) for (let i = 0; i < 256; i++) { table[i] = (i & 1) + table[(i / 2) | 0]; } return table } function countOneBits32(n) { return COUNT_BITS_TABLE[n & 0xff] + COUNT_BITS_TABLE[(n >> 8) & 0xff] + COUNT_BITS_TABLE[(n >> 16) & 0xff] + COUNT_BITS_TABLE[(n >> 24) & 0xff]; } function countOneBits16(n) { return COUNT_BITS_TABLE[n & 0xff] + COUNT_BITS_TABLE[(n >> 8) & 0xff] } function countOneBits8(n) { return COUNT_BITS_TABLE[n & 0xff] } console.log('countOneBits32', countOneBits32(0b10101010000000001010101000000000)) console.log('countOneBits32', countOneBits32(0b10101011110000001010101000000000)) console.log('countOneBits16', countOneBits16(0b1010101000000000)) console.log('countOneBits8', countOneBits8(0b10000010))

在Java 8或9中只调用Integer。bitCount。

你可以使用内置函数__builtin_popcount()。c++中没有__builtin_popcount,但它是GCC编译器的内置函数。这个函数返回一个整数中的设置位数。

int __builtin_popcount (unsigned int x);

参考:Bit Twiddling Hacks

这里有一个到目前为止还没有提到的解决方案,使用位字段。下面的程序使用4种不同的方法对100000000个16位整数数组中的设置位进行计数。计时结果在括号中给出(在MacOSX上,使用gcc -O3):

#include <stdio.h>
#include <stdlib.h>

#define LENGTH 100000000

typedef struct {
    unsigned char bit0 : 1;
    unsigned char bit1 : 1;
    unsigned char bit2 : 1;
    unsigned char bit3 : 1;
    unsigned char bit4 : 1;
    unsigned char bit5 : 1;
    unsigned char bit6 : 1;
    unsigned char bit7 : 1;
} bits;

unsigned char sum_bits(const unsigned char x) {
    const bits *b = (const bits*) &x;
    return b->bit0 + b->bit1 + b->bit2 + b->bit3 \
         + b->bit4 + b->bit5 + b->bit6 + b->bit7;
}

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

#define out(s) \
    printf("bits set: %lu\nbits counted: %lu\n", 8*LENGTH*sizeof(short)*3/4, s);

int main(int argc, char **argv) {
    unsigned long i, s;
    unsigned short *x = malloc(LENGTH*sizeof(short));
    unsigned char lut[65536], *p;
    unsigned short *ps;
    int *pi;

    /* set 3/4 of the bits */
    for (i=0; i<LENGTH; ++i)
        x[i] = 0xFFF0;

    /* sum_bits (1.772s) */
    for (i=LENGTH*sizeof(short), p=(unsigned char*) x, s=0; i--; s+=sum_bits(*p++));
    out(s);

    /* NumberOfSetBits (0.404s) */
    for (i=LENGTH*sizeof(short)/sizeof(int), pi=(int*)x, s=0; i--; s+=NumberOfSetBits(*pi++));
    out(s);

    /* populate lookup table */
    for (i=0, p=(unsigned char*) &i; i<sizeof(lut); ++i)
        lut[i] = sum_bits(p[0]) + sum_bits(p[1]);

    /* 256-bytes lookup table (0.317s) */
    for (i=LENGTH*sizeof(short), p=(unsigned char*) x, s=0; i--; s+=lut[*p++]);
    out(s);

    /* 65536-bytes lookup table (0.250s) */
    for (i=LENGTH, ps=x, s=0; i--; s+=lut[*ps++]);
    out(s);

    free(x);
    return 0;
}

虽然位域版本非常可读,但计时结果显示它比NumberOfSetBits()慢了4倍以上。基于查找表的实现仍然要快得多,特别是对于一个65 kB的表。