1.介绍
大家都知道计算机的世界是二进制世界,而位运算就是直接对二进制进行操作,这就减少了时间的开销,更为致命的是python的速度慢于c语言10-20倍之间,故掌握位运算还是很有必要的。
2.十进制与二进制的转换
这儿简单的说一下十进制对二进制的转换。
(1)十进制转为二进制,使用的辗转相除法,获得的余数的从下至上就是二进制数。见下例
(2)二进制转十进制,就是简单的指数相加。见下例
3.位运算符
位运算符一共有6个,分别为按位与(&)、按位或(|)、按位异或(^)、按位取反(~)、按位左移(<<)、按位右移(>>),这儿给个表格,总结一下运算符的名称、算符和应用,想看详细的见下面
位运算符名称 | 算法 | 应用 |
---|---|---|
按位与(&) | 0&0=0、0&1=0、1&0=0、1&1=1 | 判断奇偶性 |
按位或(|) | 0|0=0、 0|1=1、 1|0=1、 1|1=1 | |
按位异或(^) | 0^0=0、0^1=1、 1^0=1、1^1=0 | 交换两数(无中间变量) |
按位取反(~) | ~0=1、 ~1=0 | |
按位左移(<<) | a<<n:将a的二进制左移n位,最左边n位丢弃,右边补0 | 一个数乘2的n方 |
按位右移(>>) | a>>n:将a的二进制右移n位,最右边n位丢弃,左边补0 |
(1)按位与(&)
按位与对二进制怎么操作的,就是
0 & 0 = 0
1 & 0 = 0
0 & 1 = 0
1 & 1 = 1
换句话说,这种操作有点像逻辑上的且关系(即True and False)
举个例子
a = 18
b = 15
print('a的二进制是{},b的二进制是{}'.format(bin(a), bin(b)))
print(a&b)
"""
结果:
a的二进制是0b10010,b的二进制是0b1111
2
"""
使用表格来说明具体的操作
a | b | 按位与(&) |
---|---|---|
1 | 0(没有补0) | 0 |
0 | 1 | 0 |
0 | 1 | 0 |
1 | 1 | 1 |
0 | 1 | 0 |
这样获得二进制数为10 ,那么10进制数就为2^(1)+2^(0)=2
(2)按位或(|)
按位或(|)与按位与(&)类似,操作为
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1
其余算法与按位与(&)一模一样
(3)按位异或(^)
简单的说,就是两个位相同为0,相反为1,操作为:
0 ^ 0 = 0,
1 ^ 0 = 1,
0 ^ 1 = 1,
1 ^ 1 = 0
(4)按位取反(~)
简单的说,就是0 变 1,1 变 0
~0 = 1
~1 = 0
(5)按位左移(<<)
简单的说," a<< n ":将a的二进制位全部向左移n位,最左边n位舍弃,右边补0
(注意二进制数前面有0作为填充,0的个数与变量的类型有关系)
举个例子
a=18
n=3
print(a<<3)
"""
结果为:144
前面已经得到 a 的二进制为 10010
左移3位,即 10010000 转为10进制: 2^7+2^4=128+16=144
"""
(6)按位右移(>>)
简单的说,"a>>n":将一个数的二进制位全部向右移n位,最右边n位舍弃,左边补0,与按位左移(<<)反过来。
4.位运算符的运用
4.1按位与(&)
4.1.1 判断奇偶性
当一个数a,与1进行按位与操作,即
(1) a & 1 = 0,则a为偶数
(2) a & 1 = 1,则a为奇数
为什么会有这个性质呢?
证明如下:我们来看a的二进制为 0000 0001,根据按位与的性质0&1=0、0&0=0,可知结果中的二进制只有最后一位不能确定,其余均为0。又知计算a的二进制时,由于辗转相除是反过来的,故a的二进制的最后一位即为a/2的余数,不妨设其为b。
可知当b为0时,这时b&1=0,故为偶数(因b为a对2的余数)
当b为1时,这时b&1=1,为奇数(因b为a对2的余数)
4.2按位异或(^)
4.2.1交换两个数(不用中间变量)
当有2个数 a 和 b,执行下面代码段即可实现交换2个变量。
a=18
b=15
a = a ^ b
b = b ^ a
a = a ^ b
print('a={},b={}'.format(a,b))
"""
结果
a=15,b=18
"""
那为什么会有这种性质呢?
证明写在代码段中,见下
证明如下:假设a与b均为二进制数
对二进制b与a^b的情况作讨论(讨论的是二进制中的1个数)
(1)当b二进制中的某个数为0时,若a^b 相对应得这个数也为0,由异或(^)性质,
可以得到初始时a为0,这时做 a = a ^ b = 0 (假设条件)
再做 b = b ^ a(ps:0 ^ 0 = 0) 对应的这个数字为0,即b = a (ps:初始的a)
再做 a = a ^ b(ps:0 ^ 0 = 0) 对应的这个数字为0,即a = b (ps:初始的b)
(2)当b二进制中的某个数为0时,若a^b 相对应得这个数为1,由异或(^)性质,
可以得到初始时a为1,这时做 a = a ^ b = 1 (假设条件)
再做 b = b ^ a(ps:0 ^ 1 = 1) 对应的这个数字为1,即b = a (ps:初始的a)
再做 a = a ^ b(ps:1 ^ 1 = 0) 对应的这个数字为0,即a = b (ps:初始的b)
(3)当b二进制中的某个数为1时,若a^b 相对应得这个数为0,由异或(^)性质,
可以得到初始时a为1,这时做 a = a ^ b = 0 (假设条件)
再做 b = b ^ a(ps:1 ^ 0 = 1) 对应的这个数字为1,即b = a (ps:初始的a)
再做 a = a ^ b(ps:0 ^ 1 = 1) 对应的这个数字为1,即a = b (ps:初始的b)
(4)当b二进制中的某个数为1时,若a^b 相对应得这个数为1,由异或(^)性质,
可以得到初始时a为0,这时做 a = a ^ b = 1 (假设条件)
再做 b = b ^ a(ps:1 ^ 1 = 0) 对应的这个数字为0,即b = a (ps:初始的a)
再做 a = a ^ b(ps:1 ^ 0 = 1) 对应的这个数字为1,即a = b (ps:初始的b)
综上:无论b二进制唯0或1,做次操作均可交换a和b,推至n个二进制数,不失一般性,仍然成立
4.3 按位左移(<<)
4.3.1 一个数乘2的n次方更快
有一个数a,左移n位,且左移时被溢出舍弃的高位中不包含1
进行a<<n,即进行 的操作,当然如果a=1,就是
操作了。
那么为什么会有这种性质呢
证明如下:先进行一般性假设
可以得到此时
当a向左移动n位时,
可以得到新的
即
推广至一般情况,仍然成立。