简体   繁体   English

Python integer 的反转位

[英]Reversing bits of Python integer

Given a decimal integer (eg. 65), how does one reverse the underlying bits in Python?给定一个十进制 integer(例如 65),如何反转 Python 中的底层位? ie. IE。 the following operation:以下操作:

65 → 01000001 → 10000010 → 130

It seems that this task can be broken down into three steps:看来这个任务可以分解为三个步骤:

  1. Convert the decimal integer to binary representation将十进制 integer 转换为二进制表示
  2. Reverse the bits反转位
  3. Convert back to decimal转换回十进制

Steps #2 and 3 seem pretty straightforward (see this and this SO question related to step #2), but I'm stuck on step #1.第 2 步和第 3 步看起来很简单(请参阅与第 2 步相关的这个这个SO 问题),但我被困在第 1 步。 The issue with step #1 is retrieving the full decimal representation with filling zeros (ie. 65 = 01000001, not 1000001).步骤 #1 的问题是检索完整的十进制表示并填充零(即 65 = 01000001,而不是 1000001)。

I've searched around, but I can't seem to find anything.我四处寻找,但我似乎找不到任何东西。

int('{:08b}'.format(n)[::-1], 2)

You can specify any filling length in place of the 8. If you want to get really fancy,您可以指定任何填充长度来代替 8。如果您想变得非常花哨,

b = '{:0{width}b}'.format(n, width=width)
int(b[::-1], 2)

lets you specify the width programmatically.让您以编程方式指定宽度。

If you are after more speed, you can use the technique described in http://leetcode.com/2011/08/reverse-bits.html如果你追求更快的速度,你可以使用http://leetcode.com/2011/08/reverse-bits.html中描述的技术

def reverse_mask(x):
    x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1)
    x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2)
    x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4)
    x = ((x & 0x00FF00FF) << 8) | ((x & 0xFF00FF00) >> 8)
    x = ((x & 0x0000FFFF) << 16) | ((x & 0xFFFF0000) >> 16)
    return x

best way to do is perform bit by bit shifting最好的方法是逐位执行

def reverse_Bits(n, no_of_bits):
    result = 0
    for i in range(no_of_bits):
        result <<= 1
        result |= n & 1
        n >>= 1
    return result
# for example we reverse 12 i.e 1100 which is 4 bits long
print(reverse_Bits(12,4))
def reverse_bit(num):
    result = 0
    while num:
        result = (result << 1) + (num & 1)
        num >>= 1
    return result

We don't really need to convert the integer into binary, since integers are actually binary in Python.我们真的不需要将整数转换为二进制,因为整数在 Python 中实际上是二进制的。

The reversing idea is like doing the in-space reversing of integers.反转的想法就像对整数进行空间反转。

def reverse_int(x):
    result = 0
    pos_x = abs(x)
    while pos_x:
        result = result * 10 + pos_x % 10
        pos_x /= 10
    return result if x >= 0 else (-1) * result

For each loop, the original number is dropping the right-most bit(in binary).对于每个循环,原始数字正在删除最右边的位(二进制)。 We get that right-most bit and multiply 2 ( <<1 ) in the next loop when the new bit is added.当添加新位时,我们得到最右边的位并在下一个循环中乘以 2 ( <<1 )。

There's no need, and no way, to "convert a decimal integer to binary representation".没有必要也没有办法“将十进制整数转换为二进制表示”。 All Python integers are represented as binary;所有 Python 整数都表示为二进制; they're just converted to decimal when you print them for convenience.为方便起见,当您打印它们时,它们只是转换为十进制。

If you want to follow this solution to the reversal problem, you only need to find appropriate numbits .如果你想按照这个解决方案来解决反转问题,你只需要找到合适的numbits You can either specify this by hand, or compute the number of bits needed to represent an integer n with n.bit_length() (new in Python 2.7 and 3.1).您可以手动指定它,也可以使用n.bit_length()计算表示整数n所需的位数(Python 2.7 和 3.1 中的新功能)。

However, for 65, that would give you 7, as there's no reason why 65 should require any more bits.但是,对于 65,这会给你 7,因为没有理由 65 需要更多位。 (You might want to round up to the nearest multiple of 8...) (您可能需要四舍五入到最接近的 8 倍数...)

You can test the i'th bit of a number by using a shift and mask.您可以使用移位和掩码测试数字的第 i 位。 For example, bit 6 of 65 is (65 >> 6) & 1 .例如,65 的第 6 位是(65 >> 6) & 1 You can set a bit in a similar way by shifting 1 left the right number of times.您可以通过向右移动 1 的次数以类似的方式设置位。 These insights gives you code like this (which reverses x in a field of 'n' bits).这些见解为您提供了这样的代码(在“n”位的字段中反转 x)。

def reverse(x, n):
    result = 0
    for i in xrange(n):
        if (x >> i) & 1: result |= 1 << (n - 1 - i)
    return result

print bin(reverse(65, 8))

An inefficient but concise method that works in both Python 2.7 and Python 3:一种在 Python 2.7 和 Python 3 中都有效的低效但简洁的方法:

