Problem 74
コンテンツ
http://projecteuler.net/index.php?section=problems&id=74
いわゆる、すなおなアルゴリズム。
よって、遅い。
import Data.List import Data.Char next = toInteger.sum.map (f.digitToInt).(show::Integer->String) where f n = product [1..n] link = length.link' [] . iterate next where link' xs (y:ys) | elem y xs = xs | otherwise = link' (xs++[y]) ys main =print.length.filter (==60).map link $[1..10^6]
高速化への一歩は計算結果の再利用だろう。
つまりDPすれば速くなるのでは?
計算量はlinkを構成する際の数字が単調減少でないから、良く分からない。が
各数字のlink長は、次の数字(各桁の階乗和)のlink長+1だから
その数字が計算してあればそれ以降のlinkを構成する必要はない。
そして、各桁の階乗和はそんなに大きくならないはず(link最長は60ナンだし)。
だから多分、各桁の階乗和を計算する回数は10^6くらい。
ん?大して変わってなくね?
素直にやっても各桁の階乗和を計算するのは
高々60*10^6だ。
いや、しかし、好意的に解釈すれば60倍速くなるんだよ。
というか、高々60倍の高速化にしかならない。
(各桁の階乗和が計算時間の大部分を占めているという仮定のもとでは)
たぶん領域計算量は増えるんだろうな、表を作るから。
結論:DP使っても大して速くならないのでは?
実装。
import Data.List import Data.Array.IArray import Data.Char linkArray m = lArray::Array Integer Int where lArray = listArray(1,m)[link n|n<-[1..m]] link 871 = 2 link 45361 = 2 link 872 = 2 link 45362 = 2 link 169 = 3 link 363601 = 3 link 1454 = 3 link n | n== next n = 1 | otherwise = link'.minPerm$n link' n | next n <= m = (1+).(lArray!).next$n | otherwise = (1+).(link.next)$n minPerm ::Integer -> Integer minPerm n = let (zs,xs)=span(=='0').sort.show$n in read$head xs : zs++tail xs next ::Integer -> Integer next = toInteger.sum.map (f.digitToInt).show where f n = product [1..n] main = print.length.filter(==60).elems.linkArray$1000000
やっぱりあまり速くなかった。
置換の計算をさぼっているので少しは速いはず。たぶん。
というか実際必要なのは数字の組だから、1から順に探すのではなく、
数字の組を探せばよい。
こっちはDPより速くなるだろう。多分オーダーが1つ落ちる。
だから100倍ぐらい速くなるはず。でも、もうこの問題は飽きた。
作成者 Toru Mano
最終更新時刻 2023-01-01 (c70d5a1)