有效的方法来确定以64位数量设置哪个位

时间:2013-08-21 18:03:28

标签: algorithm bit-manipulation

这实际上是log base 2,但是我无法在我所处的环境中访问此功能。手动遍历这些位来验证它们是不可接受的慢。如果只是4位,我可能会将其编入索引并在数组中浪费一些空间,但是64位则不可行。

找到哪个位的聪明的常量时间方法? (数量是64位数字。)

编辑:澄清一下,数字中设置了一个位。

4 个答案:

答案 0 :(得分:4)

我假设你想要设置的最重要位的位置。做二分搜索。如果整个值为0,则不设置任何位。如果前32位为0,则该位在底部32位;否则它处于高位。然后递归到适当的32位的两个16位半部分。递归直到达到4位值并使用查找表。 (或者递归到1位值。)您只需要跟踪每个递归级别使用的一半。

答案 1 :(得分:3)

我所知道的最快的方法是使用DeBruijn Sequence

Find the log base 2 of an N-bit integer in O(lg(N)) operations with multiply and lookup

注意,在lg(N)中,N是位数,而不是最高设置位的数量。所以它是任何N位数的恒定时间。

如果你知道这个数字是2的精确幂(即只有1位设置),那么就有更快的方法。

该hack是32位。我似乎记得在某个地方看到64位示例,但目前无法跟踪它。最坏的情况是,运行两次:一次是高32位,一次是低32位。

答案 2 :(得分:0)

如果您的数字是2的幂,并且您有位计数指令,则可以执行以下操作:

bitcount(x-1)

e.g。

  x      x-1     bitcount(x-1)  

  b100   b011    2  
  b001   b000    0  

注意如果数字不是2的幂,这将不起作用。

修改

这是De Brujin方法的64位版本:

static const int log2_table[64] = {0, 1, 2, 7, 3, 13, 8, 19, 4, 25, 14, 28, 9, 34, 
                                   20, 40, 5, 17, 26, 38, 15, 46, 29, 48, 10, 31,
                                   35, 54, 21, 50, 41, 57, 63, 6, 12, 18, 24, 27, 
                                   33, 39, 16, 37, 45, 47, 30, 53, 49, 56, 62, 11, 
                                   23, 32, 36, 44, 52, 55, 61, 22, 43, 51, 60, 42, 59, 58};

int fastlog2(unsigned long long x) {
    return log2_table[ ( x * 0x218a392cd3d5dbfULL ) >> 58 ];
}

测试代码:

int main(int argc,char *argv[])
{
    int i;
    for(i=0;i<64;i++) {
        unsigned long long x=1ULL<<i;
        printf("0x%llu -> %d\n",x,fastlog2(x));
    }
    return 0;
}

神奇的64位数字是6阶二进制De Brujin序列。

乘以2的幂相当于将此数字向上移动一定数量的位置。

这意味着乘法结果的前6位对应于每个输入数字的6位数的不同子序列。 De Brujin序列具有每个子序列都是唯一的属性,因此我们可以构造一个适当的查找表,以便从子序列返回到设置位的位置。

答案 3 :(得分:0)

如果使用某些现代Intel CPU,则可以使用硬件 支持“POPulation CouNT”汇编指令:

http://en.wikipedia.org/wiki/SSE4#POPCNT_and_LZCNT

对于Unix / gcc,你可以使用宏:

#include <smmintrin.h>
uint64_t x;
int c = _mm_popcnt_u64(x);