ABC 400 B問題 Sum of Geometric Series
AtCoder beginner contest 400 に参加して、A と B の問題を正解できました。しかし、見直してみると B 問題 Sum of Geometric Series を正解できたとはいえひどいコードだったので修正した。
問題
AtCoder beginner contest 400のB 問題 Sum of Geometric Seriesは、基本的にただ足し算をするだけ。ただし合計が 10^9 を超えた場合には、文字列 inf を表示する。
ですので、することは次のように成ります。
- 指定された値までループを回して加算を繰り返す。
- 合計が 10^9 を超えたらループを抜けて inf を表示する。
コンテスト内で正解にはなったものの、改めて見直してみるとひどいコードなので、振り返りながら修正してみました。
解決方法
コンテスト内で作成したコードは次のもので、正解に成りました。
n, m = map(int, input().split())
total = 0
try:
for i in range(m + 1):
total += n ** i
if total > 1_000_000_000:
total = "inf"
except TypeError:
total = "inf"
print(total)
最初書いたコードは、10^9 を超えると次のようなエラーになってしまいました。
Traceback (most recent call last):
File "/home/nosuzuki/reposit/at_coder/ABC_400/B.py", line 20, in <module>
total += n ** i
TypeError: can only concatenate str (not "int") to str
急いでいたのでエラーメッセージをよく見ずにとりあえずの対応でエラーを捕まえて無理やり inf を表示させて正解にしています。
エラーの原因
落ち着いてエラーメッセージを読むと、can only concatenate str (not "int") to str
と書かれており、文字列の結合に問題があることが分かります。
そうです。最大値を超えている時に代入した inf に数値を結合しようとしたエラーです。これは、最大値を超えた時にループから抜ける break を忘れたため、次のループを実行してしまったためです。
そこで、次のように修正すれば例外に頼らないコードに成ります。
n, m = map(int, input().split())
total = 0
for i in range(m + 1):
total += n ** i
if total > 1_000_000_000:
total = "inf"
break
print(total)
それでも、意図的で Python では許されているとは言っても、数値が入っていた変数 total に文字列を入れることは褒められません。そこで、これをなんとかしたいと思いました。
そのためには最大値を超えたことをループ外で検出する必要があります。そこで方法としては、次の 3 つが思いつきました。
- 最大値を超えた時に、特別な値を変数 total に設定する。
- 最大値を超えた事を表すフラグを導入する。
- 何らかの方法で、break したことを検出する。
1 の特別な値(マジックナンバー)を入れることは推奨されないし、2 の条件判断をするためだけに変数を導入するのもスマートではありません。ということで、ループが break したか知ることができるか検索したら、else が使えることが分かりました1。
ループ内で break が起きなかった場合 には、else ブロックが実行されます。そこで最終的には、次のように修正できます。
n, m = map(int, input().split())
total = 0
for i in range(m + 1):
total += n ** i
if total > 1_000_000_000:
print("inf")
break
else:
print(total)
- 最大値を超えた場合には、inf を表示してループを break で抜けます。
- 最大値に達しなかった場合には、else ブロクが実行されて合計値が表示されます。