这实际上是log base 2,但是我无法在我所处的环境中访问此功能。手动遍历这些位来验证它们是不可接受的慢。如果只是4位,我可能会将其编入索引并在数组中浪费一些空间,但是64位则不可行。
找到哪个位的聪明的常量时间方法? (数量是64位数字。)
编辑:澄清一下,数字中设置了一个位。
答案 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);