异或运算常见的应用

开发 前端
大家可能比较熟悉 "与" 运算 和 "或" 运算 ,相对而言, "异或" 运算 平常使用较少,存在感也不强,如果不是刻意提起,可能还想不到它。

[[434244]]

大家可能比较熟悉 "与" 运算 和 "或" 运算 ,相对而言, "异或" 运算 平常使用较少,存在感也不强,如果不是刻意提起,可能还想不到它

其实,"异或" 运算也非常重要,它在加密、备份、算法等方面都有应用,每一位开发的同学都应该花点儿时间掌握它的特点和规律,以便在日常工作中能灵活的运用

接下来将介绍异或运算的一些基础知识以及在实际中的一些应用

基础知识

异或是计算机中一种二元逻辑运算, 运算符号是 ^,它按照二进制位进行异或运算,结果为 真 或 假, 它的运算法则如下

x y x^y
0 0 0
0 1 1
1 0 1
1 1 0

表格 第一列 和 第二列 是异或运算的两个操作数,第三列是异或运算的结果,1 表示真,0 表示假

由表格可知:如果参与运算的两个二进制位相同,则结果为 0 ,否则为 1

也就是说,异或主要用来判断两个值是否相同

重要的性质

下面列出异或运算一些重要的性质,1 表示真,0 表示假, 具体的验证过程比较简单,这里就省略了

1、一个数与自身异或,总是为 0

  1. x ^ x = 0 

2、 一个数与 0 异或,总是其自身

  1. x ^ 0 = x 

3、 交换性

  1. x ^ y = y ^ x 

4、 结合性

  1. x ^ ( y ^ z ) = ( x ^ y ) ^ z 

常见应用

异或运算本身比较简单,重点还是在应用层面,上面列出的性质,在很多方面都有应用

  • 判断两个数是否相等

一个数与自身异或,总是为 0,我们可以使用这一点来判断两个变量是否相等

  1. ( a ^ b ) == 0 

当 a 和 b 相等时,表达式为真,否则为假

  • 不使用临时变量交换两个数的值

要交换两个数的值,通常做法是借助一个临时变量,然后再进行交换,比如:tmp 是临时变量,现需要交换 a 和 b 两个变量值,过程如下

  1. tmp = a 
  2. a = b 
  3. b = tmp; 

利用异或的一些性质,不用临时变量也能实现交换两个数的值,具体过程如下

  1. a = a ^ b 
  2.  
  3. b = a ^ b 
  4.  
  5. a = a ^ b 

假如初始时,a = 1、b = 2

第一个等式 a = a ^ b 结果是 a = 1 ^ 2 = 3

紧接着第二个等式 b = a ^ b 结果是 b = 1 ^ 2 ^ 2 = 1 ^ 0 = 1

最后一个等式 a = a ^ b 结果是 b = 1 ^ 2 ^ 1 = 1 ^ 1 ^ 2 = 0 ^ 2 = 2

可以看到,最终 a = 2、 b = 1 ,它们的值实现了交换

上面的三条语句还可以进一步优化成一条,结果如下

  1. a ^= b ^= a ^= b 
  • 简化表达式

根据交换性,可以优化表达式中重复变量的异或运算,比如:表达式 a ^ b ^ c ^ a ^ b 可以做如下简化

  1. a  ^  b  ^  c  ^  a  ^  b                   # 根据 x ^ y  = y ^ x 
  2.  
  3. =  ( a  ^  a )  ^  ( b  ^  b )  ^  c        # 根据 x  ^ x = 0 
  4.  
  5. =  0  ^  0  ^  c                            # 根据 x ^ 0 = x 
  6.  
  7. = c 
  • 加密

利用异或运算加密是很常见的加密手段,它涉及到三个变量:明文、密钥、密文,假如它们分别记为 plain_text、 encrypt_key、 cipher_text

明文和密钥进行异或运算,可以得到密文

  1. plain_text ^ encrypt_key = cipher_text 

密文和密钥进行异或运算,可以得到明文

  1. cipher_text ^ encrypt_key  
  2. = ( plain_text  ^  encrypt_key ) ^ encrypt_key 
  3. = plain_text  ^ ( encrypt_key ^ encrypt_key )   # 根据 x ^ ( y ^ z ) = ( x ^ z ) ^ y 
  4. = plain_text  ^  0                              # 根据 x ^ x = 0 
  5. = plain_text 
  • 备份

根据异或的性质,异或运算还可以用于文件的备份

现有两个文件 filea 和 fileb,它们进行异或运算,会产生一个新的备份文件 bakfile

  1. bakfile = filea ^ fileb 

根据异或的性质,可以通过 bakfile 和 filea 得到 fileb,或者通过 bakfile 和 fileb 得到 filea

后面无论是 filea 或 fileb 文件损坏了,只要不是两个文件同时损坏,都可以通过两者中未损坏的文件 和 bakfile 文件,还原得到另一个文件

