Python : 位运算 —— 与、或、异或、左移、右移

Python : 位运算 —— 与、或、异或、左移、右移

位运算

位运算是把数字用 二进制 表示之后,对每一位上 0 或者 1 的运算。位运算共有 5 种运算:与、或、异或、左移、右移。

与、或、异或的运算规律:

与 ( & ) 或 ( | ) 异或 ( ^ )
0 & 0 = 0 0 | 0 = 0 0 ^ 0 = 0
0 & 0 = 0 1 | 0 = 1 1 ^ 0 = 1
0 & 0 = 0 0 | 1 = 1 0 ^ 1 = 1
0 & 0 = 0 1 | 1 = 1 1 ^ 1 = 0

左移:

左移运算符 m << n 表示把 m 左移 n 位。在左移 n 位的时候,最左边的 n 位将被丢弃,同时在最右边补上 n 个 0 。比如:

00001010 << 2 = 00101000
10001010 << 3 = 01010000

Python 代码【结果有点不一样】:

>>> bin(int('0b00001010', 2) << 2).replace('0b', '').zfill(8)
'00101000'
>>> bin(int('0b10001010', 2) << 3).replace('0b', '').zfill(8)
'10001010000'
>>> bin(-int('0b00001010', 2) << 2).replace('0b', '').zfill(8)
'-0101000'

右移:

右移运算符 m >> n 表示把 m 右移 n 位。在右移 n 位的时候,最右边的 n 位将被丢弃。但右移时处理最左边位的情形要复杂一点。如果数字是一个无符号树值,则用 0 填补最左边的 n 位;如果数字是一个有符号数值,则用数字的符号位填补最左边的 n 位。也就是说,如果数字原先是一个正数,则右移之后在最左边补 n 个 0 ;如果数字原先是负数,则右移之后在最左边补 n 个 1 。下面是对两个 8 位有符号数进行右移的例子:

00001010 >> 2 = 00000010
10001010 >> 3 = 11110001

Python 代码【结果有点不一样】:

>>> bin(int('0b10001010', 2) >> 2).replace('0b', '').zfill(8)
'00100010'
>>> bin(int('0b10001010', 2) >> 3).replace('0b', '').zfill(8)
'00010001'
>>> bin(-int('0b10001010', 2) >> 3).replace('0b', '').zfill(8)
'-0010010'

应用:二进制中 1 的个数

问题: 请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001 ,有 2 位是 1 。因此,如果输入 9 ,该函数输出 2

分析: 如果整数不等于 0 ,那么该整数的二进制表示中至少有 1 位是 1 。

  • 先假设这个数的最右边一位是 1 ,那么该数减去 1 后,最右边一位变成了0,其他位不变;
  • 再假设最后一位不是 1 而是 0 ,而最右边的 1 在第 m 位,那么该数减去 1 ,第 m 位变成 0 , m 右边的位变成 1 , m 之前的位不变;

上面两种情况总结,一个整数减去 1 ,都是把最右边的 1 变成 0 ,如果它后面还有 0 ,那么 0 变成 1 。那么我们把一个整数减去 1 ,与该整数做位运算,相当于把最右边的 1 变成了 0 ,比如 1100 与 1011 做位与运算,得到 1000 。那么一个整数中有多少个 1 就可以做多少次这样的运算。

# -*- coding: utf-8 -*-

"""
Created on 20-7-1

@author: eln

@requirements: PyCharm 2017.2; Python 3.5.5 |Anaconda 5.1.0 (64-bit)

@decription: 二进制中 1 的个数
"""


def hamming_weigth(n: int) -> int:
    """
    一个数 n 和 它-1 做与运算,就相当于干掉了最右边的 1 .
    """
    bits = 0
    while n:
        bits += 1
        n = (n-1) & n
    return bits


if __name__ == '__main__':
    print(hamming_weigth(11))  # 3
    print(all(bin(n).count('1') == hamming_weigth(n) for n in range(30)))  # True

一些测试代码

# -*- coding: utf-8 -*-

"""
Created on 20-6-19

@author: eln

@requirements: PyCharm 2017.2; Python 3.5.5 |Anaconda 5.1.0 (64-bit)

@decription: python 二进制操作
"""
# 字符串转二进制后进行位移操作
print(bin(int('0b10001010', 2) >> 3).replace('0b', '').zfill(8))

