Pythonで解く数学パズル

「プログラマ脳を鍛える数学パズル」をPythonで解きます。

フィボナッチ数列:「プログラマ脳を鍛える数学パズル」Q11の答え

問題

フィボナッチ数列のうち、各桁の数字を足した数で割り切れる数を以下の例に続けて 小さい方から5個求めてください。

例)
2 : 2 ÷ 2
3 : 3 ÷ 3
5 : 5 ÷ 5
8 : 8 ÷ 8
21 : 21 ÷ 3 ← (2 + 1 = 3で割る)
144 : 144 ÷ 9 ← (1 + 4 + 4 = 9で割る)

元ネタ:
第3回「今週のアルゴリズム:フィボナッチ数列」正解者発表|CodeIQ MAGAZINE

わたしの考え方

わたしのオリジナル解答

def fibonacci(n):
    if n == 0: return 1
    if n == 1: return 1
    return fibonacci(n-1) + fibonacci(n-2)

def sum_each_fig(m):
    sum_each = 0
    m = str(m)
    for x in m[0:]:
        sum_each += int(x)
    return sum_each

cnt = 0
n = 13
while cnt <5:
    if fibonacci(n) % sum_each_fig(fibonacci(n)) == 0:
        print fibonacci(n)
        cnt += 1
    n += 1

このプログラム、理論上は正しい筈なのだが答えを出すのにやたらと時間がかかる。何かがおかしい。4つ目の答えまでは待ったけど、5つ目の答えは待ちきれずプログラムを強制終了した。

模範解答のミソ

  • フィボナッチ数を再帰を使って求めると、値が大きくなるにつれて処理時間が爆発的に延びてしまう
  • 模範解答では「メモ化」して処理速度を上げる事を推奨している

修正されたわたしの解答

def fibonacci(n, a=1, b=0):
    return b if n < 1 else fibonacci(n - 1, a + b, a)


def sum_each_fig(m):
    sum_each = 0
    m = str(m)
    for x in m[0:]:
        sum_each += int(x)
    return sum_each


cnt = 0
n = 13
while cnt < 5:
    if fibonacci(n, a=1, b=0) % sum_each_fig(fibonacci(n, a=1, b=0)) == 0:
        print fibonacci(n, a=1, b=0)
        cnt += 1
    n += 1

気づき・反省点

  • フィボナッチ数の関数を「末尾再帰」を利用する事で高速化した
  • 実際一瞬で解答が吐き出されて感動した
  • 「メモ化」については今後の学習の課題としたい

参考にしたサイト

Algorithms with Python / 再帰定義
数値を文字列に変換(str) - 文字列 - Python入門