美文网首页
2020-11-20 逻辑运算(异或为什么可以交换值)

2020-11-20 逻辑运算(异或为什么可以交换值)

作者: 宇宙区长李小无 | 来源:发表于2020-11-20 18:03 被阅读0次

    前言

    在写冒泡排序的时候,同事看了我的代码,跟我说,你这个数据交换,不需要中转站的,用一个异或操作就可以啦。具体代码是:

    a = 2;
    b = 3;
    // 数据交换(未使用异或)
    temp = a;
    a = b;
    b = temp;
    // 数据交换(使用异或)
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
    // a = 3, b = 2;
    

    怎么样,乍一看是不是有点惊奇,别着急,让我们一探究竟。

    逻辑门

    • 与门:全为1才出1
    • 或门:有1就出1
    • 非门:全为0才出1
    • 同或门:相同就出1
    • 异或门:不同就出1
    • 与非门:先与再非(全为1就出0)
    • 或非门:先或再非(有1就出0)

    这八种逻辑门在数字电路中非常重要,但是我们程序员只需要了解即可。

    逻辑运算

    • 逻辑与(&&):
      运算符左侧为真值则继续往后运算,运算符左侧为假值,则返回运算符左侧值。
      1 && 2 && 3 // 3
      1 && 0 && 2 // 0
      0 && 2  // 0
      
    • 与运算(&):
      将运算符左右值转化为二进制数进行逻辑门运算,true转为1,false转为0,true与奇数得1,false与任何数都得0
      3 & 2 // 3
      true & 1 // 1
      true & 2 // 0
      
    • 逻辑或(||):
      向右查找到第一个真值,并返回。
      0 || 3 || 4 // 3
      1 || 2 || 3 // 1
      
    • 或运算(|):
      和与运算相同,先转化为二进制再进行逻辑门运算。
      0 | 1 // 1
      3 | 4 // 7
      
    • 非运算(!):
      非运算就是取值的“反”,真的变成假的,假的变成真的,只输出true/false,可叠加运算
      !1 // false
      !0 // true
      !!!1 // false
      
    • 异或运算(^):
      将值转化为二进制,进行异或门运算
      2 ^ 3 // 1
      1 ^ 3 // 2
      1 ^ 2 // 3
      
      发现规律了吗?通过异或,我们可以利用值本身,将两个值进行交换。
    为什么是异或

    二进制数只有两个值,不是0就是1,那么重点来了:
    两个数不同就表明他们二进制表示中有一位或者多位不同,如10和12:

    10的二进制:1010
    12的二进制:1100
    

    一对比,发现这两个数的中间两位数是不同的。那异或运算特点是什么?不同就出1。那说明两个数进行异或以后,他们不同的位置的数就会变成1。

    10 ^ 12 // 0110
    

    很明显,中间两位是1,代表10和12的二进制表示,中间两位数是不同的。与上面我们推断出的规律是一样的。

    交换位置

    两个不同的数想要交换,而我们已经知道了两个数的二进制表示中,哪些位的值是不同的,所以只需要将不同位的数交换即可。

    第一次异或
    a = 10; // 1010
    b = 12;  // 1100
    // 第一次
    a = a ^ b;  // 0110
    

    此时a的值为0110,b还是原来的值12(1100)。a是代表了原来a和b的差异值(中间两位不同);

    第二次异或
    b = a(差异值) ^ b(初始b);  // 1010
    

    此时b的值是差异值a和原来b值异或的结果。

    注意观察,异或是找不同,可以得出:
    已知左边一位数为0,那右边对应位置的数一定是其本身(0代表相同,出0,1代表不同,出1)
    已知左边一位数为1,那右边对应位置的数一定取反(0代表不同,出1,1代表相同,出0)

    那说明此时b是原来的b值将二进制表示的中间两位进行取反的结果。还记得第一次异或得出了什么结论吗?a与b再二进制表示中 中间两位 不同
    既然b已经进行了差异位的取反,那不就变成了原来a的值了吗?--仔细想想

    第三次异或
    a = a(差异值) ^ b(初始a);  // 1010将中间两位取反,得出1100
    

    第二次异或结束后,差异值0110,中间两位为1,边上两位为0,利用上面的结论,在第三次异或中,得出a的值为b(原来的a)进行差异取反,也就得到了原来的b的值。

    结论

    异或交换的核心思想就是:

    • 第一次异或得到差异值,将其随意赋给一个值a;
    • 第二次异或通过差异取反(差异值a ^ b)可以得到a的原始值,将其赋给b;
    • 第三次异或还是通过差异取反(差异值a ^ b(原始值a))得到原始值b,将其赋给a;

    废话

    不由感叹数学果然让人头秃,誓要成为数学亡子 [滑稽]。

    周五就该下棋,九五至尊不香吗?

    相关文章

      网友评论

          本文标题:2020-11-20 逻辑运算(异或为什么可以交换值)

          本文链接:https://www.haomeiwen.com/subject/xbidiktx.html