三次异或交换数字
三次异或交换数字
\(\wedge\) 在本文表示异或
void swap(int * restrict x, int * restrict y)
{
if (x == y) return;
*y = *x ^ *y;
*x = *x ^ *y;
*y = *x ^ *y;
// *y ^= *x ^= *y ^= *x;
}
三种证明方式:
- 将所有可能写出来
\[\begin{array}{|c|c|c|} \hline
\wedge & 0 & 1 \\\hline
0 & 0 & 1 \\\hline
1 & 1 & 0 \\\hline
\end{array}
\]
只考虑一位的情况,可以看出三次异或后,位交换了。而整数也是多个位组成的,自然可以交换。
-
从正常交换的过程思考
正常交换数值时,需要一个值来保持中间变量。虽然没有直接声明一个变量来保持,但其实
y
就已经作为中间变量了。我将作为中间变量的y
写作y'
。第一次异或:
y'
保存了x
与y
不同的位(异或就代表着不同);
第二次异或:一个位异或0
值不变,异或1
则取反。此时y'
中保存着原来的y
不同于x
的位,所以y'
将x
中不同于y
的位都取反了,x
也就变成了y
;
第三次异或:x
现在是y
了,成功交换了。那么将y
中不同于x
的位取反(仍然通过y'
),y'
也就变成了x
。 -
列表写逻辑值
取
*x = a, *y = b
\[\begin{align*} a \land a = 0 \\ 0 \wedge a = a \\ 1 \wedge a = \overline{a}\\ \end{align*} \]\[\begin{align*}\begin{array}{|c|c|c|} \hline step & *x & *y \\\hline 0 & a & b \\\hline 1 & a & a \wedge b \\\hline 2 & b & a \wedge b \\\hline 3 & b & a \\\hline \end{array}\end{align*} \]
虽然思路很巧妙,但是异或开销大,比传统的交换慢上很多。
实际上,可以用加减代替异或
void swap(int * restrict x, int * restrict y)
{
if (x == y) return;
*y = *x - *y;
*x -= *y;
*y += *x;
// *y += *x -= *y = *x - *y;
}
使用加减的交换过程比较好验证,也有其他更多写法,总之是三步完成交换。不过使用加减会出现溢出问题。