一定要考虑二进制系统中的所有数字。
您通常希望在此类代码中找到“循环不变量”。在这种情况下,您希望看到 a + b 在每次迭代后都保持不变。因此,如果 b 变为 0 并且我们离开循环,则 a 必须等于原始 a 和 b 的总和。下一步是确保循环最终终止。我们稍后会回到这个,首先让我们找出不变部分,在这种情况下使用相等:
a + b = (a ^ b) + 2 * (a & b)
在循环中,新 a 将等于旧 a ^ b,新 b 将是 2 * (a & b),这与 (a & b) << 1 相同。这就是你问题的本质 - 弄清楚这种平等。这正是你做加法的方式。
我将介绍两种解决方案。在两者中,我将使用一个简单的事实:
x + y = x ^ y
每当 x 和 y 没有设置公共位时。
正式看到这一点的简短方法是注意:
a + b = a + b - 2(a & b) + 2(a & b)
= (a - (a & b)) + (b - (a & b)) + 2(a & b)
= (a - (a & b)) ^ (b - (a & b)) + 2(a & b)
= (a ^ (a & b)) ^ (b ^ (a & b)) + 2(a & b)
= a ^ b + 2(a & b)
冗长的解决方案使用如下数学归纳法(这可能被某些人认为是矫枉过正,但在我看来,值得知道它):
首先确保它在 a 和 b 都等于 0 的情况下工作(您也可以尝试使用一位数字来解释该算法的工作原理)。使用数学归纳法时永远不要忘记这一步。
接下来,假设这适用于 n-1 位数字,我们必须证明它也适用于 n 位数字。现在写 a = 2a' + a'' = 2a' ^ a'' 和 b = 2b' + b'' = 2b' ^ b'' 其中 a'', b'' 在集合 {0, 1} 中(那么 2a' 和 a'' 没有设置公共位!)。然后:
(a ^ b) + 2(a & b) = (2a' ^ a'' ^ 2b' ^ b'') + 2((2a'' ^ a') & (2b'' ^ b')) =
(2a' ^ 2b') ^ (a'' ^ b'') + 2((2a'' & 2b'') ^ (a'' & b'')) =
(2a' ^ 2b') + (a'' ^ b'') + 2((2a'' & 2b'') + (a'' & b'')) =
(2a' ^ 2b') + 2((2a'' & 2b'') + (a'' ^ b'') + (a'' & b'')) =
2a' + 2b' + a'' + b'' = a + b
现在要检查的最后一件事是这个循环真的终止了。要看到这一点,请使用在每个步骤中 a 和 b 都是非负的事实,并且在每次迭代后仍然如此。
因此我们得到了 b <= a + b。接下来请注意,在 n 步之后 b 必须以 n 个零结尾。因此我们不能做超过 log_2(a+b) 步骤,因为我们会得到 b = 0 或 b = k * 2* n >= 2 *n > 2**log_2(a+b) = a+b 矛盾假设。这里**当然表示取幂。
在 Java 中,该算法也适用于负整数。这是因为负整数在 Java 和大多数编程语言中的存储方式在这里,有符号数和无符号数的加法和减法在位上的工作方式相同,因此适用于无符号数的代码也适用于有符号数。