CheckSum (Incinerator) - チェックサムを求めよ
どんな問題?
CheckSum
http://www.checkio.org/mission/check-digit/
入力値のチェックサムを求めよ。
チェックサムの求め方は以下の通り。
- 各桁の数字を変換する。
- 右から偶数番目:数字を2倍して十の桁と一の桁を足した数字。
- 右から奇数番目:数字をそのまま使う。
- 変換した数字を合計する。
- 合計値が10の倍数になるように、最後の数字を追加する。
戻り値は、3で求めた最後の数字と、2で求めた合計値をリストにして返す。(タプルじゃなくてリスト)
また、各桁が英字(大文字のみ)の場合にも、チェックサムを求めることができる。
その場合は、英字のアスキーコードから48を引いた数を使って、同じように計算する。
数字と英大文字以外は、無視してよい。
どうやって解く?
流れとしては、
- 引数から、数字と英大文字だけを取り出す。
- 右から偶数番目の文字と奇数番目の文字に分ける。
- それぞれ数字を変換して合計する。
- 合計値が10の倍数となるような、最後の数字を計算する。
という感じ。
数字と英大文字だけを取り出す
以前、House Passwordで知った string.digits と string.ascii_uppercase 定数を使おう。
s = [c for c in data if c in string.digits + string.ascii_uppercase]
右から偶数番目の文字と奇数番目の文字に分ける
ちょっとまぎらわしいが、index=0は偶数となる。
index | 5 | 4 | 3 | 2 | 1 | 0 |
奇数 | 偶数 | 奇数 | 偶数 | 奇数 | 偶数 |
文字列を右から偶数番目と奇数番目に分けるには、スライスを使って s[-1::-2] と s[-2::-2] とする。s[-1]が一番右の文字で、そこからstep=-2で1つおきに左に進んでいくので、偶数番目を取り出せる。この方法は、Bit Messageのときにも使った。
>>> '6543210'[-1::-2] '0246' >>> '6543210'[-2::-2] '135'
数字を変換して合計する
偶数番目は、数字ならint()で整数にすればよい。英字ならord()して48を引く。
def odd_digit(c): if c in string.digits: return int(c) else: return ord(c) - 48
奇数番目は、2倍して十の桁と一の桁を足す。
def even_digit(c): n = odd_digit(c) return n * 2 // 10 + n * 2 % 10
あとは、sum()で合計値を求めるだけ。
sum_digits = sum(even_digit(c) for c in s[-1::-2]) + sum(odd_digit(c) for c in s[-2::-2])
合計値が10の倍数となるような、最後の数字を計算する。
(10 - sum_digits % 10)
だと、10~1になってしまうので、もう一度 % 10 しておく。
final_char = str((10 - sum_digits % 10) % 10)
まとめてホイ
import string def checkio(data): def odd_digit(c): if c in string.digits: return int(c) else: return ord(c) - 48 def even_digit(c): n = odd_digit(c) return n * 2 // 10 + n * 2 % 10 s = [c for c in data if c in string.digits + string.ascii_uppercase] sum_digits = sum(even_digit(c) for c in s[-1::-2]) + sum(odd_digit(c) for c in s[-2::-2]) final_char = str((10 - sum_digits % 10) % 10) return [final_char, sum_digits]
クリア~。
公開後に気付いたけど、if ~ else は1行にまとめれば良かった。まあいいや。
他の人の答え
odd_digit()の部分は、数字と英大文字で分岐しなくても、
def odd_digit(c): return ord(c) - 48
でいけたのか。48ってなんのこっちゃと思ってたが、ord('0') が 48 なのか。よく見たら問題文にも書いてあった・・・。
また、final_charを求めるときに、0~9にするため
final_char = str((10 - sum_digits % 10) % 10)
としていたが、
final_char = str(-sum_digits % 10)
でいけるらしい。
負数の剰余ってあまりイメージできないけど、
>>> -1 % 10 9 >>> -2 % 10 8
となってるんだな。