# Python 二进制、整数相互转化 https://blog.csdn.net/a1628864705/article/details/52884441
# python 的按位与、或、异或 运算 https://www.cnblogs.com/xioawu-blog/p/11208066.html
for i in range(16):
    test_bin = bin(i).replace('0b', '').zfill(4)  # 整数转二进制字符串, zfill 整数补 0 , [左, 中, 中, 右]
    test_int = int(test_bin, 2)  # 二进制转整数

    step = 2
    test = [test_bin[i:i + step] for i in range(0, len(test_bin), step)]  # 将一组数分成每 N 个一组, [左中, 中右]

    test_b = int(test[0], 2) & int(test[1], 2)
    print(str(i).zfill(2), test_bin, str(test_int).zfill(2), "  ",
          test, bin(test_b).replace('0b', '').zfill(2), "即", int(test[0], 2), "&", int(test[1], 2), "=", test_b)

    # if int(test[0], 2) > 1 and int(test[1], 2) in [1, 3]:  # 左,右均为 1 的所有组合
    #     print(" ", "这是左,右均为 1 的组合:", str(i).zfill(2), test)

    step = 1
    test2 = [test_bin[i:i + step] for i in range(0, len(test_bin), step)]  # 将一组数分成每 N 个一组, [左, 中, 中, 右]
    # if int(test2[0]) == 1 and int(test2[3]) == 1:  # 左,右均为 1 的所有组合
    #     print(" ", "这是左,右均为 1 的组合:", str(i).zfill(2), test)

    for j in range(16):
        b1 = i | j  # 二进制按位   或 | ,两个位都为 0 时,结果才为 0
        b2 = i & j  # 二进制按位   与 & ,两个位都为 1 时,结果才为 1
        b3 = i ^ j  # 二进制按位 异或 ^ ,两个位相同为 0 ,相异为 1

        # print(" ", bin(i).replace('0b', '').zfill(4), "|", bin(j).replace('0b', '').zfill(4), "=",
        #       bin(b1).replace('0b', '').zfill(4), "即", str(i).zfill(2), "|", str(j).zfill(2), "=", str(b1).zfill(2),
        #       " ", bin(i).replace('0b', '').zfill(4), "&", bin(j).replace('0b', '').zfill(4), "=",
        #       bin(b2).replace('0b', '').zfill(4), "即", str(i).zfill(2), "&", str(j).zfill(2), "=", str(b2).zfill(2),
        #       " ", bin(i).replace('0b', '').zfill(4), "^", bin(j).replace('0b', '').zfill(4), "=",
        #       bin(b3).replace('0b', '').zfill(4), "即", str(i).zfill(2), "^", str(j).zfill(2), "=", str(b3).zfill(2))
        

执行结果:

/home/eln/anaconda3/envs/eln35/bin/python3.5 /home/eln/PycharmProjects/SphinxDoc/_test/test_bin.py
00010001
00 0000 00    ['00', '00'] 00 即 0 & 0 = 0
01 0001 01    ['00', '01'] 00 即 0 & 1 = 0
02 0010 02    ['00', '10'] 00 即 0 & 2 = 0
03 0011 03    ['00', '11'] 00 即 0 & 3 = 0
04 0100 04    ['01', '00'] 00 即 1 & 0 = 0
05 0101 05    ['01', '01'] 01 即 1 & 1 = 1
06 0110 06    ['01', '10'] 00 即 1 & 2 = 0
07 0111 07    ['01', '11'] 01 即 1 & 3 = 1
08 1000 08    ['10', '00'] 00 即 2 & 0 = 0
09 1001 09    ['10', '01'] 00 即 2 & 1 = 0
10 1010 10    ['10', '10'] 10 即 2 & 2 = 2
11 1011 11    ['10', '11'] 10 即 2 & 3 = 2
12 1100 12    ['11', '00'] 00 即 3 & 0 = 0
13 1101 13    ['11', '01'] 01 即 3 & 1 = 1
14 1110 14    ['11', '10'] 10 即 3 & 2 = 2
15 1111 15    ['11', '11'] 11 即 3 & 3 = 3

Process finished with exit code 0
©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值