Project Euler – 問題14

正の整数に対して以下のような反復数列を定義する。
n→n/2(nが偶数のとき)
n→3n+1(nが奇数のとき)

この規則を13から適用すると次のような数列ができる。13→40→20→10→5→ 16→8→4→2→1

この数列は10の項から成り立っているのがわかる。これはCollatz問題と呼ばれ 証明はされていないが、全ての数から始まる数列が1で終わると考えられている。

1000000未満の数でもっとも長い数列を生成する数を求めよ。

(注)数列の途中で1000000を超えることはある。

比較の対象は数列の長さで求める数は最初の数なので、最初の数と数列の長さ を組み合わせたものを扱うことにする。Haskellでは一般的にtupleと呼ばれる ()でくくったデータの組をひとつのデータとして扱うことができる。まずリス トの内包表記で1から999999の数についてその数とその数から始まる数列の長さ のtupleのリストを生成する。ここで数列の長さは定義をそのまま実装したもの で求める。

そしてそのリストからtupleの2番目の値が最大である要素を求め、最後に関数 fstで最初の値をとりだす。maximumByは指定した関数で比較して最大値を求め る関数で、cmpSndとしてtupleの2つめの要素を比較する関数を定義し使用して いる。このプログラムは若干時間がかかるが待てないほどではないのでここで はこれでよしとする。

cmpSnd (_, x) (_, y) = compare x y

euler014 = fst $ maximumBy cmpSnd [(n, collatzLength n) | n <- [1..999999]]
    where collatzLength n
              | n == 1    = 1
              | even n    = (collatzLength (n `div` 2)) + 1
              | otherwise = (collatzLength (n * 3 + 1)) + 1