怎样理解异或

一、异或

异或是离散数学中逻辑运算的一种二元计算,符号为⊕,当符号前后的两者分别为F和T时,结果才为T,若两者都是F,或两者都是T,则运算结果为F。即,

V1 V2 结果
F F F
F T T
T F T
T T F

两个数字也可以做异或操作,首先需要把两个数字转换为二进制,并把每一位的数字两两做如下比较:

V1 V2 结果
0 0 0
0 1 1
1 0 1
1 1 0

例如 3 ⊕ 5:
3的二进制为011,5的二进制为101,两者异或的结果,根据上面的表格,是110,也就是6。

二、如何理解异或

我们由前面的内容可以得知,异或真正的目的是找不同,即相同的数字作异或为T,不同的数字做异或为F。当异或对二进制数字使用的时候,它表达的是数字每位是否相同;当对十进制或其他进制的数字进行异或计算时,得出的结果代表两个数字相不相同。

三、异或的应用

在了解异或的数学背景后,我们来看一下在编程中异或的应用。以Java为例,在Java中,异或操作符为^

1) 数值型变量值交换
int a = 3;
int b = 5;
// 交换a和b

最基础的写法是

int c = a;
a = b;
b = c;

应用异或我们可以写成:

a = a ^ b;
b = a ^ b;
a = a ^ b;

我们来分析一下。a的二进制为011, b的二进制为101。

行数 a b
1 110 101
2 110 011
3 101 011

至此,a现在是101,b是011,两者完成交换。既然我们要交换a和b,那么我们只需要交换它们不相同的位置上的数字即可。第一次a与b作异或,找到的是a和b位数不同的地方,并把新的值赋给了a,第二次作异或,a现在的结果是a和b哪些位数相同(0)与哪些位数不同(1)。让a与b做异或,b的每一位如果遇到一样的(0),如果这一位是1,就会是1,如果这一位原本是0,就还是0;如果遇到不一样的(1),b的这一位如果是1,就会变成0;如果是0,就会变成1,从而完成了把一样的留存下来,把不一样的去相反的操作,也就是把a的初始值赋给了b。之后做相同的操作,把b的值赋给了a,从而完成了交换操作。

2) Leetcode: 找数组中不重复的数字

假设一个数组中有两两成对的数字,只有一个数字是不成对的,请问是哪一个数字。例如,{1,2,2,3,3},不重复的数字是1。
因为a ^ b = b ^ a,所以异或满足交换律,也就是说,我们对整个数组做异或,可得,1 ^ 2 ^ 2 ^ 3 ^ 3 = 1 ^ (2 ^ 2) ^ (3 ^ 3) = 1 ^ 0 ^ 0 = 1。实现代码如下,

int aim = numarray[0];
for(int i = 1; i < aim.length; i++){
    aim ^= numarray[i];
}
return aim
3) Leetcode: 找数组中重复的数字

假设一个数组中,只有一个数字重复出现了两次,其余的数字都只出现了一次,请问是哪一个数字。例如,{1,2,2,3},重复出现的数字为2。
设1 ^ 2 ^ … ^ k ^ … ^ 1000 = A, 则1 ^ 2 ^ … ^ k ^ k ^ … ^ 1000 = 1 ^ 2 ^ … ^ k ^ … ^ 1000 ^ k = A ^ k,A ^ A ^ k = 0 ^ k = k。也就是说,我们先把1-1000做异或(记为A),再把整个数组中的数字依次做异或(记为B),最后再将A与B做异或,即可找出我们要找的数字。实现代码如下:

int A = 1;
for(int i = 2;i<=1000;i++){
    A ^= i;
}

int B = numarray[0];
for(int i = 1; i < B.length; i++){
    B ^= numarray[i];
}

return A ^ B;

四、参考资料

[1]. 知乎 - 张英锋:如何理解异或的含义
[2]. Java中的异或运算符^
[3]. Java中异或运算的应用

你可能感兴趣的:(算法)