对于给定的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。
答案 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 。 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的指数继续工作,到那时它开始花费很长时间来进行数学计算。你在考试中x
和p
实际获得了什么?什么版本的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
...也许。但无论如何,按位算术可以为您提供如何快速完成此任务的线索。