异或(^)的性质与应用

本文目录

1 基本概念

2 异或应用

3 相关文章


1 基本概念

1.1 符号

异或是一种二进制的位运算,符号以 XOR 或 ^ 表示。

1.2 运算规则

相同为0,不同为1,即

1 ^ 1 = 0

0 ^ 0 = 0

1 ^ 0 = 1

由运算规则可知,任何二进制数与零异或,都会等于其本身,即 A ^ 0 = A。

1.3 异或性质

(1)交换律: A ^ B = B ^ A

(2)结合律: ( A ^ B ) ^ C = A ^ ( B ^ C )

(3)自反性: A ^ B ^ B = A (由结合律可推: A ^ B ^ B = A ^ ( B ^ B ) = A ^ 0 = A)

2 异或应用

2.1 变量交换

示例:将 a 和 b 两个变量值交换,例如: a = 3,b = 7,交换后,a = 7,b = 3。

// 常规方法
int temp = a;  // temp = 3
a = b;         // a = 7
b = temp;      // b = 3

// 异或方法
a = a ^ b;  // a = 3 ^ 7
b = a ^ b;  // b = (3 ^ 7) ^ 7 = 3 ^ (7 ^ 7) = 3
a = a ^ b;  // a = (3 ^ 7) ^ (3 ^ 7 ^ 7) = (3 ^ 3) ^ (7 ^ 7) ^ 7 = 7

2.2 排除偶次重复

示例:在一个整数数组中,仅存在一个不重复的数字,其余数字均出现两次(或偶数次),找出不重复数字。

// 常规方法:通过二次循环找出不重复的数字
for (...) {
    for (...) {
        ...
    }
}

// 异或方法:将所有整数异或,出现偶数次的整数会被抵消,最终留下不重复整数。
int result = 0;
for (int index = 0; index < numArray.length; index++) {
    result = result ^ numArray[index];
}
return result;

2.3 排除偶次重复变种

示例:将数字1-999存放在一个大小为1000的数组中,其中只有一个数字重复出现两次,找出重复数字。

/* 异或方法:该问题为异或自反性的变种题,将数字以二进制形式展示,即可发现其中奥秘。
 *          1:    0    0    0    0    0    0    0    0    0    1
 *          2:    0    0    0    0    0    0    0    0    1    0
 *          3:    0    0    0    0    0    0    0    0    1    1
 *          4:    0    0    0    0    0    0    0    1    0    0
 *          5:    0    0    0    0    0    0    0    1    0    1
 *          6:    0    0    0    0    0    0    0    1    1    0
 *          7:    0    0    0    0    0    0    0    1    1    1
 *          8:    0    0    0    0    0    0    1    0    0    0
 *          9:    0    0    0    0    0    0    1    0    0    1
 *         10:    0    0    0    0    0    0    1    0    1    0
 *         11:    0    0    0    0    0    0    1    0    1    1
 *         12:    0    0    0    0    0    0    1    1    0    0
 *         13:    0    0    0    0    0    0    1    1    0    1
 *         14:    0    0    0    0    0    0    1    1    1    0
 *         15:    0    0    0    0    0    0    1    1    1    1
 *         16:    0    0    0    0    0    1    0    0    0    0
 *        ....
 *        999:    1    1    1    1    1    0    0    1    1    1
 *   PreCount:  511  255  127   63   31   15    7    3    1    0
 * RepeatSpan: 1024  512  256  128   64   32   16    8    4    2
 *  MaxRepeat:    0    1    3    7   15   30   62  124  249  499
 *       Flag:    A    B    C    D    E    F    G    H    I    J
 * 
 * 每一位(列)的前置0个数PreCount:2的n-1次方减1
 * 每一位(列)的完整重复跨度为RepeatSpan:2的n次方
 * 每一位(列)的完整重复次数为MaxRepeat:(999 - PreCount) / Repeat 向下取整
 * 每一位(列)的非完整重复内的数字个数:999 - MaxRepeat * Repeat - PreCount
 *
 * 每一个完整重复内,前一半为1,后一半为0;
 * 末位(J位)完整重复内,异或结果为1;
 * 非末位(J位)完整重复内,均为偶数个1,异或结果为0;
 *
 * 由此可知:计算每一位(列)非完整重复部分的异或结果 与 完整重复部分的异或结果,即可得最终结果。
 * J位:非完整重复数字个数   1,完整重复 499个1, 整体偶数个1,最终结果0;
 * I位:非完整重复数字个数   2,完整重复 249个0, 整体偶数个1,最终结果0;
 * H位:非完整重复数字个数   4,完整重复 124个0, 整体偶数个1,最终结果0;
 * G位:非完整重复数字个数   0,完整重复  62个0, 整体偶数个1,最终结果0;
 * F位:非完整重复数字个数  24,完整重复  30个0, 整体偶数个1,最终结果0;
 * E位:非完整重复数字个数   8,完整重复  15个0, 整体偶数个1,最终结果0;
 * D位:非完整重复数字个数  40,完整重复   7个0, 整体偶数个1,最终结果0;
 * C位:非完整重复数字个数 104,完整重复   3个0, 整体偶数个1,最终结果0;
 * B位:非完整重复数字个数 232,完整重复   1个0, 整体偶数个1,最终结果0;
 * A位:非完整重复数字个数 488,完整重复   0个0, 整体偶数个1,最终结果0;
 * 
 * 即:1-999数字的异或结果为0
 *
 * 因此,将1000个整数依次异或运算,最终结果就是重复的数字,相当于重复数字与0进行异或,得到其本身。
 */
