在python中计算快速日志库2天花板

时间:2012-10-28 02:24:08

标签: python math logging binary

对于给定的x < 10^15,快速准确地确定最大整数p,以便2^p <= x

以下是我尝试过的一些事情:

首先我试过这个,但对大数字来说不准确:

>>> from math import log
>>> x = 2**3
>>> x
8
>>> p = int(log(x, 2))
>>> 2**p == x
True
>>> x = 2**50
>>> p = int(log(x, 2))
>>> 2**p == x #not accurate for large numbers?
False

我可以试试像:

p = 1
i = 1
while True:
    if i * 2 > n:
        break
    i *= 2
    p += 1
    not_p = n - p

如果p为50

,则最多需要50次操作

我可以预先计算2的所有幂,直到2 ^ 50,并使用二进制搜索来找到p。这将采取log(50)操作,但似乎有点过分和丑陋?

我找到了基于C的解决方案的这个帖子:Compute fast log base 2 ceiling

然而,它似乎有点难看,我不确定如何将其转换为python。

6 个答案:

答案 0 :(得分:26)

在Python&gt; = 2.7中,您可以使用.bit_length()整数方法:

def brute(x):
    # determine max p such that 2^p <= x
    p = 0
    while 2**p <= x:
        p += 1
    return p-1

def easy(x):
    return x.bit_length() - 1

给出了

>>> brute(0), brute(2**3-1), brute(2**3)
(-1, 2, 3)
>>> easy(0), easy(2**3-1), easy(2**3)
(-1, 2, 3)
>>> brute(2**50-1), brute(2**50), brute(2**50+1)
(49, 50, 50)
>>> easy(2**50-1), easy(2**50), easy(2**50+1)
(49, 50, 50)
>>> 
>>> all(brute(n) == easy(n) for n in range(10**6))
True
>>> nums = (max(2**x+d, 0) for x in range(200) for d in range(-50, 50))
>>> all(brute(n) == easy(n) for n in nums)
True

答案 1 :(得分:3)

您可以尝试numpy中的log2函数,该函数似乎适用于最大为2 ^ 62的幂:

>>> 2**np.log2(2**50) == 2**50
True
>>> 2**np.log2(2**62) == 2**62
True

在上面(至少对我而言)由于numpy的内部数字类型的限制而失败,但是它会处理你说你正在处理的范围内的数据。

答案 2 :(得分:3)

您在评论中指定x是一个整数,但是对于来到这里的人来说,x已经是 float ,那么 math.frexp() 会非常快在提取日志库2:

log2_slow = int(floor(log(x, 2)))
log2_fast = frexp(x)[1]-1

C function that frexp() calls只是抓住并调整指数。还有更多的splainin:

  • 下标[1]是因为frexp()返回一个元组(有效数,指数)。
  • 减法-1说明有效数在[0.5,1.0]范围内。例如,2 50 存储为0.5×2 51
  • floor()是因为您指定了2^p <= x,所以p == floor(log(x,2))

(源自another answer。)

答案 3 :(得分:2)

适用于我,OSX 10.7上的Python 2.6.5(CPython):

>>> x = 2**50
>>> x
1125899906842624L
>>> p = int(log(x,2))
>>> p
50
>>> 2**p == x
True

它至少对于高达1e9的指数继续工作,到那时它开始花费很长时间来进行数学计算。你在考试中xp实际获得了什么?什么版本的Python,在什么操作系统上运行?

答案 4 :(得分:2)

关于“对大数字不准确”,你的挑战是浮点表示确实不如你所需要的那样精确(49.999999999993 != 50.0)。一个很好的参考是“What Every Computer Scientist Should Know About Floating-Point Arithmetic。”

好消息是C程序的转换非常简单:

def getpos(value):
    if (value == 0):
        return -1
    pos = 0
    if (value & (value - 1)):
        pos = 1
    if (value & 0xFFFFFFFF00000000):
        pos += 32
        value = value >> 32
    if (value & 0x00000000FFFF0000):
        pos += 16
        value = value >> 16
    if (value & 0x000000000000FF00):
        pos += 8
        value = value >> 8
    if (value & 0x00000000000000F0):
        pos += 4
        value = value >> 4
    if (value & 0x000000000000000C):
        pos += 2
        value = value >> 2
    if (value & 0x0000000000000002):
        pos += 1
        value = value >> 1
    return pos

另一种选择是你可以舍入到最接近的整数,而不是截断:

   log(x,2)
=> 49.999999999999993
   round(log(x,2),1)
=> 50.0

答案 5 :(得分:0)

我需要计算2的上限功率(计算出使用模数运算符在给定范围内生成随机数需要多少字节的熵)。

从粗略的实验中我认为下面的计算给出了最小整数p,使得val < 2 ^ p

它可能和你一样快,并且只使用逐位整数运算。

def log2_approx(val):
    from math import floor
    val = floor(val)
    approx = 0
    while val != 0:
        val &= ~ (1<<approx)
        approx += 1
    return approx

您将通过

计算给定n的略微不同的值
log2_approx(n) - 1

...也许。但无论如何,按位算术可以为您提供如何快速完成此任务的线索。