如何用一个加号计算三个数的和

来源:互联网 发布:模拟城市4知乎 编辑:程序博客网 时间:2024/05/16 23:51

计算三个数字加和的方法是 ( a ^ b ^ c ) + ( ( ( a & b ) | ( b & c ) | ( a & c ) ) << 1 ) ...

 但如果你要问这个公式是哪里来的 ... 你就要先弄明白什么是 加法 ...

还没学会如何迈开脚步又怎么能跑起来 ... 三个数字之前 ... 我们先要从两个数字相加开始 ...

首先你要知道计算机里面的数字都是以二进制存储的 ... 你看到的数字 ... 在计算机看来都是 0 和 1 ...

我们随便挑两个数字比如 123 和 321 ... 转化成二进制之后用用竖式把它们加在一起 ...

    '    "'    001111011  = 123 + 101000001  = 321-------------   110111100  = 444

相同位置对齐 ... 从个位数加起 ... 相加的结果满二进一 ... 这个不用我多说了吧 ..?

你可能会注意到 ... 在竖式的上方我有加 ' 和 " 符号 ... 这表示进位 ...

这两个符号的区别是 ' 表示由原来的数字产生的进位 ... " 表示由 ' 产生的进位 ...

不过事实上 ... 进位这个事情本身也是二进制 ... 0 和 1 嘛 ... 所以不妨就用二进制来表示 ...

把所有由原来的数字产生的进位也就是 ' 写成 1 ... 其他填 0 ... 结果像这样 ...

   001111011  ( a   101000001  ( b   010000010  ( c

我们得到了三个数字 ... a 和 b 是原来的数字 ... c 是进位 ...

回想我们刚刚竖式计算的过程 ... 是不是每次都是先计算一位的值 ... 然后再看有没有进位要加 ..?

我们现在用程序模拟这个过程 ... 不考虑进位的时候先把 a 和 b 相加 ...

在只有一位的时候 ... 0 ^ 0 = 0 1 ^ 0 = 1 0 ^ 1 = 1 1 ^ 1 = 0 ... 只有这四种情况 ...

不知道你有没有发现 ... 在这种情况下 ... 我没有使用 + 而是用 ^ 得到了同样正确的结果 ...

^ 就是位运算操作符里面的 按位异或 ... 简单的解释就是两个数字相同就返回 0 不同就返回 1 ...

关于位运算这里就不再赘述了 ...

总之 ... a 和 b 不进位相加之后 ... 我们的式子变成了这样 ...

         '   010000010  ( c + 100111010  ( d-------------   110111100  = 444

其中 d = a ^ b ... 我们就这样成功把 a 和 b 相加的问题转化成了 c 和 d 相加的问题 ...

但是同时又出现了新的问题 ... 怎么得到 c ..?

回想一下 ... c 是怎么来的 ..? 是由进位转化来的 ... 进位又是怎么来的 ..? 满二进一 ...

也就是当 a 和 b 的某一位同时为 1 的时候 ... c 的相应位置的左边一位为 1 ...

换言之 ... 当 a 和 b 的左起第 n + 1 位同时为 1 的时候则 c 的左起第 n 位为 1 ...

用式子来描述就是 c = ( a & b ) << 1 ...

& 是位运算操作符里面的 按位与 ... << 是位运算操作符里面的 左移 ...

如果你认真看了我刚刚说的那个在另外问题里的回答的话 ... 应该不会对这两个操作符有疑问 ...

到现在为止 ... 我们又把 c 和 d 转化回了与 a 和 b 相关的式子 ...

总结一下 ... 不考虑进位的计算结果是 a ^ b ... 进位的部分是 ( a & b ) << 1 ...

所以我们可以得到结论 ... a + b = ( a ^ b ) + ( ( a & b ) << 1 ) ...

绕了一大圈 ... 我们学会了怎么把两个数相加改为另外两个数相加 ...

这个看似没用的式子 ... 恰恰是解决这道题的关键 ...

如果你愿意 ... 可以一直这么写下去 ... 比如把这个式子继续拆分变成下面的样子 ...

(a ^ b) ^ ( ( a & b ) << 1 ) + ( ( a ^ b ) & ( ( a & b ) << 1 ) )

然后继续继续 ... 直到最右边那一项左移若干次之后变成 0 ... 你就实现了完全不用加号的加法 ...

在这个时候 ... 你加上一个新的数字 ... 就实现了一个加号的三个数字相加 ...

当然如果你仍然愿意继续的话 ... 甚至可以实现不用加号的三个数字相加 ...

唯一的问题就是最终的式子会长到一个屏幕都显示不下 ... 各种没法看 ...

所以我们现在回归最初 ... 继续列竖式 ... 看看三个数相加的时候到底发生了什么 ...

我们再随便挑三个数字 ... 比如 111 222 和 333 ...

   ""#""##''   0001101111  = 111   0011011110  = 222 + 0101001101  = 333--------------   1010011010  = 666

和之前一样 ' 表示由原来的数字产生的进位 ... " 表示由 ' 产生的进位 ...

新增的 # 表示原来的数字在这里会产生进位但由于 ' 或 " 的原因发生了左移的进位位置 ...

我们同样把这个进位表示为二进制 ... ' 和 # 记为 1 其他记为 0 ... 如下 ...

   0001101111  ( a   0011011110  ( b   0101001101  ( c   0010011110  ( d

我觉得都到现在了 ... 不用我说你应该也可以看出规律了吧 ...

只要 a b c 三个数字的某一位至少有两个为 1 ... 在 d 的相应位置的左边一位就为 1 ...

用式子来描述就是 d = ( ( a & b ) | ( b & c ) | ( a & c ) ) << 1 ...

| 是位运算操作符里面的 按位或 ... 对比三个数字找到是否有某两个数字的同一位都为 1 ...

进位算出来之后 ... 不考虑进位的计算结果是什么就不用我说了吧 ..?

现在再回去看看最开始说过的那个公式 ... 是不是觉得豁然开朗了呢 ..?

终于写完了 ... 这个回答写了快两个小时 ... 最后写的头都大了 ... 完全不知道自己在说什么了已经 ...

答案里不一定都对 ... 有错误的地方欢迎指正 ... 

教导小朋友果然是体力活 ... 求安慰 ...

以及 ... 最后给你留一道思考题 ... 你要是能做出来的话也不枉我费了这么大力气写了这么多 ...

实现不用乘号的两个数相乘 ... 如果你能做出来就是真明白了 ...

恩恩就是这样啦 ...

转载自:http://nodejs.iteye.com/blog/1855220

原创粉丝点击