zoukankan      html  css  js  c++  java
  • 【计算机世界】467- XOR — 神奇的按位运算符

    一、异或运算符

    在数字逻辑中,逻辑算符异或( exclusiveor)是对两个运算元的一种逻辑分析类型,符号为 XOR 或 ⊕(编程语言中常用 ^)。但与一般的逻辑或不同,异或算符的值为真仅当两个运算元中恰有一个的值为真,而另外一个的值为非真。

    1.1 异或运算的表示形式

    名称符号
    数学符号
    英文简称xor
    程序符号^

    1.2 异或运算的真值表

    异或运算 p ⊕ q 的真值表如下:

    pq
    TTF
    TFT
    FTT
    FFF

    无论怎样改变同一行中 p,q 的位置,真值表都是成立的。

    1.3 异或运算规则

    由上述的真值表,我们可以总结出以下异或运算的运算规则:

    1 ⊕ 1 = 0
    1 ⊕ 0 = 1
    0 ⊕ 1 = 1
    0 ⊕ 0 = 0
    

    下面我们以 8 ^ 6 为例,来实际体验一下异或运算。

    8 ^ 6 = 14
      0000 1000
    ^ 0000 0110
    ------------
      0000 1110
    

    二、异或运算符性质

    名称二进制表达式(8位)
    p150000 1111
    q80000 1000
    r60000 0110

    2.1 交换律:p ⊕ q = q ⊕ p

    p ⊕ q

      0000 1111 //p=15
    ⊕ 0000 1000 //q=8
    ------------
      0000 0111
    

    q ⊕ p

      0000 1000 // q=8
    ⊕ 0000 1111 // p=15
    ------------
      0000 0111
    

    2.2 结合律:p ⊕ (q ⊕ r) = (p ⊕ q) ⊕ r

    p ⊕ (q ⊕ r)

      0000 1000 //q=8
    ⊕ 0000 0110 //r=6
    ------------
      0000 1110 //(q ⊕ r)的结果
    ⊕ 0000 1111 //p=15
    ------------
      0000 0001 // p ⊕ (q ⊕ r)的结果
    

    (p ⊕ q) ⊕ r

      0000 1111 //p=15
    ⊕ 0000 1000 //q=8
    ------------
      0000 0111 //(p ⊕ q)的结果
    ⊕ 0000 0110 //r=6
    ------------
      0000 0001 // (p ⊕ q) ⊕ r的结果
    

    2.3 恒等律:p ⊕ 0 = p

    一个数与 0 进行异或运算等于它本身

      0000 1111 //p=15
    ⊕ 0000 0000
    ------------
      0000 1111
    

    2.4 归零律:p ⊕ p = 0

    一个数与它本身进行异或运算等于 0

      0000 1111 //p=15
    ⊕ 0000 1111 //p=15
    ------------
      0000 0000
    

    基于该特性,可以通过 ab==0 来判断两个整数的值是否相等。

    2.5 自反:p ⊕ q ⊕ q = p ⊕ 0 = p

    p ⊕ q ⊕ q

      0000 1111 //p=15
    ⊕ 0000 1000 //q=8
    ------------
      0000 0111 //p ⊕ q的结果
    ⊕ 0000 1000 //q=8
    ------------
      0000 1111 // p ⊕ q ⊕ q的结果
    

    三、异或运算符应用

    3.1 使某些特定的位翻转

    给定整数 a,要求翻转 a 对应二进制表达式中的特定位。假设整数 a 的值为 10,其对应二进制表达式为 00001010(以 8 位为例),我们要求对第 3 位和第 4 位进行翻转,要实现这个需求,可以将 a 与 b(12) 进行按位异或运算。

      0000 1010 //a=10
    ⊕ 0000 1100 //b=12
    ------------
      0000 0110 //a ⊕ b的结果 
    

    通过观察以上结果,我们可以发现第 3 位(0 -> 1)和第 4 位(1 -> 0)都完成了翻转。

    3.2 不用额外变量交换两个整数的值

    给定整数 a 和 b,不用额外变量交换两个整数的值。针对该问题,可以用以下三行代码来交换 a 和 b 的值(a0 与 b0 表示原始值):

    a = a ^ b; // ① a = a0 ^ b0,b = b0
    b = a ^ b; // ② a = a0 ^ b0,b = a0 ^ b0 ^ b0 = a0
    a = a ^ b; // ③ a = a0 ^ b0 ^ a0 = b0,b = a0
    

    下面我们来分析一下上述代码的执行过程:

    • 执行完第一行代码之后,a 的值变成 a0 ^ b0 的值,记为 c,而 b 的值保持不变;

    • 执行完第二行代码之后,a 的值不变仍为 c,而 b 的值为 c ^ b 即 a0 ^ b0 ^ b0 的运算结果,利用前面提到异或运算的特性可以得出 b=a0^(b0^b0)=a0^0=a0,即 b = a0;

    • 执行完第三行代码之后,a 的值为 a0 ^ b0 ^ a0 的运算结果,同样利用异或运算的特性可以得出 a=b0^(a0^a0)=b0^0,即 a = b0。

    JavaScript Code:

    function swap(a, b) {
        a = a ^ b;
        b = a ^ b;
        a = a ^ b;
    }
    

    3.3 只出现一次的数字

    给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。异或运算符满足交换律和结合律,所以假设有一个非空整数数组为: [A C B C B A D],把每一项进行异或运算:

     A ^ C ^ B ^ C ^ B ^ A ^ D
     = A ^ A ^ B ^ B ^ C ^ C ^ D
     = 0 ^ 0 ^ 0 ^ D
     = 0 ^ D
     = D
    

    JavaScript Code:

    function singleNumber(nums) {
        let ans = 0;
        for(const num of nums) {
            ans ^= num;
        }
        return ans;
    }
    

    3.4 确定将整数 A 转换为整数 B 所需翻转的位数

    给定两个整数 A 和 B,请计算把整数 A 转换为整数 B 所需翻转的位数。下面我们来举例说明一下:

    名称十进制二进制
    A150000 1111
    B100000 1010

    通过观察上述表格,要把整数 A(15)转换成整数 B(10),需要翻转的位数为 2。这里我们再来回顾一下异或的运算规则:

    1 ⊕ 1 = 0
    1 ⊕ 0 = 1
    0 ⊕ 1 = 1
    0 ⊕ 0 = 0
    

    然后我们对整数 A 和整数 B 执行异或运算:

      0000 1111
    ⊕ 0000 1010
    ------------
      0000 0101
    

    这时我们可以知道,如果整数 A 和整数 B 对应位的值不一致的话,当前位的异或结果就为 1,在转换过程中就需要进行翻转。而要计算整数 A 转换为整数 B 所需翻转的位数,就可以转换为计算 A ⊕ B 运算结果二进制数中 1 的个数。

    JavaScript Code:

    function bitflip(a, b){
       let count = 0;
       let c = a ^ b;
       while(c != 0){
          c = c & (c - 1);
          count++;  
       }
       return count;
    }
    

    3.5 判断一个二进制数中 1 的数量是奇数还是偶数

    给定一个二进制数如 01101100,求该二进制数中 1 的数量是奇数还是偶数。利用异或运算 p ⊕ 0 = p 恒等律的特性,上述问题可以这样解答: 0^1^1^0^1^1^0^0=0。若二进制数中每 1 位执行异或运算的结果为 1,则 1 的数量是奇数,而结果为 0,则 1 的数量是偶数。

    该功能的实际应用场景是奇偶校验,比如在串口通信中,每个字节的数据都计算一个校验位,数据和校验位一起发送出去,这样接收方可以根据校验位判断接收到的数据是否有误。

    3.6 比特序列加密

    现代的密码都是建立在计算机的基础上,这是因为现代的密码所处理的数据量非常大,而且密码算法也非常复杂,不借助计算机的力量就无法完成加密和解密的操作。

    计算机的操作对象并不是文字,而是由 0 和 1 排列而成的比特序列。无论是文字、图片、声音、视频还是程序,在计算机中都是用比特序列来表示的。执行加密操作的程序,就是将表示明文的比特序列转换为表示密文的比特序列。

    这里我们来看一个比特序列异或运算的示例:

      0 1 0 0 1 1 0 0 // A
    ⊕ 1 0 1 0 1 0 1 0 // B 
    --------------------
      1 1 1 0 0 1 1 0 //(A ⊕ B)的结果
    ⊕ 1 0 1 0 1 0 1 0 // B 
    --------------------
      0 1 0 0 1 1 0 0 // A
    

    可能你已经发现了,上面的计算过程和加密、解密的步骤非常相似。

    • 将明文 A 用密钥 B 进行加密,得到密文 A ⊕ B

    • 将密文 A ⊕ B 的结果异或密钥 B 进行解密,得到明文 A

    实际上,只要选择一个合适的 B,仅仅使用 XOR 就可以实现一个高强度的密码。

    四、参考资源

    • 维基百科 - 逻辑异或

    原创系列推荐

    1. JavaScript 重温系列(22篇全)

    2. ECMAScript 重温系列(10篇全)

    3. JavaScript设计模式 重温系列(9篇全)

    4. 正则 / 框架 / 算法等 重温系列(16篇全)

    5. Webpack4 入门(上)|| Webpack4 入门(下)

    6. MobX 入门(上) ||  MobX 入门(下)

    7. 59篇原创系列汇总

    回复“加群”与大佬们一起交流学习~

    点这,与大家一起分享本文吧~

    个人博客:http://www.pingan8787.com 微信公众号【前端自习课】和千万网友一起,每日清晨,享受一篇前端优秀文章。 目前已连续推送文章 600+ 天,愿每个人的初心都能一直坚持下去!
  • 相关阅读:
    P2762 [网络流24题]太空飞行计划问题(最小割)
    poj2987 Firing[最小割]
    P2051 [AHOI2009]中国象棋[线性DP]
    poj1637 Sightseeing tour[最大流+欧拉回路]
    hdu3739 Anti LIS[最小割]
    P2766 [网络流24题]最长不下降子序列问题
    P2764 [网络流24题]最小路径覆盖问题[最大流]
    P2936(BZOJ3396) [USACO09JAN]全流Total Flow[最大流]
    BZOJ4278 [ONTAK2015]Tasowanie[后缀数组+贪心]
    Robot framework之元素定位实战
  • 原文地址:https://www.cnblogs.com/pingan8787/p/13069534.html
Copyright © 2011-2022 走看看