计算floor(log_2(x))
可以通过计算有many个快速算法的零的数量来完成。
当floor(log_{2^(1/4)}(x))
是一个64位无符号int而不是所有可能的值时,是否有类似的计算x
的技巧?
从log_{2^(1/4)}(x)=4*log_2(x)=log_2(x^4)
开始,这相当于为floor(4*log_2(x))
或floor(log(x^4))
找到有效的算法。
答案 0 :(得分:0)
在我们计算floor(log_2(x))
之后,我们可以划分z = x / 2^floor(log_2(x))
并考虑计算floor(log_{2^(1/4)}(z))
的问题z
属于范围[1, 2)
,因为{{ 1}}。 floor(log_{2^(1/4)}(x)) = 4 floor(log_2(x)) + floor(log_{2^(1/4)}(z))
只有四种可能性,因此floor(log_{2^(1/4)}(z))
与常量进行两次比较(三次无分支)
z
就足够了。为了完全避免浮点运算,“除法”可以实现为左移,使(2^(1/4))^1, (2^(1/4))^2, (2^(1/4))^3
与类似表示的常数进行比较。
现在使用C代码:
(2^63) z
用于计算常量的Mathematica / Wolfram Alpha查询是
#include <assert.h>
#include <stdio.h>
static int mylog(unsigned long long x) {
assert(x > 0ULL);
/* compute n = floor(log2(x)) */
unsigned long long y = x | (x >> 1);
y |= y >> 2;
y |= y >> 4;
y |= y >> 8;
y |= y >> 16;
y |= y >> 32;
y -= (y >> 1) & 0x5555555555555555ULL;
y = (y & 0x3333333333333333ULL) + ((y >> 2) & 0x3333333333333333ULL);
y = (y + (y >> 4)) & 0x0f0f0f0f0f0f0f0fULL;
y = (y + (y >> 8)) & 0x00ff00ff00ff00ffULL;
y = (y + (y >> 16)) & 0x0000ffff0000ffffULL;
y = (y + (y >> 32)) & 0x00000000ffffffffULL;
int n = (int)y - 1;
/* normalize x and use binary search to find the last two bits of the log */
x <<= 63 - n;
n <<= 2;
if (x < 0xb504f333f9de6485ULL) {
return x < 0x9837f0518db8a970ULL ? n : n + 1;
} else {
return x < 0xd744fccad69d6af5ULL ? n + 2 : n + 3;
}
}
int main(void) {
unsigned long long x;
while (scanf("%llu", &x) == 1) {
printf("%d\n", mylog(x));
}
}