覆盖特定范围内某些位的最佳方法
给定一串二进制位,怎么才能最好地覆盖其中的某一部分呢?
比如说,给你一串:
0100 1010
假设我想把中间的两个二进制位替换成10,结果应该是:
0101 0010
那我该怎么做呢?
一开始,我想我可以把想要覆盖的位(也就是10)移动到正确的位置(变成10000),然后用位运算中的“或”操作来合并。但我发现这样虽然能保留其他的位,但并不能指定我到底想覆盖哪些位。
我在研究Python的bitarray模块,但我想再确认一下,是否有一种非常简单的位运算可以直接帮我完成这个操作。
谢谢。
6 个回答
我看到你在“位操作”方面得到了很好的回答。不过,如果你需要频繁进行这类操作,我还是建议你看看这个讨论,这里有很多有用的信息,还有一些相关的链接。此外,正如那个维基所提到的,你可以看看一些在这里找到的包(比如BitVector、BitPacket、bitarray)——可读性很重要,经验表明,去掉那些不明显的内联代码,改用一些命名好的API,可以让你的代码更清晰。
如果你决定进行位操作,自动生成给定位索引的位掩码是非常关键的。我建议你从一个“原子位掩码”开始,这个掩码只有一个1位,通过位移来构建:
>>> bin(1 << 7)
'0b10000000'
如你所见,1 << 7
表示一个1后面跟着7个0——因此:
>>> bin((1 << 7) - 1)
'0b1111111'
(1 << 7) - 1
恰好有7个1(你需要加上括号,因为位移操作<<
的优先级低于减法操作-
),这些1是最低有效位,简称LSB(当然,左边都是0)。
有了一个简单的方法来生成“一个有N个1的位掩码”(作为最低有效位),那么设置从N到M的位为1,其它位清零就变得简单了——使用命名函数可以让代码更易读:
>>> def n_lsb(x): return (1 << x) - 1
...
>>> def n_to_m(n, m): return n_lsb(n) & ~ n_lsb(m)
...
>>> bin(n_to_m(7, 3))
'0b1111000'
所以,现在要把从N到M的位设置为某种模式,正如其他回答所示:
>>> def set_bits(i, n, m, pat): return (i & ~n_to_m(n, m))|(pat<<m)
...
>>> bin(set_bits(511, 7, 3, 0b0101))
'0b110101111'
虽然这个回答本身并没有让你能做比其他回答更多的事情,但我希望它能帮助你“学会钓鱼”(而不是仅仅给你一条鱼;-)),从而帮助你(和其他读者)在未来的学习中。
顺便说一下,我建议尽量减少位操作和算术操作的混合——在这个例子中,我唯一使用的算术操作是- 1
在n_lsb
中,这是一个比较清晰的例子;在更一般的情况下,二进制补码算术(从位操作的角度看普通整数算术)可能会比较复杂。
a = 0b01001010
a &= 0b11100111
a |= 0b00010000
这里的 and
操作是把目标位清零,也就是把它们变成0:
01001010
& 11100111
--------
01000010
然后 or
操作会把这些位填充上我们想要的数据:
01000010
| 00010000
--------
01010010
这个过程是先把你想要清除的位(也就是把它们变成零,同时保留其他位不变)进行处理,然后再进行按位或运算。
可以使用按位与运算,配合一个特定的模式(在这个例子中是 11100111
)。
如果你已经有了这个模式的“正版本”(在这里是 00011000
),这个版本更容易生成,那么你可以通过一种叫做“反码”的方法来得到“负版本” 11100111
。在Python和大多数类似C语言的编程语言中,这个反码用 ~
来表示。