如何反转数字的备用位

时间:2012-10-27 07:10:25

标签: python bit-manipulation

问题是如何从LSB开始反转数字的备用位。目前我正在做的是先做一个

count = -1
while n:
   n >>= 1
   count += 1

首先找到最左边设置位的位置,然后运行一个循环来反转每个交替位:

i = 0
while i <= count:
 num ^= 1<<i
 i += 2

是否有快速入侵解决方案而不是这个相当无聊的循环解决方案?当然,解决方案无法对整数的大小做出任何假设。

4 个答案:

答案 0 :(得分:5)

我认为这可行:

带有交替掩码10101010...10的按位异或应该每隔一位反转一次。

如果要在“0011”中反转交替位,下表显示结果:

i | m | i XOR m
0 | 0 | 0
0 | 1 | 1
1 | 0 | 1
1 | 1 | 0

在python中,使用i ^ m实现XOR。当然,您还必须确定面具的字面值,这取决于i数字的大小。

答案 1 :(得分:2)

之前的单线解决方案,

n ^ sum(2**i for i in range(0, len(bin(n))-2, 2))

是O(lg n)解,其中n是输入数。下面所示的渐近快得多的解决方案在时间O(lg(lg n))中运行,即,与输入数中的位数的对数成比例的时间。注意,如图所示的二进制搜索在测试中工作正常,但也许可以改进。

编辑:表达式-1<<L是一个掩码,其高位设置且L低位清零。例如,python将255显示为(-1<<8)&255的值,将256显示为(-1<<8)&256的值。程序首先加倍L(使越来越多的低位清零),直到L超过数字v中的位数;也就是说,直到(-1<<L)&v为零。在L的每次加倍时,它可以向上移动R.然后程序使用二进制搜索,重复地将L-R差减半,以找到L = R + 1,使得v&(-1<<L) == 0v&(-1<<R) > 0,以确定v是L位长。 之后,程序将交替位掩码k的长度加倍,直到它至少为L位长。如果L为奇数,则将掩码移位一位。 (而不是if L & 1: k = k<<1它可以说k <<= L&1。注意,我将“备用位”解释为从MSB下方的位开始。而是始终切换位0,2,4 ...,删除if L & 1: k = k<<1行。)然后它通过(1<<L)-1和(2 ** L)-1来选择k的低L位。注意,程序的O(lg(lg n))时间限制取决于O(1)逻辑运算;但随着L变大(超过几百位),1<<L等变为O(lg n)运算。

def clearaltbits(v):
    if not v:
        return 0
    L, R = 16, 0
    # Find an upper bound on # bits
    while (-1<<L) & v:
        R, L = L, 2*L
    # Binary search for top bit #
    while not (-1<<L) & v:
        m = (L+R)/2
        if (-1<<m) & v:
            R = m
        else:
            L = m
        if L==R+1: break
    print bin(v),'has',len(bin(v))-2,'bits.'
    # Make big-enough alternate-bits mask
    k, b = 0b0101010101010101, 16
    while not (-1<<L) & k:
        k = (k<<b)|k
        b += b
    if L & 1:
        k = k<<1
    k = k & ((1<<L)-1)
    print bin(k^v),'fin'


clearaltbits(3**3)
clearaltbits(5**6)
clearaltbits(7**17)
clearaltbits(13**19)

四个函数调用的输出如下所示。

0b11011 has 5 bits.
0b10001 fin

0b11110100001001 has 14 bits.
0b10100001011100 fin

0b110100111001001110000011001001100110111010000111 has 48 bits.
0b100001101100011011010110011100110011101111010010 fin

0b10011110100000000111000001101101000001011000100011000010010000111010101 has 71 bits.
0b11001011110101010010010100111000010100001101110110010111000101101111111 fin

答案 2 :(得分:1)

扩展答案trideceth12给出 - 这是一个计算并应用掩码的单行:

n ^ sum(2**i for i in range(0, len(bin(n))-2, 2))

...假设你确实想要从LSB开始,那就是。

或者,如果您从最重要的设置位开始:

n ^ sum(2**i for i in range(len(bin(n))-3, -1, -2))

答案 3 :(得分:0)

这适用于任何长度的正整数:

def invert_alt_bits(n):
    m = 1
    while True:
        n ^= m
        m <<= 2
        if m > n: break
    return n