Pythonic way to iterate over bits of integer

前端 未结 7 2065
面向向阳花
面向向阳花 2021-02-02 08:08

Let\'s a=109 or 1101101 in binary. How do I iterate over bits of this number, eg: [64, 32, 8, 4, 1]

相关标签:
7条回答
  • 2021-02-02 08:43
    >>> [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]
    
    0 讨论(0)
  • 2021-02-02 08:44

    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
    • think about checking bitfield is >= 0
    0 讨论(0)
  • 2021-02-02 08:45

    S.O. won't let me put this as a comment, but here's a line-by-line example of how Duncan's solution works. Hopefully this clarifies what's happening.

    Let's use the decimal number 109 as an example:

    # 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.
    
    0 讨论(0)
  • 2021-02-02 08:49

    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:

    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
    
    0 讨论(0)
  • 2021-02-02 08:49

    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]
    
    0 讨论(0)
  • 2021-02-02 08:50

    The efficiency of F.J.'s answer can be dramatically improved.

    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. Duncan still wins but not the one liner is at least in the same class.

    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
    
    0 讨论(0)
提交回复
热议问题