練習問題 6.14.2

6.5.2で定義した is_prime は、n が素数か判定するのに、n-1 から順に 2 までのすべての数で割り切れるかどうか試していますが、これはあまり効率がよいとはいえません。以下のそれぞれの方法で、3000番目の素数の計算が速くなるかどうか試してみなさい。

2. 割る数の上限を、ルートn の小数点以下の切り捨ての数とする。切り捨ては、OCaml では、floor という関数になる。

解答

type intseq = Cons of int * (int -> intseq);;

(* n番目の要素を取り出す関数 *)
let rec nthseq n (Cons(x, f)) =
    if n = 1 then x
    else nthseq (n-1) (f x);;

(* 素数かどうかを調べる。割る数の最大は、調べる数の平方根 *)
let is_prime2 x =
    let rec is_divisible_from_2_to n =
        if n > int_of_float(floor(sqrt (float_of_int x))) then false
        else
            (n > 1) && ((x mod n = 0) || is_divisible_from_2_to (n+1))
    in
    not (is_divisible_from_2_to 2);;

(* 素数の無限列 *)
let rec prime_seq x =
    if is_prime2 (x+1)
    then Cons(x+1, prime_seq)
    else prime_seq (x+1);;

  (* 素数の無限列を得る *)
let prime = prime_seq 1;;

let test1 =  nthseq 10 prime = 29;;
let test2 =  nthseq 3000 prime = 27449;; (* 一瞬で答えが出た *)