简体   繁体   English

Pythonic迭代整数位的方法

[英]Pythonic way to iterate over bits of integer

Let's a=109 or 1101101 in binary. 设二进制的a=1091101101 How do I iterate over bits of this number, eg: [64, 32, 8, 4, 1] 如何迭代此数字的位,例如: [64, 32, 8, 4, 1]

There's a trick for just getting the 1's out of the binary representation without having to iterate over all the intervening 0's: 只需要从二进制表示中获取1,而不必迭代所有介入的0,这是一个技巧:

def bits(n):
    while n:
        b = n & (~n+1)
        yield b
        n ^= b


>>> for b in bits(109):
    print(b)


1
4
8
32
64

My approach: 我的方法:

def bits(number):
    bit = 1
    while number >= bit:
       if number & bit:
           yield bit
       bit <<= 1

I don't think there is a builtin function for it. 我不认为它有内置函数。

I also wonder if there isn't a better approach to whatever you are doing. 我也想知道对你所做的事情是否有更好的方法。 There's a good chance you don't really want to iterate over the bits like this. 很有可能你真的不想迭代这样的位。 They may be a much better way. 他们可能是一个更好的方式。

Out of curiosity I ran some timing on the methods posted here, my results: 出于好奇,我在这里发布的方法上运行了一些时间,我的结果:

Winston 2.35238099098
F.J. 6.21106815338
F.J. (2) 5.21456193924
Sven 2.90593099594
Duncan 2.33568000793
freegnu 4.67035484314

FJ converts to a string, I'm guessing that hurts his performance. FJ转换成一个字符串,我猜这会伤害他的表现。 The various optimisation attempts help, but not enough Sven produces the reverse of everybody else, which might be an advantage if you really needed that. 各种优化尝试都有所帮助,但不足以让Sven产生与其他人相反的情况,如果您确实需要它,这可能是一个优势。 Duncan's approach wins speedwise (just barely) 邓肯的进攻获胜速度(几乎没有)

Again with 340282366920938463463374607431768211457 instead of 109: 再次使用340282366920938463463374607431768211457而不是109:

Winston 44.5073108673
F.J. 74.7332041264
Sven 47.6416211128
Duncan 2.58612513542

Nice, Duncan! 很好,邓肯! It should be noted that this is pretty much the best case for Duncan's method, so it won't always have this dramatic an advantage. 应该注意的是,对于Duncan的方法来说,这几乎是最好的情况,所以它不会总是具有这种戏剧性的优势。

>>> [2**i for i, v in enumerate(bin(109)[:1:-1]) if int(v)]
[1, 4, 8, 32, 64]

Obviously the order is reversed here, you could either just use this or reverse the result: 显然这里的顺序是相反的,您可以使用它或反转结果:

>>> [2**i for i, v in enumerate(bin(109)[:1:-1]) if int(v)][::-1]
[64, 32, 8, 4, 1]

edit: Here is a slightly longer version that should be more efficient: 编辑:这是一个稍长的版本,应该更有效:

from itertools import takewhile, count
[p for p in takewhile(lambda x: x <= 109, (2**i for i in count())) if p & 109]

Python 2.7: Python 2.7:

def binary_decomposition(x):
    p = 2 ** (int(x).bit_length() - 1)
    while p:
        if p & x:
            yield p
        p //= 2

Example: 例:

>>> list(binary_decomposition(109))
[64, 32, 8, 4, 1]

SO won't let me put this as a comment, but here's a line-by-line example of how Duncan's solution works. 因此,我不会将此作为评论,但这里是Duncan解决方案如何运作的逐行示例。 Hopefully this clarifies what's happening. 希望这可以澄清正在发生的事情。

Let's use the decimal number 109 as an example : 我们以十进制数109为例

# 109 is .............. 0110 1101
# ~109 is -110 which is 1001 0010   NOTE: It's -110 instead of -109 because of 1's compliment
# ~109+1 is -109....... 1001 0011
# 109 AND ~109 is...... 0000 0001 = 1  <---- 1st value yielded by the generator
# 109 XOR 1 is......... 0110 1100 = n = 108

# 108.................. 0110 1100
# ~108+1= -108......... 1001 0100
# 108 AND -108......... 0000 0100 = 4  <---- 2nd value yielded by the generator
# 108 XOR 4............ 0110 1000 = n = 104

# 104.................. 0110 1000
# ~104+1............... 1001 1000
# 104 AND -104......... 0000 1000 = 8  <---- 3rd value yielded by the generator
# 104 XOR 8............ 0110 0000 = n = 96

# 96................... 0110 0000
# ~96+1................ 1010 0000
# 96 AND -96........... 0010 0000 = 32 <---- 4th value yielded by the generator
# 96 XOR 32............ 0100 0000 = n = 64

# 64................... 0100 0000
# ~64+1................ 1100 0000
# 64 AND -64........... 0100 0000 = 64 <---- 5th value yielded by the generator
# 64 XOR 64............ 0000 0000 = n = 0; thus, the while loop terminates.

Example one-line solution: 示例单行解决方案:

[1 << bit for bit in xrange(bitfield.bit_length()) if bitfield & (1 << bit)]

Or: 要么:

[bit for bit in (1 << n for n in xrange(bitfield.bit_length())) if bitfield & bit]

Notes: 笔记:

  • use range in Python 3 在Python 3中使用范围
  • think about checking bitfield is >= 0 考虑检查位域> = 0

The efficiency of FJ's answer can be dramatically improved. FJ答案的效率可以大大提高。

from itertools import count,takewhile
[2**i for i in takewhile(lambda x:109>2**x,count()) if 109&2**i][::-1]

I like one liners :) 我喜欢一个衬垫:)

I did a quick timeit.Timer.timeit() against this and @Duncan. 我做了一个快速timeit.Timer.timeit()对此和@Duncan。 Duncan still wins but not the one liner is at least in the same class. Duncan仍然获胜,但不是一个班轮至少在同一个班级。

from timeit import Timer
duncan="""\
def bits(n):
 while n:
  b=n&(~n+1)
  yield b
  n^=b
"""
Duncan=Timer('list(bits(109))[::-1]',duncan)
Duncan.timeit()
4.3226630687713623
freegnu=Timer('[2**i for i in takewhile(lambda x:109>2**x,count()) if 109&2**i][::-1]','from itertools import count,takewhile')
freegnu.timeit()
5.2898638248443604

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM