【问题标题】:How to efficiently compute floor log base 2^(1/4)如何有效地计算底数基数 2^(1/4)
【发布时间】:2014-08-28 20:57:55
【问题描述】:

计算floor(log_2(x)) 可以通过计算存在many 快速算法的零的数量来完成。

x 是一个覆盖所有可能值的 64 位无符号整数时,是否有任何类似的技巧来计算 floor(log_{2^(1/4)}(x))

由于log_{2^(1/4)}(x)=4*log_2(x)=log_2(x^4),这相当于为floor(4*log_2(x))floor(log(x^4))找到一个有效的算法。

【问题讨论】:

  • log_{2^(1/4)}(x) = log_2(x)/log_2(2^(1/4)) = log_2(x)/(1/4) = 4 log_2(x)
  • 哦,哎呀,我忘记了分母中的 2^。对不起!

标签: algorithm bit-manipulation numerical-methods


【解决方案1】:

计算floor(log_2(x))后,我们可以划分z = x / 2^floor(log_2(x)),考虑计算floor(log_{2^(1/4)}(z))的问题,其中z属于[1, 2)范围,因为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

就足够了。为了完全避免浮点运算,“除法”可以实现为左移,使(2^63) z 与类似表示的常量进行比较。

现在使用 C 代码:

#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));
  }
}

计算常数的 Mathematica/Wolfram Alpha 查询是

BaseForm[Table[Ceiling[2^(63+i/4)],{i,1,3}],16] .

【讨论】:

  • 这太神奇了,谢谢!作为额外的奖励,由于大 x 的舍入误差,这实际上比简单的 floor(4*log_2(x)) 方法准确。性能方面,直接翻译成 Go 并不会产生我预期的那么大的性能差异。对于随机、均匀分布的 uint64 值,我将 54.7ns/op 用于天真的方法,19.8ns/op 用于上述方法。 log_2 计算部分单独的基准测试为 7.9ns/op
猜你喜欢
  • 2018-05-28
  • 2012-04-27
  • 2017-08-14
  • 2015-04-15
  • 1970-01-01
  • 2016-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多