您的位置:首页 > 其它

神奇的异或

2010-01-18 02:03 162 查看
很久很久以前,看到过那么一道题,不用心的变量,交换两个int。

做法是:

a=a^b;

b=a^b;

a=a^b;

从那以后我就对异或有了好感,总觉得异或特别牛逼。

再见到异或就是学组成原理的时候讲加法器了。

二进制的加法,每一位0+0=1,0+1=1,1+0=1,1+1=0,其实也就是异或。

由此发现,原来那个交换数的问题,如果不考虑溢出的情况的话,其实可以想成:

a=a-b;

b=b+a;

a=b-a;

其实就是先求出a和b的差,存放在a里面,由于知道b,再知道a和b的差,a是可以算出来的,所以没有丢失任何信息。然后b=b+a就是在b上加一个差,把b就变成了a,然后现在有a了,再减去一个差,就又得到了b。不需要其他变量。这就比异或容易理解多了。

但其实加减法,在二进制的层面,就是异或。

对比第二个做法,我不禁会想,为什么第一种做法看上去那么牛逼呢,三个式子居然都是一样的,第二个为什么就不一样呢。

因为加减法是有方向的,而异或没有。

异或的本质是什么?如果两个位的值一样,就是0,不一样,就是1。嗯,其实异或就是表示的差异。a=a^b之后,a里存放的是什么?就是原来的a和b之间的差异。这和a=a-b有异曲同工之妙,不同的是减法不满足交换律,a=a-b和a=b-a是不一样的,而异或是满足交换律的。也就是说,a=a-b的a,比起a=a^b的a,多了一点东西。你可以说是符号,也可以说是方向。a-b表示的是b到a的差异,而不是a到b的差异。

然而,这个方向是多余的。因为加入我知道b,我必然是要求a,必然是要用b到a的差异,反之亦然。也就是说,这个方向,其实已经暗含在另外的这一个变量里面。

而异或,表示的是纯粹的差异,没有方向。第一句a=a^b,把差异存放在a里,第二句b=a^b,求的是b和差异的差异,自然是a。第三句a=a^b,就是求原来的a和差异的差异,也就是b了。

由此我想到功夫熊猫里面乌龟对浣熊说的一句话:There is just news. There is no good or bad. 差异就是差异,没有方向的。

后来我想到了一个做undo/redo的机制。

要做undo/redo,必然少不了要把历史记录下来,记录什么呢,有的说记数据,有的说记操作。然而现在我发现这两者没有实质的区别。你可以说本质上就是记录差异。

大学的时候做过一个window绘图板程序,要求支持undo/redo操作。记数据是最简单的,但是开销太大,要支持20次undo的话就要花费20倍的内存。所以我用的是记录操作,每种操作都会定义一种逆操作。undo一次就调用逆操作。这就好像上面的第二种方法,加法和减法就互为逆操作。换句话说,操作是有方向的。

但是,上面不是得出结论,方向是多余的吗。确实是多余的。因为其实我们只需要记录差异。

如果把数据存放在一块连续的内存里,利用异或,就很容易得到一次操作前后数据的差异。这个差异,虽然也是和数据一样大的一块内存,但是如果一个操作只改变内存的特定的某一小块的话,这个差异将有很大一块都是0。这意味着这个差异很容易进行压缩。假如我们的数据的结构比较好,分为一块一块的,那么有可能我可以知道一个操作只会改变哪一块内存。那么在求差异的时候只需要把操作前后的这一块内存拿来异或就可以了。可以说每个操作就对应了一个差异,而操作和差异都是不带方向的。然后我们把这些差异保存起来,维持一个序列,那其实undo和redo都是在当前的数据上异或要undo或者redo的这个操作所对应的差异。undo和redo,在这种机制下被统一起来了。因为我们不需要方向,方向的信息其实已经保存在当前的数据里。如果当前数据是操作前的,那我必然是要求操作后的,反之亦然。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: