在Python中找到大于n的2的最小幂

时间:2013-01-10 21:26:43

标签: python

在Python中返回大于或等于给定非负整数的2的最小幂的最简单函数是什么?

例如,大于或等于6的2的最小幂是8。

6 个答案:

答案 0 :(得分:32)

让我们测试一下:

import collections
import math
import timeit

def power_bit_length(x):
    return 2**(x-1).bit_length()

def shift_bit_length(x):
    return 1<<(x-1).bit_length()

def power_log(x):
    return 2**(math.ceil(math.log(x, 2)))

def test(f):
    collections.deque((f(i) for i in range(1, 1000001)), maxlen=0)

def timetest(f):
    print('{}: {}'.format(timeit.timeit(lambda: test(f), number=10),
                          f.__name__))

timetest(power_bit_length)
timetest(shift_bit_length)
timetest(power_log)

我使用range(1, 1000001)而非range(1000000)的原因是power_log版本将在0上失败。我在较大范围内使用少量代表而不是在小范围内使用大量代表的原因是因为我希望不同版本在不同域上具有不同的性能。 (如果你希望用巨大的千位数来调用它,当然,你需要一个使用它的测试。)

使用Apple Python 2.7.2:

4.38817000389: power_bit_length
3.69475698471: shift_bit_length
7.91623902321: power_log

使用Python.org Python 3.3.0:

6.566169916652143: power_bit_length
3.098236607853323: shift_bit_length
9.982460380066186: power_log

使用pypy 1.9.0 / 2.7.2:

2.8580930233: power_bit_length
2.49524712563: shift_bit_length
3.4371240139: power_log

我相信这表明2**在这里是缓慢的部分;使用bit_length代替log可以加快速度,但使用1<<代替2**更为重要。

另外,我认为它更清楚。 OP的版本要求你进行心理上下文 - 从对数切换到位,然后再转换为指数。要么始终保持位(shift_bit_length),要么留在日志和指数(power_log)。

答案 1 :(得分:15)

始终返回2**(x - 1).bit_length()是不正确的,因为虽然它为x = 1返回1,但它为x = 0返回非单调2。对于x = 0,单调安全的简单修复是:

def next_power_of_2(x):  
    return 1 if x == 0 else 2**(x - 1).bit_length()

示例输出:

>>> print(', '.join(f'{x}:{next_power_of_2(x)}' for x in range(10)))
0:1, 1:1, 2:2, 3:4, 4:4, 5:8, 6:8, 7:8, 8:8, 9:16

2**float('-inf') == 0开始,可以谦虚地认为x = 0应该返回0(而不是1)。

答案 2 :(得分:9)

这对你有用吗?

import math

def next_power_of_2(x):
    return 1 if x == 0 else 2**math.ceil(math.log2(x))

请注意,{3}在Python 3中可用,但在Python 2中不可用。使用它而不是math.log2可以避免后者在2 ** 29及更高版本的数值问题。

示例输出:

math.log

>>> print(', '.join(f'{x}:{next_power_of_2(x)}' for x in range(10))) 0:1, 1:1, 2:2, 3:4, 4:4, 5:8, 6:8, 7:8, 8:8, 9:16 开始,可以谦虚地认为x = 0应该返回0(而不是1)。

答案 3 :(得分:2)

我们可以使用位操作来执行以下操作:

def next_power_of_2(n):
    if n == 0:
        return 1
    if n & (n - 1) == 0:
        return n
    while n & (n - 1) > 0:
        n &= (n - 1)
    return n << 1

示例输出:

>>> print(', '.join(f'{x}:{next_power_of_2(x)}' for x in range(10)))
0:1, 1:1, 2:2, 3:4, 4:4, 5:8, 6:8, 7:8, 8:8, 9:16

如需进一步阅读,请参阅this resource

答案 4 :(得分:1)

v+=(v==0);
v--;
v|=v>>1;
v|=v>>2;
v|=v>>4;
v|=v>>8;
v|=v>>16;
v++;

对于16位整数。

答案 5 :(得分:0)

嗯,我知道这个问题很老了,我的回答也很简单,但我真的很惊讶这么长时间没有人在这里发布它,所以我将它作为答案发布。

最简单的解决方案真的很简单,不需要导入任何库,如果使用while 语句,您可以在一个循环中完成! >

所以逻辑很简单,创建一个值为1的变量,当变量的值小于数字时,将变量乘以2!

代码:

i = 1
while i < n: i *=2

n = 1, 63, 64, 255, 256, 1000, 4095 的返回值:

2, 64, 64, 256, 256, 1024, 4096

这可以很容易地修改以计算下一个与许多其他基地的电源柜:

def nextpower(num, base):
  i = 1
  while i < num: i *= base
  return i