ここにあるすべての数字を 2 進法で考えてみてください。
そのようなコードで通常見つけたいのは、「ループ不変条件」です。この場合、反復ごとに a + b が一定であることを確認したいと考えています。したがって、b が 0 になってループを抜ける場合、a は元の a と b の合計に等しくなければなりません。次のステップは、ループが最終的に終了することを確認することです。後でこれに戻ります。まず、この場合は等式を使用している不変部分を理解しましょう。
a + b = (a ^ b) + 2 * (a & b)
ループ内で、新しい a は古い a ^ b と等しくなり、新しい b は 2 * (a & b) になります。これは (a & b) << 1 と同じです。これが問題の本質です。この平等。それはまさにあなたが足し算をする方法です。
解決策を2つ紹介します。両方で、次の単純な事実を使用します。
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 の場合に機能することを確認します (このアルゴリズムがどのように機能するかを説明する 1 ビットの数値を試すこともできます)。数学的帰納法を使用するときは、この手順を忘れないでください。
次に、これが 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 の両方が非負であり、これが各反復後も true のままであるという事実を使用します。
したがって、b <= a + b となります。次に、n ステップの後、b は n 個のゼロで終わらなければならないことに注意してください。したがって、b = 0 または b = k * 2* n >= 2 *n > 2**log_2(a+b) = a+b のいずれかが矛盾するため、log_2(a+b) ステップを超えることはできません。予測。ここで ** はもちろん累乗を表します。
Java では、このアルゴリズムは負の整数でも機能します。これは、負の整数が Java およびほとんどのプログラミング言語でどのように格納されるかによるものです。ここで、符号付き数値と符号なし数値の加算と減算はビットに対して同じように機能するため、符号なし数値で機能するコードは符号付きでも機能します。