私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「竹やぶ焼けた?」(CodeIQ)を参照してください。
ここでは指定された値を基数とするように変換してみます。
複数の基数が指定されたとき、そのいずれでも回文数となるような数を求めます。
※回文数とは左から読んでも右から読んでも同じである数のことです
例えば、10進数の「20」は2進数だと「10100」で回文数ではありませんが、3進数では「202」なので回文数です。
標準入力から2つの数字が与えられた時、この2つの数字を基数とする数に変換することを考えます。
10進数で10以上の数のうち、上記の条件を満たすような最小の数を出力してください。
Pythonで解答しています。
#!/usr/local/bin/python3 # -*- coding: utf-8 -*- import fileinput def getRadixNum(x, n): if x < 0: return None if not 2 <= n <= 36: return None if x == 0: return "0" nums = "0123456789abcdefghijklmnopqrstuvwxyz" ret = "" while x > 0: ret = nums[x%n] + ret x = x//n return ret def isKaibun(x): d = len(x) // 2 for i in range(d): l = i r = len(x)-i-1 if x[l] != x[r]: return False return True if __name__ == "__main__": for line in fileinput.input(): if not line.strip(): continue rads = line.strip().split(",") rads = list(map(lambda a: int(a), rads)) r1 = max(rads) r2 = min(rads) i = 9 while True: i += 1 s1 = getRadixNum(i, r1) if s1 is None: print("Radix is too big. [" + str(r1) + "]") break if not isKaibun(s1): continue s2 = getRadixNum(i, r2) if isKaibun(s2): break print(str(i))
この問題は2つのことが要求されます。
1つは進数変換ができること。
もう一つは回文であることを判断できることです。
別の問題で36進数への変換をやっていたのでそのコードを元に(この問題をやった時点では結構Pythonにも慣れてきたので)ちょっと洗練した関数にしています。getRadixNum(x, n)がその関数です。
引数のxが変換対象の数、nは変換の基数です。関数は進数変換した結果の文字列を返します。
進数変換はxを基数nで割ったあまりが最初の桁になり、その商が0になる迄桁を1つ上げながら同じことを繰り返すことでできます。
numsは進数変換後の各桁の文字を定義した定数です。Pythonの文字列は文字の配列としても扱えるので、元の10進数の値を基数nで割った余りが文字の配列の添え字番号と一致することを利用して結果文字列を生成しています。
isKaibun()が回文のチェックをする関数です。
理屈は単純で文字列の両側から1文字ずつ同じか調べて異なる文字が見つかったらFalseを返し、最後まで同じだったらTrueを返すだけです。ただし、検査は文字列の長さの半分(小数点以下切り捨て)までしか必要ありません。
愚直に1つずつ検査しています。
whileを使っているのはいつかは終わるとはいえカウンタがいくつまで必要かわからないループのためです。iを9で初期化しているのは10から始めるためです。9以下の場合、10進数では1文字しかないので回文としては扱わないだろうと思ってこうしました。
この問題は長い間手をつけずに放置していました。理由は何進数まで対応すれば良いか仕様にないという曖昧さのためいまいちやる気が起きなかったのです。理論上、わけのわからない大きな基数(1000000とか)でもありえますから。やってみたら16進数までしかありませんでしたが。