*57の一番上の桁を入れ替えることで以下の6つの素数を作り出すことができる: 157, 257, 457, 557, 757, 857。
56**3の上から3桁目と4桁目を同じ数字で置き換えることで56003, 56113, 56333, 56443, 56663, 56773という素数の族を作り出すことができる。これは 7つの素数になる一番最初の数である。ここでこの族の中の一番最初の数56003 がこの属性を持つ最小の素数ということになる。
(連続している必要はない)一部の桁を同じ数字で置き換えることで8つの素数 を作り出せる最小の素数を求めよ。
順番に調べていけば必ず答えが見つかるというのは想像に難くないが、少ない 計算量で最小の答えが見つかる方法が全く見当がつかない。ここでは以下の方 針で地道に計算することにする。
まずn桁の素数について考える。最初にn桁の全素数のリストを生成する。次に n桁のうちの置き換える場所が1〜n箇所の全てのパターンを考える。素数の中か らそのパターンに当てはまる(置き換えようと思っている場所が同じ数字)も のだけをとりだす。パターン以外の部分が等しいものでグループに分ける。同 じグループに要素が8個あったらそれが解である。
このやり方では必ずしも最初に最小の解が見つかるとも限らないので、とりあ えず目測で5桁と6桁の数で要素数が7より大きいものを取り出して表示すること にする。
checkというのがn桁の素数のうちi箇所を置き換えたものを調べる関数で、ここ ではnが5,6のときにiが1からnについて全て調べ、concatでリストを連結した後 に、長さが7より大きいものをfilterで取り出している。
checkの中身はpsが対象とするn桁の素数全体、posが置き換える場所のパターン のリスト、それぞれのパターンについて、当てはまるものをfilterでとりだし、 family関数で分類している。
このプログラムは非常に計算に時間がかかるが幸運にも答えが求まったのでこ こはこれでよしとする。
euler051 = filter ((> 7) . length)
$ concat $ [check i n | n <- [5..6], i <- [1..n]]
where
makePos i n
| i == 0 = [take n $ repeat 0]
| i == n = [take n $ repeat 1]
| otherwise = (map (1 :) $ makePos (i - 1) (n - 1))
++ (map (0 :) $ makePos i (n - 1))
match p n = length (delete (-1) $ nub $ zipWith m' p ns) == 1
where ns = intToList n
m' 0 i = -1
m' 1 i = i
family p [] = []
family p [n] = [[n]]
family p (n:ns) = (n : filter (eqn n) ns)
: (family p $ filter (not . (eqn n)) ns)
where eqn x y = all eqn' $ zip3 p xs ys
where xs = intToList x
ys = intToList y
eqn' (p, x, y) = p == 1 || x == y
check i n = concat [family p $ filter (match p) ps | p <- pos]
where ps = takeWhile (< 10 ^ n) $ dropWhile (< 10 ^ (n - 1)) primes
pos = makePos i n