int result = 0;
for (int index = 0; index < numArray.length; index++) {
    result = result ^ numArray[index];
}
return result;

2.4 LeetCode 260:只出现一次的数字Ⅲ

题目:给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。

思路:

(1) 先通过一次异或操作,重复元素会被抵消,最终结果相当于两个单次出现的元素(分别记为one和two)的异或值;

(2) 由异或规则可知,若两个元素one和two的异或值的某二进制位为1,则表示两个元素在该二进制位上的值不同,即分别为1和0,找到其中一个满足条件(为1)的二进制位(记为bitValue);

(3) 根据(2)找到的二进制位bitValue,可以将原数组分成两个部分,one 和 two 分别在两个部分,而相同的重复元素也会被分到同一个部分(因为其相应的二进制位的值是相同的);

(4) 对于两个部分再次进行异或操作,即相当于 排除偶次重复 问题,最终可以得到两个题解。

class Solution {

  public int[] singleNumber(int[] nums) {

    // 通过异或操作,最终结果等于两个单次出现的元素的异或值。
    int filterResult = 0;
    for (int num : nums) {
      filterResult ^= num;
    }

    // 计算首个为1(从右侧开始)的二进制位的值
    int bitValue = filterResult & (filterResult - 1) ^ filterResult;

    // 以首个为1的二进制位将原数组分为两个部分并分别进行异或运算,最终结果为两个题解。
    int oneResult = 0, twoResult = 0;
    for (int num : nums) {
      if ((num & bitValue) > 0) {
        oneResult ^= num;
      } else {
        twoResult ^= num;
      }
    }

    return new int[]{oneResult, twoResult};
  }
}

3 相关文章

《全排列(Java)》

《位运算:减法与补码》

《图解:常用排序算法(冒泡、选择、插入、希尔、快速、归并、堆)》

《回溯算法(试探算法)》

《动态规划:鸡蛋掉落》

《动态规划:单词拆分》

《状态机:只出现一次的数字II》

《最小堆:TopK问题》

《链表:快慢指针》

  • 209
    点赞
  • 834
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
AES(Advanced Encryption Standard)加密是一种对称加密算法,也是目前应用非常广泛的加密算法之一。它采用分组加密方式,将明文分为固定长度的数据块,并通过多轮的加密运算来进行加密。其核心运算包括字节替换、行移位、列混淆和轮密钥加等步骤,通过多轮运算来实现高强度的加密。 相比之下,异或加密是一种简单的位运算。异或运算是指对两个二进制数相同位置的位进行比较,若位相同则结果为0,若位不同则结果为1。异或加密是通过将明文与密钥进行异或运算得到密文的一种加密方式。由于异或运算具有逆运算的性质,所以可以通过再一次对密文与密钥进行异或运算,得到原始的明文。 相比而言,AES加密更加安全可靠。AES加密采用复杂的运算过程和密钥扩展机制,其安全性经过广泛的验证和应用。而异或加密相对简单,容易受到频率分析等攻击手段的威胁。异或加密的安全性依赖于密钥的安全性,对密钥的管理和保护要求较高。 因此,在实际应用中,为了保证数据的安全性,一般更倾向于选择使用AES加密。不过,异或加密在一些特殊情况下也可作为一种简单加密方式来使用,如在一些简单的防护算法、校验算法和简易数据加密等场景中。但需要注意的是,异或加密的安全性相对较低,不适用于对抗一些高级攻击手段的场景。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值