算法通关村第十一关——搞清位运算

源码、反码和补码

很多人都记不清源码、反码和补码的区分,都是二进制,其实记忆起来很简单,分为正数和负数来记。正数的原码、反码和补码都是一样的,负数的原码符号位为1,反码是在原码的基础上进行改变:保持符号位不变,其他位取反;补码是在反码的基础上:反码的末位加1。如下图所示:

在这里插入图片描述

位运算规则

与:&。运算规则:对于每个二进制位,都为1,结果才为1,否则为0。

0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

或:|。运算规则:对于每个二进制位,只要有一个为1,结果就为1。

00 = 0
01 = 1
10 = 1
11 = 1

异或:,代码中用^表示。运算规则:对于每个二进制位,相同为0,不相同为1。

00 = 0
01 = 1
10 = 1
11 = 0

取反:~,运算规则:对于每个二进制位进行取反操作,1变为0,0变为1。

~1 = 0
~0 = 1

移位运算与乘除法

左移运算:<<,将全部二进制位左移若干位,高位丢弃,低位补0。对于左移运算,算术移位和逻辑移位是相同的。

右移运算:>>,将全部二进制位向右移动若干位,低位丢弃,高位的补位由算术移位或逻辑移位决定:

  • 算数右移,最高位补最高位,如-50(补码:1100 1110)算数右移两位是-13,对应二进制:1111 0011(补码)
  • 逻辑右移,最高位补0,如-50逻辑右移两位是51,对应的二进制表示:0011 0011(补码)

将一个数左移 k位,等价于将这个数乘以 2 k 2^k 2k。当乘数不是 2 的整数次幂时,可以将乘数拆成若干项 2 的整数次幂之和,例如, a × 6 a×6 a×6等价于 ( a < < 2 ) + ( a < < 1 ) (a<<2)+(a<<1) (a<<2)+(a<<1)。算术右移运算对应除法运算,将一个数右移 k 位,相当于将这个数除以 2 k 2^k 2k,结果向下取整。

位运算常用技巧

  • 幂等律: a & a = a a \& a=a a&a=a a ∣ a = a a∣a = a aa=a(注意异或不满足幂等律);

  • 交换律: a & b = b & a a \& b = b \& a a&b=b&a a ∣ b = b ∣ a a ∣ b = b ∣ a ab=ba a ⊕ b = b ⊕ a a ⊕ b = b ⊕ a ab=ba

  • 结合律: ( a & b ) & c = a & ( b & c ) (a \& b) \& c = a \& (b \& c) (a&b)&c=a&(b&c)

    ( a ∣ b ) ∣ c = a ∣ ( b ∣ c ) (a ∣ b) ∣ c = a ∣ (b ∣ c) (ab)c=a(bc)

    ( a ⊕ b ) ⊕ c = a ⊕ ( b ⊕ c ) (a ⊕ b) ⊕ c = a ⊕ (b ⊕ c) (ab)c=a(bc)

  • 分配律: ( a & b ) ∣ c = ( a ∣ c ) & ( b ∣ c ) (a \& b) ∣ c = (a ∣ c) \& (b ∣ c) (a&b)c=(ac)&(bc)

    ( a ∣ b ) & c = ( a & c ) ∣ ( b & c ) (a ∣ b) \& c = (a \& c) ∣ (b \& c) (ab)&c=(a&c)(b&c)

    ( a ⊕ b ) & c = ( a & c ) ⊕ ( b & c ) (a ⊕ b) \& c = (a \& c) ⊕ (b \& c) (ab)&c=(a&c)(b&c)

  • 德摩根律: ∼ ( a & b ) = ( ∼ a ) ∣ ( ∼ b ) , ∼ ( a ∣ b ) = ( ∼ a ) & ( ∼ b ) ∼(a \& b) = (∼a) ∣ (∼b),∼(a ∣ b) = (∼a) \& (∼b) (a&b)=(a)(b)(ab)=(a)&(b)

  • 取反运算性质: − 1 = ∼ 0 , − a = ∼ ( a − 1 ) −1 = ∼0,−a = ∼(a−1) 1=∼0a=∼(a1)

  • 与运算性质: a & 0 = 0 a \& 0 = 0 a&0=0 a & ( − 1 ) = a a \& (−1) = a a&(1)=a a & ( ∼ a ) = 0 a \& (∼a) = 0 a&(a)=0

  • 或运算性质: a ∣ 0 = a a ∣ 0 = a a0=a

  • 异或运算性质: a ⊕ 0 = a a ⊕ 0 = a a0=a a ⊕ a = 0 a ⊕ a=0 aa=0

如何获取、设置和更新某个为位的数据,也有固定的套路。

  1. 获取

    将1左移i位,得到形如0001 0000的值。接着对这个值与num执行&操作,从而将i位之外的所有位清零,最后检查结果是否为0。不为0说明i位为1,否则i位为0。

    function getBit(num, i) {
        return ((num & (1<<i)) !== 0);
    }
    
  2. 设置(将某一位设置为1)

    先将1左移i位,得到形如0001 0000的值,接着对这个值和num执行|操作,这样只会改变i位的数据。这样做则会使除i位外的位均为0,故不会影响num的其余位。

    function setBit(num, i) {
        return (num | (1<<i));
    }
    
  3. 清零(将某一位设置为0)

    该方法与setBit相反,首先将1左移i位,得到形如0001 0000的值,接着对这个值取反进而得到类似1110 1111的值,接着对该值执行&操作,故而不会影响到num的其余位,只会清零i位。

    function clearBit(num, i) {
        let mask = ~(1<<i)
        return num & mask;
    }
    
  4. 更新

    该方法将setBitclearBit结合,首先用如1110 1111的值将num的第i位清零。接着将待写入值v左移i位,得到一个i位为v但其余位都为0的数。最后对之前的结果执行| 操作,v为1则numi位更新为1,否则为0:

    function updateBit(num, i, v) {
        let mask = ~(1<<i);
        return (num & mask) | (v<<i);
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值