异或运算常见的应用

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

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

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

基础知识

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

xyx^y
000
011
101
110

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

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

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

重要的性质

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

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

x ^  x = 0

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

x  ^  0 = x

3、 交换性

x  ^ y =  y ^ x

4、 结合性

x ^ ( y ^ z ) =  ( x ^ y )  ^ z

常见应用

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

  • 判断两个数是否相等

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

( a ^ b ) == 0

ab相等时,表达式为真,否则为假

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

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

tmp = a
a = b
b = tmp;

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

a  =  a  ^  b
b  =  a  ^  b
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,它们的值实现了交换

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

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

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

a  ^  b  ^  c  ^  a  ^  b                   # 根据 x ^ y  = y ^ x

=  ( a  ^  a )  ^  ( b  ^  b )  ^  c        # 根据 x  ^ x = 0

=  0  ^  0  ^  c                            # 根据 x ^ 0 = x

= c
  • 加密

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

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

plain_text  ^  encrypt_key = cipher_text

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

cipher_text ^ encrypt_key 
= ( plain_text  ^  encrypt_key ) ^ encrypt_key
= plain_text  ^ ( encrypt_key ^ encrypt_key )   # 根据 x ^ ( y ^ z ) = ( x ^ z ) ^ y
= plain_text  ^  0                              # 根据 x ^ x = 0
= plain_text
  • 备份

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

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

bakfile  =  filea  ^  fileb

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

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

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

fileb  ^  bakfile
=  fileb  ^ ( filea  ^  fileb )
=  fileb  ^  filea  ^  fileb        # 根据 x ^ ( y ^ z ) = ( x ^ z ) ^ y
=  fileb  ^  fileb  ^  filea        # 根据 x ^ x = 0
=  0  ^ filea                       # 根据 x ^ 0 = x
=  filea

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

filea  ^  bakfile
=  filea  ^ ( filea  ^  fileb )
=  filea  ^  filea  ^  fileb        # 根据 x ^ ( y ^ z ) = ( x ^ z ) ^ y 
=  filea  ^  filea  ^  fileb        # 根据 x ^ x = 0
=  0  ^ fileb                       # 根据 x ^ 0 = x
=  fileb

解算法题

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

一个长度为 n-1 的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围 0 ~ n-1 之内,

在范围 0 ~ n-1 内的 n 个数字中有且只有一个数字不在该数组中,请找出这个数字


示例 1:

输入: [ 0,1,3 ]

输出: 2


示例 2:

输入: [ 0,1,2,3,4,5,6,7,9 ]

输出: 8

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

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

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

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

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

#include <stdint.h>
#include <iostream>
using namespace std;

int32_t find_missing(int32_t *ary, int32_t len)
{
    //数组长度小于等于1,直接返回 -1
    if(len <= 1)
    {
        return -1;
    }
    //结果
    int32_t result = 0;
    //result 和 数组中每一个元素值进行异或运算
    for (int32_t i = 0; i < len; i++)
    {
        result ^= ary[i];
    }
    //result 和 0 到 n 中每一个值进行异或运算
    for (int32_t j = 0; j <= len; j++)
    {
        result ^= j;
    }
    return result;
}
//编译: g++ -g -o test  test.cpp
int32_t main(int32_t argc , char *argv[])
{
    int32_t ary[] = { 0, 1, 3 };
    int32_t result = find_missing(ary, sizeof(ary) / sizeof(int32_t));
    std::cout << "result = " << result << std::endl;
    
    return 0;
}

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

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

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

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

小结

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

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值