前言
文章图片取自其他资料,侵权私信删除
什么是异或运算
异或运算的规则可以定义为:如果两个值不相同,则结果为真(1);如果两个值相同,则结果为假(0)
网友说的
加解密
密钥交换
密钥交换就是通信双方需要将加密密钥发送给对方,但不让中间的信道监听者知道密钥是什么,而利用XOR就可以实现一种最简单的密钥交换方案:
- 客户端生成一个随机密钥p,然后再使用自己的密钥k1对其XOR加密,将加密后的s1发送给服务端,即s1 = p ^ k1。
- 服务端再使用自己的密钥k2对s1做XOR加密,将加密后的s2回复给客户端,即s2 = s1 ^ k2。
- 客户端再使用自己的密钥k1对s2做XOR解密,将解密后的s3回复给服务端,即s3 = s2 ^ k1。
- 服务端再使用自己的密钥k2对s3做XOR解密,即s4 = s3 ^ k2,而按XOR的性质,s4会等于p,即客户端顺利将p发送给了服务端,且中间通信数据都是加密的。
s4 = s3 ^ k2
= (s2 ^ k1) ^ k2
= ((s1 ^ k2) ^ k1) ^ k2
= (((p ^ k1) ^ k2) ^ k1) ^ k2
= p ^ k1 ^ k2 ^ k1 ^ k2 // 应用结合律
= p ^ (k1 ^ k2) ^ (k1 ^ k2) // 再应用结合律,把k1 ^ k2看成整体,就是加密之后再解密了
= p
//也可以这样证明
= p ^ k1 ^ k2 ^ k1 ^ k2
= p ^ (k1 ^ k1) ^ (k2 ^ k2) // 应用交换律
= p ^ 0 ^ 0 // 应用自身与自身异或为0
= p // 应用任何值与0异或为其自身
数据备份
使用XOR也可以很容易实现多个数据的互备,如下:
假如有数据a、b、c,则z = a ^ b ^ c,然后把数据z备份下来。
- 当a丢失时,可使用z ^ b ^ c来恢复。
- 当b丢失时,可使用z ^ a ^ c来恢复。
- 当c丢失时,可使用z ^ a ^ b来恢复。
磁盘阵列技术raid5(冗余独立磁盘阵列)的数据备份原理,就是用的这个特性
应用场景
chatgpt说的,待验证
- 错误检测和纠错:在数据传输或存储过程中,可能会发生错误。通过对数据进行异或运算,并将结果与预期的结果进行比较,可以检测到数据是否发生了变化。例如,在通信协议中,可以使用异或运算来校验数据的完整性。
- 图像处理:异或运算可以用于图像的掩码操作。通过将图像与掩码进行异或运算,可以实现图像的特定区域的显示或隐藏。这种技术常用于图像的叠加、透明效果等。
- 计算机逻辑:在计算机的逻辑设计中,异或运算常用于实现一些逻辑功能。例如,异或门可以用于构建数字电路,实现逻辑与、或、非等操作。
- 数据压缩:异或运算可以用于数据的压缩。通过对相邻的数据进行异或运算,并只存储运算结果和必要的差异信息,可以减少数据的存储空间。
- 哈希函数:某些哈希函数的实现中可能会用到异或运算。通过对输入数据进行异或运算,可以生成哈希值,用于数据的快速查找、比较或验证。
- 算法设计:异或运算在一些算法中也有特殊的应用。例如,在一些排序算法中,可以利用异或运算来交换元素的位置,提高算法的效率。
错误检测和纠错
在数据传输或存储过程中,通常会使用校验和(checksum)来检测数据是否发生了变化。校验和的一种简单实现方式就是对所有的数据进行异或运算,然后将结果附加到数据的末尾。接收方在接收到数据后,可以通过对数据和校验和进行同样的异或运算,然后检查结果是否为0,来判断数据是否发生了变化。
图像处理
异或运算的作用就是帮我们从两堆相似的信息中,发现一些不同的信息。
图像反色处理:将每个像素的颜色值与255进行异或运算,从而实现颜色的反转。
天文学家可以用异或运算来发现新的天体
通过同一块区域不同时间的拍摄图像,异或可以找到差异
图形学中的橡皮筋技术
在计算机图形学中,异或运算常常被用作"橡皮筋"技术的一部分,用于实现动态绘制和擦除图形。
异或运算在这里的用途主要是因为它的两个关键性质:1) 一个值与其自身进行异或运算会得到0 ;2) 一个值与0进行异或运算会得到其自身。这意味着,如果你将一个像素与一个特定的颜色(通常是白色)进行异或运算两次,那么这个像素的颜色会恢复到原来的颜色。
当你在屏幕上动态绘制一个图形(例如,一个线段或一个矩形)时,你可以首先使用异或运算将图形绘制到屏幕上。然后,当你需要移动或改变这个图形时,你可以再次使用异或运算,将原来的图形从屏幕上擦除(因为两次异或运算会恢复原来的颜色)。然后,你可以使用异或运算将新的图形绘制到屏幕上。这样,你就可以实现图形的动态绘制和擦除,而不需要重新绘制整个屏幕。
数据压缩
异或运算可以用于一种简单的数据压缩技术,称为差分编码。差分编码是一种无损压缩技术,它通过存储数据与其前一个值的差异(而不是数据本身)来工作。这对于数据中存在大量连续重复值的情况非常有效,例如在某些类型的图像或音频文件中。
// 压缩函数
function compress(data) {
let last = 0;
return data.map(x => {
let result = x ^ last;
last = x;
return result;
});
}
// 解压缩函数
function decompress(data) {
let last = 0;
return data.map(x => {
let result = x ^ last;
last = result;
return result;
});
}
// 示例数据
let data = [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3];
console.log('原始数据:', data);
// 压缩数据
let compressed = compress(data);
console.log('压缩后的数据:', compressed);
// 解压缩数据
let decompressed = decompress(compressed);
console.log('解压缩后的数据:', decompressed);
差分编码的主要优势在于它可以改变数据的分布,使得数据更适合进一步的压缩。例如,如果原始数据中有很多连续的重复值,那么差分编码后的数据将有很多的零。这种数据可以通过其他压缩技术(如游程编码或哈夫曼编码)来进行进一步的压缩。
游程编码(Run-Length Encoding,简称RLE)是一种非常简单的无损压缩技术。它主要用于处理连续重复的数据。在游程编码中,连续重复的数据被替换为两个字节,一个表示重复的数据,另一个表示这个数据重复的次数。例如,考虑一个字符串 "AAAABBBCCD"。使用游程编码,这个字符串可以被压缩为 "4A3B2C1D"。
这种压缩方法在处理有大量连续重复数据的情况时非常有效。例如,它经常被用于图像压缩,因为在许多图像中,相邻的像素通常有相同或相似的颜色。
易错点
异或运算最典型的一个应用,是做两个数字的交换
XOR 链表 – 内存高效的双向链表
在通常的双向链表中,每一个节点需要有两个指针,一个 prev,指向之前的节点;一个 next,指向之后的节点。
但是,异或双向链表中,只有一个指针,我们可以管它叫 xor_ptr。这个指针指向的地址,是 prev 和 next 两个地址异或的结果。其中,头结点的 prev 地址取 0;尾结点的 next 地址取 0。
这样一来,如果我们需要获得一个节点的 next 的地址,只需要 xor_ptr ^ prev 就好;
如果我们需要获得一个节点的 prev 的地址,只需要 xor_ptr ^ next 就好。
我们之所以可以这么做,是因为对于双向链表,所有的查询操作,肯定是从头到尾,或者从尾到头进行的,而不可能直接从中间进行。也就是所谓的链表不支持随机访问。
因此,在我们遍历异或双向链表的过程中,如果我们是从头到尾遍历的话,我们就可以一直跟踪每一个节点的 prev 值。用这个值和 xor_ptr 做异或操作,拿到每一个节点的 next;
同理,如果我们是从尾到头遍历的话,我们就可以一直跟踪每一个节点的 next 值。用这个值和 xor_ptr 做异或操作,就可以拿到每一个节点的 prev 。
实现异或
a ^ b = (~a & b) | (a & ~b)
- (~a & b):这部分会找出所有 a 中为 0,b 中为 1 的位。这些位在异或运算中的结果应该是 1。
- (a & ~b):这部分会找出所有 a 中为 1,b 中为 0 的位。这些位在异或运算中的结果也应该是 1。
然后,我们使用或操作(|)将这两部分的结果合并起来,就得到了异或运算的结果。