【问题标题】:How to invert alternate bits of a number如何反转数字的交替位
【发布时间】:2012-10-27 07:10:25
【问题描述】:

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

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

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

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

有没有一种快速破解解决方案来代替这种相当无聊的循环解决方案?当然,解决方案不能对整数的大小做任何假设。

【问题讨论】:

  • 我假设你有兴趣先找到 msb 而不是 lsb,在大端架构中(我假设你有)你熟悉最左边的 lsb 位
  • 出于兴趣,您对这个算法有什么想法?
  • 没什么。这是一个面试问题..:P
  • 你的回答会是“我会就堆栈溢出征求意见吗?” :p

标签: python bit-manipulation


【解决方案1】:

我认为这可能有效:

具有交替掩码 10101010...10 的按位 XOR 应该每隔一个位反转一次。

如果你想反转 `0011' 中的交替位,结果如下表所示:

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

在 python 中,XOR 是通过i ^ m 实现的。当然,您还必须确定掩码的字面值,这取决于您的 i 数字的大小。

【讨论】:

  • XOR 是找出 msb 后的正确解决方案,因为 0 ^ 1-> 1
  • 只要掩码长度相同,它将适用于任何长度数,我相信@ZeroPiraeus 答案会根据输入的长度计算适当的长度掩码。答案中的 8 位掩码只是一个示例。
【解决方案2】:

以前的单线解决方案,

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

是 O(lg n) 解,其中 n 是输入数。下面显示的渐近更快的解决方案在时间 O(lg(lg n)) 中运行,也就是说,时间与输入数中位数的对数成正比。请注意,所示的二进制搜索在测试中运行良好,但也许可以改进。

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

【讨论】:

  • 看起来很有希望。能否请您详细说明一下,具体来说,-1&lt;&lt;L 做了什么?我也不太清楚二进制搜索...
  • @Cupidvogel,我添加了一段关于
  • 还添加了注释我对“备用位”的解释,即从 MSB 正下方的位开始。要始终切换位 0,2,4... 而不是有时 1,3,5...,请删除 if L & 1: k = k
  • OP 问题的第一行说“问题是如何反转数字的交替位,从 LSB 开始”这听起来不像你根据你在笔记中所说的第一部分重新做。
  • @martineau,感谢您指出这一点。是的,if L &amp; 1: k = k&lt;&lt;1 行应该被删除。当 L 为奇数时,这些行将 0101...0101 掩码移动了一位。移除它们后,k^v 将始终从 LSB 开始切换位。另外,开头附近的if not v: return 0 应更改为if not v: return 1
【解决方案3】:

扩展 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))

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

    • 对于 n = 0b11011,产生不正确的 0b01110。 (应该是 0b10001。)
    • @jwpat7: 我assert 0b11011 ^ 0b10101 == invert_alt_bits(0b11011)
    • 我同意 0b11011 ^ 0b10101 == invert_alt_bits(0b11011)。该等式证明 invert_alt_bits() 对所述问题产生了不正确的答案。
    • @jwpat7:猜猜我们对“陈述的问题”的解释不同。
    • 运行问题中的代码符合你的解释;即问题的代码也给出了0b01110,零比雷埃夫斯的第一个单线也是如此。
    猜你喜欢
    • 2018-03-24
    • 2022-05-24
    • 1970-01-01
    • 1970-01-01
    • 2021-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多