本文由gpt生成,仅作为本人自用的参考资料使用,不保证完全正确!
Python 中的位运算是非常常用且高效的操作,尤其在算法题、图论、压缩状态、权限管理等场景中非常有用。
运算符 | 名称 | 作用 | 示例 ( a = 0b0110 ,b = 0b1011 ) |
结果(二进制) |
---|---|---|---|---|
& |
按位与(AND) | 两位都为 1 ⇒ 1,否则 0 | a & b |
0b0010 |
| |
按位或(OR) | 只要有一位为 1 ⇒ 1 | a | b |
0b1111 |
^ |
按位异或(XOR) | 不同为 1,相同为 0 | a ^ b |
0b1101 |
~ |
按位取反(NOT) | 0→1,1→0 (含符号位) | ~a |
…1111 1001 † |
<< |
左移 | 高位丢弃,低位补 0 | a << 2 |
0b11000 |
>> |
右移 | 正数高位补 0;负数补 1 | b >> 1 |
0b0101 |
† 取反得到的是 无限长补码 表示;见下节说明。
另外:
0b
是 Python 中表示 “二进制” 的前缀,0b中的0并不是符号位
特性一览,摘自**力扣评论** :
与运算(AND):
任何数和0做与运算,结果是0,即 x & 0 = 0。
例如,5(101) & 0 = 0。
任何数和其自身做与运算,结果是自身,即 x & x = x。
例如,5(101) & 5(101) = 5(101)。
用途:清除某些位,比如清除最低位:
x & (x - 1) # 去掉最低位的 1
或运算(OR):
任何数和0做或运算,结果是自身,即 x | 0 = x。
例如,5(101) | 0 = 5(101)。
任何数和其自身做或运算,结果是自身,即 x | x = x。
例如,5(101) | 5(101) = 5(101)。
用途:设定某些位为 1
异或运算(XOR):
任何数和0做异或运算,结果是自身,即 x ^ 0 = x。
例如,5(101) ^ 0 = 5(101)。
**任何数和其自身做异或运算,结果是0,即 x ^ x = 0**。
例如,5(101) ^ 5(101) = 0。
异或运算满足交换律和结合律,即 a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c。
例如,5(101) ^ 3(011) ^ 4(100) = 5 ^ (3 ^ 4) = (5 ^ 3) ^ 4。
用途:
a ^ a = 0
a ^= b; b ^= a; a ^= b
非运算(NOT):
非运算会反转操作数的所有位,包括符号位。
对整数 x
,返回 -(x+1)
~5 # = -6,因为:~x = -x - 1
左移运算(SHL):
左移n位等于乘以2的n次方,即 x << n 等价于 x * 2^n。
例如,5(101) << 2 = 20(10100)。
x << k
不会改变 x
本身
左移运算不改变操作数的符号位。
右移运算(SHR):
每右移 1 位,相当于 //2(向下取整)
右移n位等于除以2的n次方,即 x >> n 等价于 x // 2^n。
例如,20(10100) >> 2 = 5(101)。
x >> k
不会改变 x
本身
右移运算正数高位补 0;负数补 1
右移并不会改变符号,Python 会保留负号
Python 的整数没有位数上限,用二进制补码无限延伸。
x = -5
→ 内部补码为 …11111011
~x
相当于 -(x+1)
x = -5
print(~x) # 4
print(~4) # -5
函数 / 方法 | 作用 | 栗子 |
---|---|---|
bin(x) /oct(x) /hex(x) |
转 2/8/16 进制字符串 | bin(10) →'0b1010' |
int(s, base) |
任意进制转十进制 | int('FF',16) →255 |
int.bit_length() |
去掉符号位后所需位宽 | (1023).bit_length() →10 |
int.bit_count() (Py 3.8+) |
Hamming weight,统计 1 的个数 | (0b1011).bit_count() →3 |
int.to_bytes() /int.from_bytes() |
整数↔字节序列 | (255).to_bytes(2,'big') |
a ^= b
b ^= a
a ^= b
x & 1 == 1 # 奇数
x & 1 == 0 # 偶数
0-based 的意思是「从 0 开始编号」,位运算相关的最低位也就是第0位一般是最右边的那一位
(x >> k) & 1 == 1
# 或:
x & (1 << k) != 0
含义:
x >> k
把第 k
位移到最右边& 1
只保留最右边那一位(高位如果还有1,就被消去了)1
mask = 1 << k # 第 k 位掩码 (0‑based),例如1 << 3 就等价于0b1000,第0位的1左移了三位抵达第k位
x |= mask # 把第 k 位变成 1(无论原来是几)
x &= ~mask # 把第 k 位清 0
x ^= mask # 把第 k 位翻转(0→1, 1→0)
low8 = x & 0xff # 仅保留最低 8 位
high4 = (x >> 4) & 0xf
背后的思路:用掩码(mask)保留需要的位
0xff
是什么?0xff = 0b11111111 = 255
8 个 1
,用于掩盖(保留)最低 8 位。
x = 0b1011011100101101
x & 0xff = 仅保留最后 8 位,其它全部变 0
所以 x & 0xff
就是 保留最低 8 位,其余清零。
(x >> 4) & 0xf
是什么?x >> 4
:将 x 向右移 4 位,相当于丢掉低 4 位;& 0xf = 0b1111
:只保留当前最低的 4 位;✅ 举个例子
x = 0b10110110_00111100
low8 = x & 0xff # 得到 0b00111100 = 0x3C = 60
high4 = (x >> 4) & 0xf # 移掉低 4 位再取低 4 位 = 0b0110 = 6
S = 0b10110 # 假设 S 表示一个集合
sub = S
while sub:
# 处理子集 sub
sub = (sub - 1) & S
它是用来枚举一个集合(用位表示)的所有子集。
集合 S 用一个二进制数表示,比如 S = 0b10110
表示集合 {1, 2, 4}
,
也就是第i
位若为1,代表i
在集合中;
它的子集也用相同形式表示,比如 0b10010
表示 {1, 4}
;
每次减 1 再与原集合相与,就能跳转到下一个有效子集。
sub = S # 从全集 S 开始
while sub:
# 处理 sub,例如打印、统计等
sub = (sub - 1) & S
这个过程枚举了 S 的所有非空子集,不重复、也不多余。
假设 S = 0b110
,对应集合 {1, 2}
:
枚举过程如下:
step | sub (二进制) | 子集(集合) |
---|---|---|
1 | 110 |
{1, 2} |
2 | 101 |
{0, 2} ❌不合法(超出 S)→ 被掩码去掉 →100 {2} |
3 | 011 |
{0, 1} ❌也不合法 → 掩码 →010 {1} |
4 | 001 |
{0} ❌ → 掩码 →000 |
+ - *
,但高于比较运算 < > ==
。~
> << >>
> &
> ^
> |
。~
是单目)。x = 1 << 3 + 1 # 等价于 1 << (3+1) → 16
y = 1 | 2 & 3 # 等价于 1 | (2 & 3) → 1|2 → 3
~x == -(x+1)
。bit_length
&bit_count
、掩码技巧让位运算在性能与内存受限的场景(如算法竞赛、嵌入式、加密)非常有用。