こんにちは
今回の学びは
・pythonはintは桁の上限がない。floatはある。マイナスの表現は自分でやれ
・XORとANDで半加算器がつくれます
問題は。。。
(時間配分は5分問題理解 10分検討 10分実装 20分振り返り の一文45分構成)
まずは入出力をしっかりおさえましょう。
この問題、難しくて解けませんでした。
+を使わずに考えてね、なんて
コンピューターの仕組みを理解した上で工夫したら解けるよね!?と挑発されているようです。
良問な気がします。
位置ビットづつ計算してcarry overを作ってという方法で
実装していまたのですが、
実装前後でこれは毎回書きましょう。timespace analysis (時間とスペースの分析)は O(n)
これだとマイナスのケースがうまくいきません。
後半
a &= (2**33 - 1)
のようにすることで強制的に32bit Intにして
python3の上限がない制約を取っ払おうとしたのですが、それでも結果は変わらず
解答をみることとしました。
取りあえず11/13までクリアした私の誤解答はこちら
class Solution:
def getSum(self, a: int, b: int) -> int:
ans = 0
carry = 0
i = 0
# turn them into 32 bit integer
a &= (2**33 - 1)
b &= (2**33 - 1)
for k in range(32):
a_ = a & 1
b_ = b & 1
if a_ == 1 and b_ == 1:
if carry == 1:
x = 1
else:
x = 0
carry = 1
elif a_ == 1 or b_ == 1:
if carry == 1:
x = 0
carry = 1
else:
x = 1
carry = 0
else:
if carry == 1:
x = 1
carry = 0
else:
x = 0
carry = 0
ans |= (x * 2**i)
a >>= 1
b >>= 1
i += 1
ans &= (2**33 - 1)
return ans
# 1532pm start (1, 2, 3), (-2, 3, 1), (3, 4, 7), (8, 14, 22), (-3, -9, -12), # 2023pm # やっぱりminusのケースでうまくいきません。。。 # むずかしですね。 # 11 / 13 test cases passed. 途中、家事やら早みまして時間の経過は大きいですが これどうやって解くのでしょうか? この時点で珍しくbad評価の方が多い問題であると気づきました。どうやら皆さん解答がeasyとは思わないとの意見か、そもそも解答に納得いってないか。 自分がどう感じるか楽しみです。 ということで
みて見ていきましょう。 まずこちらの
まじかーーーーー XORとANDの組み合わせで足し算になってる〜〜〜〜〜〜〜〜〜〜〜 ・足し算はXORとANDの組み合わせでできる 知らなかった わからなかった
def _add(a, b):
if b == 0:
return a
return _add(a ^ b, (a & b) << 1)
むむ
でもこれ
よく見てみたら、よくわからん。単順に一行とかじゃなくて再帰になってます
一行で (a ^ b) & ((a & b) << 1)でいけそうかと。。。思って喜んだのだが。
これも同じだね。でもキレイだね。
class Solution(object):
def getSum(self, a, b):
"""
:type a: int
:type b: int
:rtype: int
"""
mask = 0xFFFF
Max = 0x7FFF
if b == 0:
return ~(a ^ mask) if a >= Max else a
return self.getSum((a ^ b) & mask, ((a & b & mask) <<1))
まだ理解できてないけど電子回路アダーーー!っ
ここいらで
pythonでのビット演算子を復習しよう
なぜならほぼ忘れているからだw
あら〜2の補数だなんて懐かしい、みたいな気持ちに浸りつつ復習しよう
すると
x = 1 を反転させて x = ~x x = x + 1 をすれば-1となることがわかる これが2の補数だ わかりやすく表示すると print(bin(1&0xff)) 0b1 print(bin(~1&0xff)) 0b11111110 print(bin((~1+1)&0xff)) 0b11111111
のような感じです
そして
それから電子回路勉強してみよう
この電子回路の解答には様々なコンピューターの真実が隠されていそうな気がするからだ。
en.wiki digital adder
おうおう日本語でもありますなぁ。
wikiだけでは理解できませんでしたので動画youtube電卓で足し算ができる仕組なんかも見させていただきました。
半加算器では1たす1ができます。それ以外はできません!きっぱり。

なんどやっても、マイナスの計算がうまくいきません。
一つは、half adderとfull adderの実装が微妙にまちがっていた。
最初はfull_adderをXORつかわないパターンでかいていたのですが、微妙にバグをおいているようで
シンプルなXORとANDをつかった半加算器に切り替えました。それはよかったです。
それでfull加算器もつくりました。
このあたりの文字列をつかった加算器の実装は参考にしました。でも疑問、なぜ文字列やねん!と。最終的には私のかいた
intで扱っている加算器の方が良いでしょう。:)
2つめは、マイナスの計算、特に-3, -9が答え-12になってほしいのに 4294967284 となるやん!
なんじゃこら
最終的な糸口は
これって答え的には合ってんじゃね!?と
print(bin(ans & 0xff))
をついかしてみると
0b11110100
だと
おっしゃるわけです。
ふとipythonをおこして、これって-12のことなんじゃね!?と確かめると
In [42]: x Out[42]: -12 In [43]: bin(x) Out[43]: '-0b1100' In [44]: bin(x & 0xff) Out[44]: '0b11110100'
そうだ。といことになって、自分の計算にやっと自信がもてました。
あとは、若干こじつけだけど
こんなデバッグで-12のパターンをとおしました。
このアイデアはこちらの記事(ビット表現からサインed イントへの変換方法)も参考にしました。
if byte > 127:
return (256-byte) * (-1)
else:
return byte
・pythonはintは桁の上限がない。floatはある。マイナスの表現は自分でやれ
という学びを得ました
def getSum(self, a: int, b: int) -> int:
i = 0
ans = 0
c = 0 # initally C is 0
for i in range(32):
a_ = (a >> i) & 1
b_ = (b >> i) & 1
s, c = self.full_adder(a_, b_, c)
ans = ans | (s << i)
print(bin(ans & 0xff))
if ans > 0xff:
print("dekai")
ans = (0xff - (ans & 0xff) + 1) * -1
else:
print("chisai")
return ans
後は、これを少しばかりMAX = 0xffffと汎用化させて無事クリアとなりました!
最終的な実装です
マイナスの扱いがすごくむずかしかったです。
from typing import Tuple
class Solution:
def half_adder(self, a, b) -> Tuple[int, int]:
s = 1 & (a ^ b)
c = 1 & (a & b)
return (s, c)
def full_adder(self, a, b, x) -> Tuple[int, int]:
(s1, c1) = self.half_adder(a, b)
(s, c2) = self.half_adder(s1, x)
c = (c1 | c2)
return (s, c)
def getSum(self, a: int, b: int) -> int:
# a = (a & 2**33-1)
# b = (b & 2**33-1)
i = 0
ans = 0
c = 0 # initally C is 0
for i in range(32):
a_ = (a >> i) & 1
b_ = (b >> i) & 1
s, c = self.full_adder(a_, b_, c)
ans = ans | (s << i)
#print(bin(ans & 0xff))
MAX = 0xffff
if ans > MAX:
ans = (MAX - (ans & MAX) + 1) * -1
return ans
実装後です。timespace analysis (時間とスペースの分析)は O(N)
まとめ
・pythonはintは桁の上限がない。floatはある。マイナスの表現は自分でやれ
・XORとANDで半加算器がつくれます
最後に一言、これはeasyな問題ではないです。に一票!
勉強なりました!
以上です
どうやら皆さん解答がeasyとは思わないとの意見か、そもそも解答に納得いってないか。
自分がどう感じるか楽しみです。
ということで