三次异或交换数字

三次异或交换数字

\(\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;
}

三种证明方式:

  1. 将所有可能写出来

\[\begin{array}{|c|c|c|} \hline \wedge & 0 & 1 \\\hline 0 & 0 & 1 \\\hline 1 & 1 & 0 \\\hline \end{array} \]

只考虑一位的情况,可以看出三次异或后,位交换了。而整数也是多个位组成的,自然可以交换。

  1. 从正常交换的过程思考

    正常交换数值时,需要一个值来保持中间变量。虽然没有直接声明一个变量来保持,但其实y就已经作为中间变量了。我将作为中间变量的y写作y'

    第一次异或:y'保存了xy不同的位(异或就代表着不同);
    第二次异或:一个位异或0值不变,异或1则取反。此时y'中保存着原来的y不同于x的位,所以y'x中不同于y的位都取反了,x也就变成了y
    第三次异或:x现在是y了,成功交换了。那么将y中不同于x的位取反(仍然通过y'),y'也就变成了x

  2. 列表写逻辑值

    *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;
}

使用加减的交换过程比较好验证,也有其他更多写法,总之是三步完成交换。不过使用加减会出现溢出问题。

posted @ 2022-10-27 11:50  Violeshnv  阅读(60)  评论(0编辑  收藏  举报