http://projecteuler.net/index.php?section=problems&id=35
重複して素数判定したくないのでうまく代表元を選んでそれを回転して素数判定したいところです。それには、回転して最小の数を代表元に選べばよいです。例えば、791なら791→917→179だから、179を代表元にします。しかし、代表元を列挙するのは難しいので、頭の数字を決めたら、それより小さな数字は使えないことにして列挙します。回転してみて最小だったらそれを使います。2桁以上の場合、当然数字は1,3,7,9しか使えません。最後に回転して同じ数が出てくる場合があるので、それを考慮に入れてカウントします。
let sieve max_n =
let a = Array.create (max_n + 1) true
a.[0] <- false
a.[1] <- false
for p in Seq.takeWhile (fun k -> k * k <= max_n)
(Seq.filter (fun k -> a.[k])
(Seq.initInfinite (fun k -> k))) do
for k in p*2..p..max_n do
a.[k] <- false
List.filter (fun i -> a.[i]) [ 2..max_n ]
let rec pow n e = if e = 0 then 1 else n * (pow n (e - 1))
let rec num_digits n = if n < 10 then 1 else 1 + (num_digits (n / 10))
let rotate n =
let e = num_digits n
let m = pow 10 (e - 1)
Seq.take e (Seq.unfold (fun n' -> Some(n', n' / 10 + n' % 10 * m)) n)
let N = 6
let primes = sieve (int (sqrt (double (pow 10 N))))
let is_prime n = Seq.forall (fun p -> n % p <> 0)
(Seq.takeWhile (fun p -> p * p <= n)
(List.toSeq primes))
let is_circular_prime n = Seq.forall is_prime (rotate n)
let is_rot_min n = Seq.forall (fun m -> m >= n) (rotate n)
let rot_min n =
if n = 1 then
seq { 2..9 }
else
let ds = [ 1; 3; 7; 9 ]
let rec rot_min_core n d0 =
if n = 0 then
seq [ 0 ]
else
seq { for m in rot_min_core (n - 1) d0 do
for d in Seq.filter (fun d -> d >= d0) ds ->
m * 10 + d
}
let p = pow 10 (n - 1)
seq {
for d0 in ds do
for m in rot_min_core (n - 1) d0
-> d0 * p + m
}
let uniq n = Set.count (Set.ofSeq (rotate n))
printfn "%d" (Seq.sum (Seq.map uniq
(Seq.filter
(fun n -> is_rot_min n && is_circular_prime n)
(seq { for n in 1..N do
for m in rot_min n -> m }))))