在数字逻辑中,逻辑算符异或(exclusive or
)是对两个运算元的一种逻辑分析类型,符号为 XOR 或 ⊕(编程语言中经常使用 ^
)。但与通常的逻辑或不一样,异或算符的值为真仅当两个运算元中恰有一个的值为真,而另一个的值为非真。javascript
名称 | 符号 |
---|---|
数学符号 | ⊕ |
英文简称 | xor |
程序符号 | ^ |
异或运算 p ⊕ q 的真值表以下:java
p | q | ⊕ |
---|---|---|
T | T | F |
T | F | T |
F | T | T |
F | F | F |
不管怎样改变同一行中 p,q 的位置,真值表都是成立的。算法
由上述的真值表,咱们能够总结出如下异或运算的运算规则:编程
1 ⊕ 1 = 0 1 ⊕ 0 = 1 0 ⊕ 1 = 1 0 ⊕ 0 = 0
下面咱们以 8 ^ 6 为例,来实际体验一下异或运算。segmentfault
8 ^ 6 = 14 0000 1000 ^ 0000 0110 ------------ 0000 1110
名称 | 值 | 二进制表达式(8位) |
---|---|---|
p | 15 | 0000 1111 |
q | 8 | 0000 1000 |
r | 6 | 0000 0110 |
p ⊕ q数组
0000 1111 //p=15 ⊕ 0000 1000 //q=8 ------------ 0000 0111
q ⊕ p编程语言
0000 1000 // q=8 ⊕ 0000 1111 // p=15 ------------ 0000 0111
p ⊕ (q ⊕ r)加密
0000 1000 //q=8 ⊕ 0000 0110 //r=6 ------------ 0000 1110 //(q ⊕ r)的结果 ⊕ 0000 1111 //p=15 ------------ 0000 0001 // p ⊕ (q ⊕ r)的结果
(p ⊕ q) ⊕ rspa
0000 1111 //p=15 ⊕ 0000 1000 //q=8 ------------ 0000 0111 //(p ⊕ q)的结果 ⊕ 0000 0110 //r=6 ------------ 0000 0001 // (p ⊕ q) ⊕ r的结果
一个数与 0 进行异或运算等于它自己code
0000 1111 //p=15 ⊕ 0000 0000 ------------ 0000 1111
一个数与它自己进行异或运算等于 0
0000 1111 //p=15 ⊕ 0000 1111 //p=15 ------------ 0000 0000
基于该特性,能够经过 a ⊕ b == 0
来判断两个整数的值是否相等。
p ⊕ q ⊕ q
0000 1111 //p=15 ⊕ 0000 1000 //q=8 ------------ 0000 0111 //p ⊕ q的结果 ⊕ 0000 1000 //q=8 ------------ 0000 1111 // p ⊕ q ⊕ q的结果
给定整数 a,要求翻转 a 对应二进制表达式中的特定位。假设整数 a 的值为 10,其对应二进制表达式为 0000 1010
(以 8 位为例),咱们要求对第 3 位和第 4 位进行翻转,要实现这个需求,能够将 a 与 b(12) 进行按位异或运算。
0000 1010 //a=10 ⊕ 0000 1100 //b=12 ------------ 0000 0110 //a ⊕ b的结果
经过观察以上结果,咱们能够发现第 3 位(0 -> 1)和第 4 位(1 -> 0)都完成了翻转。
给定整数 a 和 b,不用额外变量交换两个整数的值。针对该问题,能够用如下三行代码来交换 a 和 b 的值(a0 与 b0 表示原始值):
a = a ^ b; // ① a = a0 ^ b0,b = b0 b = a ^ b; // ② a = a0 ^ b0,b = a0 ^ b0 ^ b0 = a0 a = a ^ b; // ③ a = a0 ^ b0 ^ a0 = b0,b = a0
下面咱们来分析一下上述代码的执行过程:
b = a0 ^ (b0 ^ b0) = a0 ^ 0 = a0
,即 b = a0;a = b0 ^ (a0 ^ a0) = b0 ^ 0
,即 a = b0。JavaScript Code:
function swap(a, b) { a = a ^ b; b = a ^ b; a = a ^ b; }
给定一个非空整数数组,除了某个元素只出现一次之外,其他每一个元素均出现两次。找出那个只出现了一次的元素。异或运算符知足交换律和结合律,因此假设有一个非空整数数组为:[A C B C B A D]
,把每一项进行异或运算:
A ^ C ^ B ^ C ^ B ^ A ^ D = A ^ A ^ B ^ B ^ C ^ C ^ D = 0 ^ 0 ^ 0 ^ D = 0 ^ D = D
JavaScript Code:
function singleNumber(nums) { let ans = 0; for(const num of nums) { ans ^= num; } return ans; }
给定两个整数 A 和 B,请计算把整数 A 转换为整数 B 所需翻转的位数。下面咱们来举例说明一下:
名称 | 十进制 | 二进制 |
---|---|---|
A | 15 | 0000 1111 |
B | 10 | 0000 1010 |
经过观察上述表格,要把整数 A(15)转换成整数 B(10),须要翻转的位数为 2。这里咱们再来回顾一下异或的运算规则:
1 ⊕ 1 = 0 1 ⊕ 0 = 1 0 ⊕ 1 = 1 0 ⊕ 0 = 0
而后咱们对整数 A 和整数 B 执行异或运算:
0000 1111 ⊕ 0000 1010 ------------ 0000 0101
这时咱们能够知道,若是整数 A 和整数 B 对应位的值不一致的话,当前位的异或结果就为 1,在转换过程当中就须要进行翻转。而要计算整数 A 转换为整数 B 所需翻转的位数,就能够转换为计算 A ⊕ B 运算结果二进制数中 1 的个数。
JavaScript Code:
function bitflip(a, b){ let count = 0; let c = a ^ b; while(c != 0){ c = c & (c - 1); count++; } return count; }
给定一个二进制数如 0110 1100
,求该二进制数中 1 的数量是奇数仍是偶数。利用异或运算 p ⊕ 0 = p 恒等律的特性,上述问题能够这样解答:0 ^ 1 ^ 1 ^ 0 ^ 1 ^ 1 ^ 0 ^ 0 = 0
。若二进制数中每 1 位执行异或运算的结果为 1,则 1 的数量是奇数,而结果为 0,则 1 的数量是偶数。
该功能的实际应用场景是奇偶校验,好比在串口通讯中,每一个字节的数据都计算一个校验位,数据和校验位一块儿发送出去,这样接收方能够根据校验位判断接收到的数据是否有误。
现代的密码都是创建在计算机的基础上,这是由于现代的密码所处理的数据量很是大,并且密码算法也很是复杂,不借助计算机的力量就没法完成加密和解密的操做。
计算机的操做对象并非文字,而是由 0 和 1 排列而成的比特序列。不管是文字、图片、声音、视频仍是程序,在计算机中都是用比特序列来表示的。执行加密操做的程序,就是将表示明文的比特序列转换为表示密文的比特序列。
这里咱们来看一个比特序列异或运算的示例:
0 1 0 0 1 1 0 0 // A ⊕ 1 0 1 0 1 0 1 0 // B -------------------- 1 1 1 0 0 1 1 0 //(A ⊕ B)的结果 ⊕ 1 0 1 0 1 0 1 0 // B -------------------- 0 1 0 0 1 1 0 0 // A
可能你已经发现了,上面的计算过程和加密、解密的步骤很是类似。
实际上,只要选择一个合适的 B,仅仅使用 XOR 就能够实现一个高强度的密码。
本人的全栈修仙之路订阅号,会按期分享 Angular、TypeScript、Node.js/Java 、Spring 相关文章,欢迎感兴趣的小伙伴订阅哈!