常见的位运算包括:
1. 按位与(&):对两个二进制数的每一位进行逻辑与运算。只有当对应位都是1时,结果才为1,否则为0。
示例:5 & 3
等价于 0101 & 0011
,结果是 0001
,即 1
。
2。 按位或( | ):对两个二进制数的每一位进行逻辑或运算。只要有一个对应位是1,结果就是1。
示例:5 | 3
等价于 0101 | 0011
,结果是 0111
,即 7
。
3. 按位异或(^):对两个二进制数的每一位进行异或运算。当对应位不同时,结果为1;相同时,结果为0。
示例:5 ^ 3
等价于 0101 ^ 0011
,结果是 0110
,即 6
。
Takeaway: 0 和 任意数做异或运算都等于这个数自身;1 和 任意数做异或运算,结果是将这个数每个二进制位上的数字反转过来。
以上运算也被称作是双目运算,即把两个整数作为二进制数,对这两个数的每个数位逐一进行相应运算
4. 左移(<<):将二进制数的每一位向左移动指定的位数,右边用0填充。
示例:5 << 1
等价于 0101 << 1
,结果是 1010
,即 10
。
5. 右移(>>):将二进制数的每一位向右移动指定的位数,左边用0(对于无符号数)或符号位(对于有符号数)填充。
示例:5 >> 1
等价于 0101 >> 1
,结果是 0010
,即 2
。
6. 取反 (~):对二进制数的每个二进位取反,使数字 11 变为 00,00 变为 11
这些运算在低级编程和性能优化中非常有用,因为它们直接操作二进制位,执行速度通常比其他类型的运算快。
应用1 - 判断整数奇偶
一个整数,只要是偶数,其对应二进制数的末尾一定为 0 (0 * 2^0);只要是奇数,其对应二进制数的末尾一定为 1 (1 * 2 ^0)。所以,我们通过与 1 进行按位与运算,即可判断某个数是奇数还是偶数。
(x & 1) == 1 # 奇数,因为 x 最后一位是 1
(x & 1) == 0 # 偶数,因为 x 最后一位是 0
应用2 - 二进制数选取指定位
如果我们想要从一个二进制数 X 中取出某几位,使取出位置上的二进位保留原值,其余位置为 0,则可以使用另一个二进制数 Y,使该二进制数上对应取出位置为 1,其余位置为 0 (不管这个位置原先是什么数字,都会变成0)。然后令两个数进行按位与运算(X & Y
),即可得到想要的数。
举个例子,比如我们要取二进制数 X=01101010 的末尾 4 位,则只需将 X=01101010 与 Y=00001111 (末尾 4 位为 1,其余位为 0) 进行按位与运算,即 01101010 & 00001111 == 00001010
。其结果 00001010 就是我们想要的数(即二进制数 01101010 的末尾 4 位)。
应用3 - 反转指定位
如果我们想要把一个二进制数 X 的某几位进行反转,则可以使用另一个二进制数 Y,使得该二进制上对应选取位置为 1,其余位置为 0。然后令两个数进行按位异或运算(X ^ Y
),即可得到想要的数。
举个例子,比如想要将二进制数 X=01101010 的末尾 4 位进行反转,则只需将 X=01101010 与 Y=00001111(末尾 4 位为 1,其余位为 0)进行按位异或运算,即 01101010 ^ 00001111 = 01100101
。其结果 01100101 就是我们想要的数(即将二进制数 X=01101010 的末尾 4 位进行反转)。
(因为对于XOR来说,1 ^ 1 = 0, 0 ^ 1 = 1, 刚好起到反转的效果)
应用4 - 交换两个数(只能交换两个数)
实际上也是利用了应用3
a, b = 01010, 10100
a ^= b # a = a ^ b = 11110 相当于省去一个临时变量,利用负负得正
b ^= a # b = b ^ a = 01010
a ^= b # a = a ^ b = 10100
应用5 - 将二进制最右侧为 1 的二进位改为 0
通过 X & (X-1) 即可完成
比如 X=01101100,X−1=01101011,则 X & (X - 1) == 01101100 & 01101011 == 01101000
,结果为 01101000(即将 X 最右侧为 1 的二进制为改为 0)
应用6 - 计算二进制中二进位为 1 的个数
利用应用5的性质,因为一次的X & (X-1)可以把最右侧的 1 改为 0,那么可以多次操作,直到 X全部变成 0,并记录下操作的次数
应用7 - 判断一个数是否为 2 的次方
通过判断 X & (X - 1) == 0
是否成立,即可判断 X 是否为 2 的幂次方。
这是因为:
- 凡是 2 的幂次方,其二进制数的某一高位为 1,并且仅此高位为 1,其余位都为 0。比如:4(10)=00000100(2)、8(10)=00001000(2)。
- 不是 2 的幂次方,其二进制数存在多个值为 1 的位。比如:510=00000101(2)、610=00000110(2)。
接下来我们使用 X & (X - 1)
操作,将原数对应二进制数最右侧为 1 的二进位改为 0 之后,得到新值:
- 如果原数是 2 的幂次方,则通过
X & (X - 1)
操作之后,新值所有位都为 0,值为 0。 - 如果该数不是 2 的幂次方,则通过
X & (X - 1)
操作之后,新值仍存在不为 0 的位,值肯定不为 0。
所以我们可以通过是否为 0 即可判断该数是否为 2 的幂次方。
应用8 - 用位运算实现快速乘 2 和 除 2 操作
乘2:y << 1 -> y 代表的二进制数集体向左移动一位
除2(向下取整): y >> 1 -> y 代表的二进制数集体向右移动一位