Problem14(2) -BigInt-
30分プログラム、その279。Problem14 3n+1問題の続き。
昨日のプログラムにPrintfをしこんで追いかけてみると、一部でオーバーフローが発生しているため値が収束しなくなっていた。
そこで、OCamlの任意精度整数ライブラリ、BigIntモジュールを使ってみた。ただ、BigIntは
# (big_int_of_int 0) = (big_int_of_int 0);; Exception: Invalid_argument "equal: abstract value".
のように=で比較できないので、Hashtblに突っこむことができない。そこでファンクターを使って、BigInt専用のハッシュテーブルを作った。
こんなにがんばったのに、BigIntじゃ遅すぎて解けなかった。やっぱりintの範囲を越えたときだけBigIntを使うようにする、みたいな仕組みが必要なんだろうな。OCamlをやめてPythonとかにすれば解決する問題な気もする。
使い方
$ ocamlopt nums.cmxa problem14-bigint.ml -o p14 $ ./p14 ...
ソースコード
open Big_int (* util *) let ($) f g x = f (g x) let (@@) f g = f g (* big-int syntax sugar *) let (/@) x y = div_big_int x y let ( *@ ) x y = mult_int_big_int x y let (%@) x y = mod_big_int x y let (=@) x y = eq_big_int x y let (+@) x y = add_int_big_int y x (* sub function *) let even n = (n %@ (big_int_of_int 2)) =@ (big_int_of_int 0) (* BigInt Hash *) module BigIntHash : Hashtbl.HashedType with type t = big_int = struct type t = big_int let equal x y =x =@ y let hash x = Hashtbl.hash x end module Hash = (Hashtbl.Make(BigIntHash) : Hashtbl.S with type key = big_int) let table = Hash.create 10 (* collatz *) let collatz n = let rec loop n r = if Hash.mem table n then begin (Hash.find table n) + r end else if n =@ (big_int_of_int 1) then 1+r else if even n then loop (n /@ (big_int_of_int 2)) @@ 1+r else loop (3 *@ n +@ 1) @@ 1+r in let _ = Printf.printf "call %d\n" n in let _ = flush stdout in let n' = big_int_of_int n in let r = loop n' 0 in Hash.replace table n' r; r let range a b = let rec f r a b = if a = b then r else f (a::r) (a+1) b in f [] a b let solve _ = let f ((_,count) as x) m = let count' = collatz m in if count < count' then (m,count') else x in List.fold_left f (0,0) @@ range 1 1_000_000;; let _ = let (x,y) = solve () in Printf.printf "%d,%d\n" x y