def bit_reverse(i, n):
    return int(format(i, '0%db' % n)[::-1], 2)

For your example:对于您的示例:

>>> bit_reverse(65, 8)
130

Regularly there is the need to apply this operation on array of numbers and not for single number.通常需要将此操作应用于数字数组而不是单个数字。 To increase speed, it's probably better to use NumPy array.为了提高速度,最好使用 NumPy 数组。 There are two solutions.有两种解决方案。

x1.34 faster than second solution: x1.34 比第二种解决方案快:

import numpy as np
def reverse_bits_faster(x):
  x = np.array(x)
  bits_num = x.dtype.itemsize * 8
  # because bitwise operations may change number of bits in numbers
  one_array = np.array([1], x.dtype)
  # switch bits in-place
  for i in range(int(bits_num / 2)):
    right_bit_mask = (one_array << i)[0]
    left_bit = (x & right_bit_mask) << (bits_num - 1 - i * 2)
    left_bit_mask = (one_array << (bits_num - 1 - i))[0]
    right_bit = (x & left_bit_mask) >> (bits_num - 1 - i * 2)
    moved_bits_mask = left_bit_mask | right_bit_mask
    x = x & (~moved_bits_mask) | left_bit | right_bit
  return x

Slower, but more easy to understand (based on solution proposed by Sudip Ghimire ):更慢,但更容易理解(基于​​Sudip Ghimire 提出的解决方案):

import numpy as np
def reverse_bits(x):
  x = np.array(x)
  bits_num = x.dtype.itemsize * 8
  x_reversed = np.zeros_like(x)
  for i in range(bits_num):
    x_reversed = (x_reversed << 1) | x & 1
    x >>= 1
  return x_reversed

You could also use a Look up table (that can be generated once using methods above):您还可以使用查找表(可以使用上述方法生成一次):

LUT = [0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240,
       8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120,
       248, 4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180,
       116, 244, 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60,
       188, 124, 252, 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50,
       178, 114, 242, 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218,
       58, 186, 122, 250, 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214,
       54, 182, 118, 246, 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94,
       222, 62, 190, 126, 254, 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81,
       209, 49, 177, 113, 241, 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89,
       217, 57, 185, 121, 249, 5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85,
       213, 53, 181, 117, 245, 13, 141, 77, 205, 45, 173, 109, 237, 29, 157,
       93, 221, 61, 189, 125, 253, 3, 131, 67, 195, 35, 163, 99, 227, 19, 147,
       83, 211, 51, 179, 115, 243, 11, 139, 75, 203, 43, 171, 107, 235, 27,
       155, 91, 219, 59, 187, 123, 251, 7, 135, 71, 199, 39, 167, 103, 231, 23,
       151, 87, 215, 55, 183, 119, 247, 15, 143, 79, 207, 47, 175, 111, 239,
       31, 159, 95, 223, 63, 191, 127, 255]

def reverseBitOrder(uint8):
    return LUT[uint8]

One more way to do it is to loop through the bits from both end and swap each other.另一种方法是遍历两端的位并相互交换。 This i learned from EPI python book.这是我从 EPI python 书中学到的。

i = 0; j = 7
num = 230
print(bin(num))
while i<j:
    # Get the bits from both end iteratively
    if (x>>i)&1 != (x>>j)&1:
        # if the bits don't match swap them by creating a bit mask
        # and XOR it with the number 
        mask = (1<<i) | (1<<j)
        num ^= mask
    i += 1; j -= 1
print(bin(num))

All what you need is numpy您需要的只是 numpy

import numpy as np
x = np.uint8(65)
print( np.packbits(np.unpackbits(x, bitorder='little')) )

performance:表现:

py -3 -m timeit "import numpy as np; import timeit; x=np.uint8(65); timeit.timeit(lambda: np.packbits(np.unpackbits(x, bitorder='little')), number=100000)"
1 loop, best of 5: 326 msec per loop

kimstik: You should move the import out of the statement for better performance record. kimstik:您应该将导入从语句中移出以获得更好的性能记录。

>>> python -m timeit --setup="import numpy as np; x = np.uint8(65);" --repeat=10 --number=1000 "np.packbits(np.unpackbits(x, bitorder='little'))"
1000 loops, best of 10: 2.37 usec per loop 
bin(x)[:1:-1]

one line, and it automatically goes for the top bit.一行,它会自动进入最高位。

>>> x = 0b1011000
>>> bin(x)[:1:-1]
'0001101'
>>> x = 0b100
>>> bin(x)[:1:-1]
'001'

the "0b" on the front of the text-conversion is stripped by the "1" in [:1:-1] which, after the inversion (by -1) has 1 automatically added to it (sigh, range is really weird) before being used as the start point not the end.文本转换前面的“0b”被 [:1:-1] 中的“1”剥离,在反转(由-1)之后自动添加 1(叹息,范围真的很奇怪) 在被用作起点而不是终点之前。

you'll need zero-padding on the front to get it a fixed-width reversing but even there [:1:-1] will still do the auto-length-detection您需要在前面进行零填充以使其具有固定宽度的反转,但即使在那里 [:1:-1] 仍然会进行自动长度检测

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM