CoqでProject Euler (problem 44)

Problem44が解けた? - みずぴー日記に触発されて、Project EulerのProblem 44に挑戦してみた。

問題44:
五角数は Pk = k(3k-1)/2で生成される. 
五角数のペア PmとPnについて, 差と和が五角数になるものを考える. 
差を D = |Pm - Pn| と書く. 
差 D の最小値を求めよ.

この問題は D の最小性がポイントになる。見付けたほとんどのプログラムは、扱う五角数の上限を固定して計算していたが、それでは厳密には D 自体の最小性を保証することはできない。
今回は D の最小性を保証するプログラムと、その根拠となる単調性をCoq証明した。

アイデアは次のとおり:
m = n+i とおき、五角数の差D を考えると、Dはi, nそれぞれにおいて単調増加になる。
この単調増加性を使えば、ある基準値Mに対して、D(i,n)がMを越えないようなすべての五角数の組を調べることができる。

厳密さを保つために任意精度演算ライブラリNumsのbigなintを使用したが、わりと現実的な時間内に応答を得ることができた。

実行結果:

Answer: 5482660

real    1m52.673s
user    1m52.563s
sys     0m0.059s
(**
   solve.ml [projecto euler problem 44]
   2008/06/23 Y. Imai wrote.
   compile $ ocamlopt nums.cmxa solve.ml
**)

open List
open Big_int
let ( @@ ) f x = f x

let big = big_int_of_int
let ( =. ), ( >. ), ( *. ), ( +. ), ( -. ), ( /. ) =
  eq_big_int,
  gt_big_int,
  mult_big_int,
  add_big_int,
  sub_big_int,
  div_big_int
let pent n = (big 3 *. n *. n -. n) /. big 2
let is_pent y =
  let s = big 1 +. big 24 *. y in
    square_big_int (sqrt_big_int s) =. s &&
      mod_big_int (sqrt_big_int s +. big 1) (big 6) =. zero_big_int
      
let solve m =
  let store = ref [] in
  let rec aux i n =
    let diff = pent (big (n+i)) -. pent (big n) in
    let sum  = pent (big (n+i)) +. pent (big n) in
    match diff >. m, n = 1 with
      | true, true -> !store
      | true, false -> aux (i+1) 1
      | false, _ ->
	  if is_pent sum && is_pent diff then
	    store := diff :: !store;
	  aux i (n+1)
  in aux 1 1
    
let main =
  print_endline @@ "Answer: " ^ string_of_big_int @@ hd
  @@ sort compare_big_int @@ solve @@ big 5500000

ここからがいよいよ証明

続きを読む