向后循环通过数字中的最高位

时间:2014-12-03 13:40:05

标签: python optimization set bit-manipulation

我有一个函数接收一个整数并返回一个由2的幂组成的集合,其总和等于输入值:

def bin_set(n):
    b = set()
    while n:
        hbit = 1 << n.bit_length()-1
        b.add(hbit)
        n -= hbit
    return b

所以我计算数字的最高位并将其添加到集合中,但是我应该将n的值发送到循环的下一次迭代?我使用了n = n-hbit因为while的条件而且它在某种程度上有效,但我很确定这是一种错误的做法。

是否有不同的方法可以做到这一点,可能是使用不同的循环而没有对数/ bit twiddling / bit_length()或者这是唯一的方法吗?

2 个答案:

答案 0 :(得分:1)

这种方法很好用;您是通过减法从n中删除检测到的位,直到删除所有位。结果,n.bit_length()结果也会减少。

您可以使用XOR(^运算符)来清除高位,而不是减法:

def bin_set(n):
    b = set()
    while n:
        hbit = 1 << n.bit_length() - 1
        b.add(hbit)
        n ^= hbit
    return b

另一种方法是右移高位而不是改变n

def bin_set(n):
    b = set()
    hbit = 1 << n.bit_length() - 1
    while hbit:
        if n & hbit:  # the high bit is set
            b.add(hbit)
        hbit >>= 1
    return b

但这实际上做了更多测试!

您的版本只循环次数与n中设置的位数相同,但每次迭代计算位数,而上述版本循环n.bit_length()次。 int.bit_length()需要O(N)平均时间来计算位长度,因此你需要O(KN)时间来从N位产生K值集合,将高位移位需要O(N)时间。

这使得我的版本渐近变得更好,但是因为int.bit_length()方法在C中工作并且将循环减少到每4位只有一次循环迭代,所以它并没有听起来那么糟糕。在你开始看到我的胜利之前,你需要 humongous 号码。

答案 1 :(得分:0)

你可以使用一种名为set comprehension的东西,这是一种非常简洁的方法来创建它们。

def bin_set(n):
    return {1 << p for p in xrange(n.bit_length()-1, -1, -1) if n & 1 << p}

print bin_set(0)   # --> set([])          
print bin_set(10)  # --> set([8, 2])      
print bin_set(12)  # --> set([8, 4])      
print bin_set(15)  # --> set([8, 1, 2, 4])