在逻辑学中,逻辑算符异或(exclusive or)是对两个运算元的一种逻辑析取类型,符号为 ^ 或 XOR 或 ⊕(编程语言中常用^)。但与一般的逻辑或不同,异或算符的值为真仅当两个运算元中恰有一个的值为真,而另外一个的值为非真。转化为命题,就是:“两者的值不同。”或“有且仅有一个为真。”
定义:
1 ⊕ 1 = 0
0 ⊕ 0 = 0
1 ⊕ 0 = 1
0 ⊕ 1 = 1
真值表:
Y
B = 0
B = 1
A = 0
0
1
A = 1
1
0
表达式:
Y = A’ & B | A & B’
(注意:以下的否全部使用符号')
根据定义我们很容易获得异或两个特性:
恒等律:X ⊕ 0 = X 归零律:X ⊕ X = 0
然后我们使用真值表可以证明:
(1)交换律
A ⊕ B = A' & B | A & B'
B ⊕ A = B' & A | B & A'
因为&与和|或两个操作满足交换律,所以:
A ⊕ B = B ⊕ A
(2)结合律
(A ⊕ B) ⊕ C
= (A' & B | A & B') ⊕ C
= (A' & B | A & B')' & C | (A' & B | A & B') & C '
= ((A' & B)' & (A & B')')& C | A' & B & C ' | A & B' & C '
= ((A | B') & (A' | B))& C | A' & B & C ' | A & B' & C '
= (A & B | 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 | A' & B & C ' | A & B' & C '
证明过程中使用了如下几个方法:
&与 |或的交换律、分配律和结合律:
A & B = B & A
A | B = B | A
(A & B) & C = A & (B & C)
(A | B) | C = A | (B | C)
A & (B | C)= A & B | A & C
A | B & C = (A | B) & (A | C)
摩尔定理:
(A & B)' = A' | B'
(A | B)' = A' & B'
结论:
交换律:A ⊕ B = B ⊕ A
结合律:A ⊕ (B ⊕ C) = (A ⊕ B) ⊕ C
有了归零率和结合律,我们就可以轻松证明:
自反:A ⊕ B ⊕ B = A ⊕ 0 = A
可能这些特性会很顺其自然的理解,但是如果你在解决问题的时候,你可能会忘记异或的这些特性,所以适当的应用可以让我们加深对异或的理解;
A ⊕ 1 = A'
A ⊕ 0 = A
A ⊕ A = 0
A ⊕ A' = 1
(说明:以下的的异或全部使用符号^)
可能你已经被乱七八糟的公式和演算搞的有点烦了,不就是很简单的异或运算吗?还解释的那么复杂,不要着急,打好了基础,你就站在了巨人的肩膀。
(1)汇编语言将寄存器置零
XOR EAX,EAX
(2)异或翻转二进制
0 ^ 1 = 1
1 ^ 1 = 0
翻转10100001的第6位可以将该数与00100000进行按位异或运算;10100001 ^ 00100000 = 10000001
(3)异或判断二进制数中1的数量是奇数还是偶数
求10100001中1的数量是奇数还是偶数,第一个想到的方法当然是判断最后一位是否为1,让后右移一位,直至变量为0为止。最后判断是奇数还是偶数。
如果用异或,那把每一位异或,就是:
1 ^ 0 ^ 1 ^ 0 ^ 0 ^ 0 ^ 0 ^ 1 = 1
结果为1就是奇数个1,结果为0就是偶数个1。
这条性质可用于奇偶校验(Parity Check),比如在串口通信过程中,每个字节的数据都计算一个校验位,数据和校验位一起发送出去,这样接收方可以根据校验位粗略地判断接收到的数据是否有误,但如果校验位也错了,那可是一个大问题:)。
(4)RAID5
校验和恢复主要利用的了异或的特性:if a ^ b = c then a ^ c = b。一个很好的应用实例是RAID5,使用3块磁盘(A、B、C)组成RAID5阵列,当用户写数据时,将数据分成两部分,分别写到磁盘A和磁盘B,A ^ B的结果写到磁盘C;当读取A的数据时,通过B ^ C可以对A的数据做校验,当A盘出错时,通过B ^ C也可以恢复A盘的数据。这就是RAID5的基本原理。
但RAID5的实现比上述的描述复杂多了,有兴趣的可以上网查一下。
(5)经典题目:不使用其他空间,交换两个值
a = a ^ b;
b = a ^ b; //a ^ b ^ b = a ^ 0 = a;
a = a ^ b;