日本語翻訳サイトは、プロジェクトオイラー日本語 でネット検索です。
問92「桁の2乗による連鎖」
各桁の2乗を足し合わせて新たな数を作ることを, 同じ数が現れるまで繰り返す.
例えば
44 → 32 → 13 → 10 → 1 → 1
85 → 89 → 145 → 42 → 20 → 4 → 16 → 37 → 58 → 89
のような列である. どちらも1か89で無限ループに陥っている.
驚くことに, どの数から始めても最終的に1か89に到達する.
では, 10,000,000より小さい数で89に到達する数はいくつあるか.
注意!!!
自力で解きたい人へ
以降の記述には解法に関するネタバレが含まれます。
私の回答例は以下の通りです。
def f(n): P = [i*i for i in xrange(10)] s = len(str(n)) m = 9*9*s L = [0]+[-1]*(m-1) L[1], L[89] = 0, 1 for i in xrange(1, m): j = i while L[j]<0: j, r = 0, j while r: j += P[r%10] r //= 10 L[i] = L[j] M = [0]*m for i in xrange(10): M[P[i]] = 1 for u in xrange(2, s): W = [0]*m for i in xrange(m): for j in xrange(10): if P[j]<=i: W[i] += M[i-P[j]] M = W[:] return sum([i*j for i, j in zip(L, M)]) n = 10000000 print f(n)
1から10,000,000まで、桁の2乗和の連鎖を総当りで計算すると、とても1分ルールに合いません。
試行錯誤したところ、桁2乗和は1回計算するだけで値がずいぶんちいさくなることがわかりました。
桁数sのときの桁2乗和最大値mは9がs個並んだ数で、m = 9*9*s です。
7桁ならば、m = 9*9*7 = 567 です。
そこで、
・m以下の数は桁2乗和の連鎖を最後まで計算して89ループになるか判定しておく。
・桁2乗和は1回だけ計算しm以下にしたところで数える。
ことにしました。
・89ループ判定結果リストL
1からmまでの桁2乗和の連鎖を最後まで行い、89ループになる場合は1、その他は0を設定します。
-1で初期設定しているので、while L[j]<0として未設定が続く限り連鎖をたどります。
rのwhileループで、rの10で割った余りとして、下から1桁ずつ取り出して2乗和を累積します。
2乗の計算はいちいち行わず、2乗リストP[0,1,4,9,16...]を参照します。
・桁2乗和ごとの件数リストM
初期値として1桁数値の桁2乗和の位置(1,4,9...)に1を設定します。
2桁以上の数値については、1つ少ない桁数までの累積件数の、最上位の数値の2乗分だけ小さい位置にある値を、採用し累積していきます。
このようにしてだんだん桁数を増やしていきます。
こうして、
・89ループ判定結果リストL(89ループに達する数の位置に1が立ち、他は0が設定)
・桁2乗和を1回だけ計算した値ごとの件数リストM
の同じ位置の要素同士の積を求めることで89ループの部分を取り出します。
この総和が求める件数です。
解答はこのすぐ下の行です。文字の色を白にしてます。選択状態にすると見えます。
8581146