up
100
作者 jokin 2017-01-29 12:59:44
写了0文章0获得了0条评论
暂无说说 · 评论
经典的XOR异或交换算法
字数·1686 评论0 喜欢0 转发0

这是常见的面试题:不借助临时变量交换两个整数值a和b。

目录

解法

加减法

a = a + b;  // a存储总和,b保持原值。
b = a - b;  // 总和减去b等于a,覆盖b,b完成交换。
a = a - b;  // 总和减去完成交换的b(值是a)等于b,覆盖a,a完成交换。

条件:a + b的值不溢出。

乘除法

a = a * b;  // 总和
b = a / b;  // 其中一个
a = a / b;  // 另一个

条件:a * b的值不溢出,且b的值不能为0(除数不能为0)。

异或法

a = a ^ b;
b = a ^ b;
a = a ^ b;
// 有部分帖子说,可以写成 a ^= b ^= a ^= b
// 但不建议这样做,因为一连串的写法的执行结果依赖于编译器的实现,另外也不直观。

条件:不能用于浮点型,因为只有整型能够进行位运算

关于这点,有个疑问,浮点数在内存也是二进制,为什么不能进行位运算?原因是,这是人为的限制,浮点数在内存是以科学计数法存储,存储的是符号 + 有效数字 + 次方,对这样的数进行位运算,并不是十分有意义。据说除了没意义外,还有浮点数的位运算效率问题。还有,对浮点数进行位运算,到底是要对数值部分,还是次方部分,还是整个内存进行位运算呢?这部分没有考究,只作记忆。

总结

不借助临时变量交换两个整数值,思路都是下面这样:假设要交换的两个值是a、b。

  1. 将a与b以某种规则生成一个值c,规则是值c能与ab其中一个运算,得到另一个。
  2. 将这个值c存储到a或b的变量空间,这样就有值c和ab其中一个。
  3. 通过值c和ab其中一个运算,得到要交换的值,完成一个交换。
  4. 通过值c和完成交换的一个,得到另一个。

问题

  1. 异或法中,如果交换的变量是自己,经过测试,结果是0?看帖子看到这个结论。

帖子里之所以这样说,是因为帖子里a、b是指针或引用,且指向了同一个空间。这里的交换要求至少有两个空间,一个用于存储原值,一个用于存储规则生成的值。如果只有一个空间,就只能存储规则生成的值,原值被覆盖而丢失,最终无法完成交换。不仅是异或法,加减法和乘除法都会失效。

位运算

需要提醒一下,位运算的结果依赖于操作系统的位域,32位系统与64位系统,对同一个整型进行相同的位运算,结果很可能不一样。这主要是因为有溢出的问题。

异或运算

异或运算是逻辑运算(也叫布尔运算),即结果是

运算法则:

a ^ 0 = a         (0 ^ 0 = 0, 1 ^ 0 = 1)
a ^ 1 = ~a        (0 ^ 1 = 1, 1 ^ 1 = 0)
a ^ a = 0         (0 ^ 0 = 0, 1 ^ 1 = 0)

a ^ b ^ b = a ^ (b ^ b) = a ^ 0 = a
a ^ b = c  则 c ^ b = a, c ^ a = b   (证明:c ^ b = (a ^ b) ^ b = a ^ 0 = a)

数值的位运算需要转换为二进制后按位运算,二进制的位只有0和1,所以上面的定律也适合数值。

异或的应用

交换数值

a ^ b = c 则 c ^ b = a, c ^ a = b 符合上面的交换的规则,所以可以用来交换数值。

加解密

a ^ b ^ b = a,可以将b作为秘钥,对a进行加解密。

常见是对字节进行异或加解密。比如加密:FF ^ 4 = FB,解密FB ^ 4 = FF

但是,不建议使用异或加密,因为太容易被破解。就算不知道加密秘钥,也可以被穷举。比如上面,不需要知道与什么异或,只需要知道FF与FB对应,有FB的地方替换成FF即能解密。曾经见过网吧的网盘、视频网站的视频文件使用了XOR加密,把加密后的文件和原文件比对一下就破解了。相比XOR加密,可以使用稍微强一点的加密算法,比如CR4。

学习笔记,仅供参考