位运算的奇巧淫技|字节青训营笔记

161 阅读4分钟

👀 这是我参与「第四届青训营 」笔记创作活动的的第11天!


😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀😁😀


[@5V)GZ]0U6U)M9_NOZVUD6D.gif

🍻位运算的奇巧淫技

位运算简介:

程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作。比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算。举个例子,6的二进制是110,11的二进制是1011,那么6 and 11的结果就是2,它是二进制对应位进行逻辑运算的结果(0表示False,1表示True,空位都当0处理)。


位运算的特点:

在处理整形数值时,可以直接对组成整形数值的各个位进行操作。这意味着可以使用屏蔽技术获得整个数中的各个位。 &(与)、|(或)、^(异或)、~(非/取反)

和<<运算符将二进制位进行右移或者左移操作。

运算符将用0填充高位;>>运算符用符号位填充高位,没有<<<运算符。 对于int型,1<<35与1<<3是相同的,而左边的操作数是long型是需要对右侧的操作数作数模64。 与:相同为1,或:有一个为1结果为1,异或:相同为0,不同为1. 位运算的规则:

位运算的规则

ab~aa&ba|ba^b
110110
011011
001000
100011

异或的性质:

  • 异或 可以理解为不进位的加法:1+1=0; 0+0=0;1+0=1

  • 交换律 可任意交换运算因子的位置,结果不变。

  • 结合律: 即(ab)c == a(bc)

  • 自反性: ABB = A^0=A,连续喝同一个因子做异或运算,最终结果为自己。 位运算的简单应用

注意:对于任何数x,都有x^x =0, x^0 = x, 同自己求异或为0,同0求异或为自己。

🍻位运算的简单应用


🍻案例一:判断奇偶数


思路:

任何整数,如果是奇数,则转化为二进制数后,最后一位二进制位肯定为1,为偶数,则最后一位二进制位为0。利用这个性质,将任意整数x与1作与运算,如果结果为1,则x为奇数;结果为0,则x为0数。

代码

n=int(input())
if n&1==1:
    print('你输入的数%d为奇数' %n)
else:
    print('你输入的数%d为偶数' %n)


image.png


🍻案例二:获取二进制位是1还是0


思路:

方法一: 做与运算。例如:判断x的第N位二进制是1还是0,可以与1<<N-1做与运算,判断最终结果是1还是0。如果最终结果是0,则x的第N位为0,否则第N位的二进制位1。

方法二: 做与运算。例如:判断x的第N位二进制是1还是0,可以将x>>N-1位,与1做与运算,判断最终结果是1还是0。如果最终结果是0,则x的第N位为0,否则第N位的二进制位1。

代码

n=int(input())
i=int(input())
flag=n & 1<<i-1
if flag==0:
    print("你输入的数字%d的第%d位为0" %(n,i))
else:
    print("你输入的数字%d的第%d位为1" %(n,i))

image.png

代码

n=int(input())
i=int(input())
flag=n>>i-1 & 1
if flag==1:
    print("你输入的数字%d的第%d位为1" %(n,i))
else:
    print("你输入的数字%d的第%d位为0" %(n,i))

image.png


🍻案例三:交换两个整数变量的值


利用异或的性质实现。对于任何数x,都有x^x =0, x^0 = x, 同自己求异或为0,同0求异或为自己。 自反性:ABB = A^0=A,连续喝同一个因子做异或运算,最终结果为自己。

重点:

(1)一个变量按位异或自己的结果为0,即:a ^ a = 0;

(2)一个变量按位异或0的结果为自己,即:a ^ 0 = a;

比如:交换A,B的值

A = A ^ B
B = A ^ BB = A ^ B ^ B = AA = A ^ B (A = A ^ A ^ B = B)

代码

i=int(input())
j=int(input())
i=i^j
j=i^j
i=i^j
print(i)
print(j)

image.png

🍻案例四:找出落单的那个数

一个数组里除了某个数字之外,其他的数字都出现了两次。请写程序找出这个只出现了一次的数字。

思路: 两个数异或位0

代码

str=[1,2,3,4,5,6,7,7,8,8,6,4,3,2,1]
k=0
for i in range(len(str)):
    k=k^str[i]
print(k)

image.png

🍻案例五:二进制中1的个数

请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。

例:9的二进制表示为1001,有2位是1.

思路

  • 和案列二的思路相同

代码

n=int(input())
sum=0
for i in range(len(bin(n))-2):
    if n>>i&1==1:
        sum=sum+1
print(sum)

image.png

  • 其他方法:(x-1)& x,利用该式可循环消去低位的1,循环了多少次,就有多少个1

代码

n=int(input())
count=0
while(1):
    flag=n&(n-1)
    n=flag
    count+=1
    if(flag==0):
        break
print(count)

image.png

🍻案例六:是不是2的整数次方:

用一条语句判断一个整数是不是2的整数次方

思路: 和案例二想法相似,一个数为2的整次方则它的二进制只有一个1

代码

n=int(input())
if((n&n-1)==0):
    print("二的整次方")
else:
    print("不是二的整次方")

image.png

🍻案例七:将整数的奇偶位互换:

题目: 将一个整数的二进制位上的1与0做交换。

思路: 利用位运算的异或运算和与运算。

例如:求10,交换后的数。 10的二进制为:1010

(x<<1) ^ (y>>1) = (0000<<1) ^ (1010>>1)
                =  0000 ^ 0101
                =  0101   //从而实现了,奇偶位互换

代码

n=int(input())
x=n&0xaaaaaaaaaaaaaaaa
y=n&0x5555555555555555
k=len(bin(n)[2::]) if len(bin(n)[2::])>len(bin((x>>1)^(y<<1))[2::]) else len(bin((x>>1)^(y<<1))[2::])
print((bin(n)[2::]).zfill(k))
print((bin((x>>1)^(y<<1))[2::]).zfill(k))

image.png