Python 动态位操作示例

Python dynamic bit manipulation example

我目前正在编写一个 python 需要位操作的程序。

输入是一个整数数组序列,比如说

1, 2, 3, 4, 5, 6, 7, 8

输出是检查每个整数是否等于或大于 3。如果是,则为 1,否则为 0。输出为二进制 整数:

00111111

我该怎么做?

如果有新数字进来,比如9。我需要删除序列中的第一个数字,比如序列中的1。所以我的新序列将是:

2,3,4,5,6,7,8,9

那么结果应该是01111111。但是我想对我的旧整数 00111111 使用 左移 操作。我怎样才能做到这一点?谢谢!

您可以使用如下函数:

# Assumes msb first, lsb last, bits is iterable of integers in [0,1]
def bits2int(bits):
    v = 0
    for b in bits:
        v <<= 1
        v += b
    return v

然后,给定您的输入数组 arr,调用它:

arr = [1, 2, 3, 4, 5, 6, 7, 8]
val = bits2int([i >= 3 for i in arr])

print(val)                   # 63
print("{:08b}".format(val))  # 00111111

您的其他输入数组也是如此,您可以这样调用它:

arr = [2,3,4,5,6,7,8,9]
val = bits2int([i >= 3 for i in arr])

print(val)                  # 127
print("{:08b}".format(val)  # 01111111

编辑:从你问题的最后一部分来看,你似乎想要 "update" val 给出一些新的整数。

这可以使用上述方法完成(更新 arr,在更新的 arr 上再次调用 bits2int),或 "manually",使用:

arr = [1, 2, 3, 4, 5, 6, 7, 8]
val = bits2int([i >= 3 for i in arr])
# Now, val = 63 = 0b00111111

# Handle additional integer
new_int = 9             # Give the additional integer a name, to make it clear
val <<= 1               # Shift up all bits
val |= (new_int >= 3)   # Set lowest bit according to some logic
                        #   (here, if new_int >= 3)
print(val)
print("{:08b}".format(val))

最后,val 与上面第二次 bits2int 调用的输出相同:

127
01111111

请注意,<<= 运算符是 in-place/augmented 左移运算符,|= 运算符是 in-place/augmented 按位或运算符。


更新

根据评论中的讨论,您不仅要跟踪值,还要跟踪位数。这比上面的简单功能更复杂。这是您可以使用的基本 class:

class BitArray(object):
    @classmethod
    def from_array(cls, bits=None):
        self = cls()
        if bits is None: bits = []
        self.val = 0
        for b in bits:
            self.val <<= 1
            self.val += b
        self.nbits = len(bits)
        return self

    @classmethod
    def from_int(cls, val, nbits=None):
        self = cls()
        if nbits is None: nbits = val.bit_length()
        self.val = val
        self.nbits = nbits
        return self

    # Define equality
    def __eq__(self,  other):
        if self.val   != other.val:   return False
        if self.nbits != other.nbits: return False
        return True

    # Better representation of BitArray object
    def __repr__(self):
        return "BitArray(nbits=%d, val=%d)" % (self.nbits, self.val)

    # Output a binary string of nbits width, or width if specified (width must be >= nbits)
    def binstr(self, width=None):
        if width is None: width = self.nbits
        assert width >= self.nbits
        return "{:b}".format(self.val).zfill(width)

    # Conversion to int e.g. int(BitArrayInstance)
    def __int__(self):
        return self.val

    # "in-line" left shift
    def __ilshift__(self, n):
        self.val <<= n
        self.nbits += 1
        return self

    # "in-line" bitwise or
    def __ior__(self, v):
        self.val |= v
        return self

    # Helper function to generate a bit mask
    def _mask(self):
        return (1 << self.nbits) - 1

    # Trim n bits off the left side (truncate n most significant bits)
    def ltrim(self, n):
        self.nbits = max(0, (self.nbits - n))
        self.val &= self._mask()

    # "Pops" off msb, shifts all bits up 1, sets lsb to v
    def rotate_in(self, v):
        v = int(v)
        assert v in [0,1]
        self.ltrim(1)
        self.__ilshift__(1)
        self.__ior__(v)
        return self

现在,在执行相同的 <<=|= 操作后,您可以使用 ltrim 方法删除最左边的位/msb:

arr = [1, 2, 3, 4, 5, 6, 7, 8]
b1 = BitArray.from_array([i >= 3 for i in arr])
print(b1)
new_int = 9
b1 <<= 1
b1 |= (new_int >= 3)
b1.ltrim(1)
print(b1)

这输出:

BitArray(nbits=8, val=63)
BitArray(nbits=8, val=127)

请注意,nbits 值在两个打印函数中是相同的。 <<= 1nbits 增加 1,然后 ltrim(1) 将其减少 1,回到 8。它按 "throwing out" 最左边的位执行,如您所问。

我还为这 3 个操作添加了一个方便的函数,rotate_in(bit_val)。您可以通过以下方式使用它:

arr = [1, 2, 3, 4, 5, 6, 7, 8]
b2 = BitArray.from_array([i >= 3 for i in arr])
print(b2)
new_int = 9
b2.rotate_in(new_int >= 3)
print(b2)

这个输出(同上):

BitArray(nbits=8, val=63)
BitArray(nbits=8, val=127)

我们可以验证这两个 BitArray 等价于

# Ensure b1 equals b2
print(b1 == b2)  # True

最后,为了验证这对于长度为 600 的输入数组同样有效,我们创建了一个包含 600 个元素的 1 列表,将其转换为 BitArray,然后比较结果值(与int(BitArrayInstance)) 我们期望的是:

# Test 600 element array :)
arr = [1] * 600
b3 = BitArray.from_array(arr)
print(int(b3) == ((2**600) - 1))  # True