当 filea 文件损坏了,可以通过 fileb 和 bakfile 进行异或运算得到完好的 filea 文件

  1. fileb  ^  bakfile 
  2. =  fileb  ^ ( filea  ^  fileb ) 
  3. =  fileb  ^  filea  ^  fileb        # 根据 x ^ ( y ^ z ) = ( x ^ z ) ^ y 
  4. =  fileb  ^  fileb  ^  filea        # 根据 x ^ x = 0 
  5. =  0  ^ filea                       # 根据 x ^ 0 = x 
  6. =  filea 

同样,当 fileb 文件损坏了,可以通过 filea 和 bakfile 进行异或运算得到完好的 fileb 文件

f

  1. filea  ^  bakfile 
  2. =  filea  ^ ( filea  ^  fileb ) 
  3. =  filea  ^  filea  ^  fileb        # 根据 x ^ ( y ^ z ) = ( x ^ z ) ^ y  
  4. =  filea  ^  filea  ^  fileb        # 根据 x ^ x = 0 
  5. =  0  ^ fileb                       # 根据 x ^ 0 = x 
  6. =  fileb 

解算法题

有些算法可以利用异或的思路进行求解,下面列出力扣上的一道算法题来说明,题目如下:

  1. 一个长度为 n-1 的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围 0 ~ n-1 之内, 
  2.  
  3. 在范围 0 ~ n-1 内的 n 个数字中有且只有一个数字不在该数组中,请找出这个数字 
  4.  
  5.  
  6. 示例 1: 
  7.  
  8. 输入: [ 0,1,3 ] 
  9.  
  10. 输出: 2 
  11.  
  12.  
  13. 示例 2: 
  14.  
  15. 输入: [ 0,1,2,3,4,5,6,7,9 ] 
  16.  
  17. 输出: 8 

最快捷的解决方法是把数组的所有元素以及 0 到 n-1 之间的整数 全部进行异或运算

  1. arry[0] ^ arry[1] ^ arry[2] ... ^ arry[n-2] ^ 0 ^ 1 ^ 2 .... ^ (n-1) 

由于数组元素值范围在 0 到 n-1,且所有元素值都没有重复

所以,上述的计算式子中,0 到 n-1 每个数会出现两次,只有缺少的那个数仅出现一次,根据异或的性质 x ^ x = 0 可知,相同值之间的异或运算等于 0,因此算式的结果其实就是缺少的那个数

下面给出测试文件 test.cpp,代码是用 C++ 实现的,可以自行用其他语言实现

  1. #include <stdint.h> 
  2. #include <iostream> 
  3. using namespace std; 
  4.  
  5. int32_t find_missing(int32_t *ary, int32_t len) 
  6.     //数组长度小于等于1,直接返回 -1 
  7.     if(len <= 1) 
  8.     { 
  9.         return -1; 
  10.     } 
  11.     //结果 
  12.     int32_t result = 0; 
  13.     //result 和 数组中每一个元素值进行异或运算 
  14.     for (int32_t i = 0; i < len; i++) 
  15.     { 
  16.         result ^= ary[i]; 
  17.     } 
  18.     //result 和 0 到 n 中每一个值进行异或运算 
  19.     for (int32_t j = 0; j <= len; j++) 
  20.     { 
  21.         result ^= j; 
  22.     } 
  23.     return result; 
  24. //编译: g++ -g -o test  test.cpp 
  25. int32_t main(int32_t argc , char *argv[]) 
  26.     int32_t ary[] = { 0, 1, 3 }; 
  27.     int32_t result = find_missing(ary, sizeof(ary) / sizeof(int32_t)); 
  28.     std::cout << "result = " << result << std::endl; 
  29.      
  30.     return 0; 

使用 g++ -g -o test test.cpp 命令编译,接着运行程序,结果如下:

  1. [root@localhost test]# ./test  
  2. result = 2 

当然,这道题目还有其他的解法,比如:利用加法来解,大家自己去思考下,这里不做介绍了

题目链接 https://leetcode-cn.com/problems/que-shi-de-shu-zi-lcof/

小结

通过上文,我们可以看到,异或运算在很多方面都有应用,其实,它的应用远不止文中介绍的方面,所以,我们花时间去掌握它是非常值得做的一件事

 

责任编辑:武晓燕 来源: Linux开发那些事儿
相关推荐

2022-05-18 16:06:15

位运算异或运算

2022-05-23 15:02:19

异或运算面试真题

2017-08-29 09:40:26

JavaScript代码神经网络

2021-08-06 10:26:14

蜂窝物联网物联网IOT

2012-10-26 11:37:12

2012-02-02 15:24:57

2010-11-23 15:42:14

2023-02-03 16:16:34

物联网

2022-10-26 14:55:53

AIoT物联网人工智能

2012-02-22 20:46:24

金万维异速联

2018-07-29 15:27:04

AI训练光速运算人工智能

2010-01-04 15:12:27

Silverlight

2012-07-25 15:56:07

2021-02-21 06:36:57

运算技巧按位

2013-05-08 11:01:35

BYODPC应用IT系统

2021-07-26 11:30:21

Wine开发者补丁

2011-07-21 09:12:55

2018-05-28 08:28:24

云计算应用场景

2023-02-15 11:58:29

2019-05-29 18:35:04

Windows 10Office 365应用程序
点赞
收藏

51CTO技术栈公众号