位运算的性质和应用

位运算有

  • & 按位与
  • | 按位或
  • ^按位异或
  • >>n 右移n位
  • < 左移n位

参考:
https://zhuanlan.zhihu.com/p/37167219
https://www.cnblogs.com/lmsthoughts/p/6659571.html

在int为32位的语言中,有一些计算的小技巧

1、int最大值
(1<<31)-1

2、int最小值
1<<31

3、取绝对值
a ^ (a>>31) - (a>>31);

4、全1
~0 = -1 = 0xffffffff(1个f 4位,8个f 32位)

5、负数
-a = ~a + 1

一些性质

  1. a对其自身运算
    a & a = a
    a | a = a
    a ^ a = 0 (去重常用)

  2. a对0运算
    a & 0 = 0
    a | 0 = a
    a ^ 0 = a (置换常用)

  3. a对(~0)运算
    a & (~0) = a
    a | (~0) = ~0
    a ^ (~0) = ~a (取反的另一种写法)

  4. 通过位运算交换两个数
    a ^= b;
    b ^= a;
    a ^= b;

位与操作解释:第一步:a ^= b ---> a = (a^b);
第二步:b ^= a ---> b = b^(a^b) ---> b = (b^b)^a = a
第三步:a ^= b ---> a = (a^b)^a = (a^a)^b = b

  1. 判断奇偶(取出最后一位)
    a & 1 等价于 a % 2(结果等于0为偶,位运算效率高)

  2. 判断符号是否相同
    a ^ b >= 0

  3. 取出第i+1位
    a & (1<

  4. i+1位 置1
    a |=1<

  5. i+1位 置0
    a &=~(1<

  6. i+1位 反转
    a ^ (1<

  7. 在对应i+1位,插入b的对应位;
    a |=1< a & (b & 1< (置1后相与=置0后相或)

  8. 保留最后i-1位;
    a & ((1<

  9. 清零最后i-1位;
    a & ~((1<

  10. 删除最后的1;
    a & (a-1) (可用于判断2的幂数)

  11. 仅保留最后一个1;
    a & (-a)

  12. 得到最高位的1;
    a = a |(a>>1);
    a = a |(a>>2);
    a = a |(a>>4);
    a = a |(a>>8);
    a = a |(a>>16);
    return (a+1)>>1;

异或的一些性质

  • x^0 = x
  • x^x = 0
  • 交换律:x^y = y^x
  • 结合律:(x^y)^z = x^(y^z)
  • 自反性:x^y^y = x
    其中- x^0 = xx^x = 0x^y^y = x很常在位运算的题中用到

A集合里拿掉数x得到B集合,求x

XOR(X)表示将X集合内所有的数做异或
XOR(B) ^ XOR(A) = XOR(B) ^ XOR(B) ^ x = 0 ^ x = x

A集合里拿掉数x、y得到B集合,求x和y

首先按上一个的办法
xor(A) ^ xor(B) = xor(B) ^ xor(B) ^ x ^ y = 0 ^ x ^ y = x ^ y

x ^ y的二进制结果,第n位为1,说明x和y的第n位不相同

根据第n位是否为0把A里所有的数分成A1和A0两个数组(A1里的数的二进制第n位都是1,A0都是0)

A1和A0应该各包含了a或者b(这样第n位才能异或出1),同理可以把B分成B1和B0两个数组

可以得到第一个数 x = A1^B1

第二个数可以y = A0^B0,当然也可以用x^y^x求得

另外如果x^y为0,即x == y,令SUM(X)为X集合内所有数求和
(SUM(A) - SUM(B)) / 2 = x

集合A里只有数x出现1次,其余数全都重复出现2次,求x

xor(A) = x^y^y^…^z^z = x^(y^y^…^z^z) = x^0 = x

集合A里只有数x出现1次,其余数全都重复出现3次,求x

xor的本质相当于“按位模2加”(adding modulo 2),令p1,p2…pn为布尔值,true为1、false为0,(+)表示异或操作。

p1 (+) p2 (+) ... (+) pn == ( p1 + p2 + ... + pn ) % 2

所以只需要实现按位模3加

( p1 + p2 + ... + pn ) % 3

将集合中所有数二进制表示的同一位的0或1相加,最终的和对3去摸,得到的数即是x

如 A = {5, 7, 7, 7},二进制表示两个数

  101
  111
  111
+ 111
------
  434
%   3
------
  101

将每个数想象成32位的二进制,对于每一位的二进制的1和0累加起来必然是3N或者3N+1, 为3N代表目标值在这一位没贡献,3N+1代表目标值在这一位有贡献(=1),然后将所有有贡献的位|起来就是结果。这样做的好处是如果题目改成K个一样,只需要把代码改成cnt%k。

     res,flag = 0 ,0
     for n in nums:
         if n < 0:
             flag += 1
     flag %= 3
     for i in range(32):
         mask = 1 << i
         cnt = 0
         for num in nums:
              if mask & num:
                  cnt += 1
          if cnt % 3:
              res |= mask

python下这个方法遇到负数时需要改一下,可以先计算负数的个数,然后将全部数转成绝对值进行计算

没有^操作时候实现异或,只用&与 |或 ~非

公式很多 如
x ^ y == (~x & y) | (x & ~y)

消去x最后一位的1

x & (x - 1)
x = 1100
x - 1 = 1011
x & (x - 1) = 1000

你可能感兴趣的:(位运算的性质和应用)