Ocaml中的递归 - 输出意外结果

时间:2016-10-26 20:52:09

标签: recursion ocaml

let n = read_int();;


let ftp = Hashtbl.create 1;;

let rec perrin n = 
   match n with
      0 -> 3
     |1 -> 0
     |2 -> 2
     |_ -> if Hashtbl.mem ftp n 
             then Hashtbl.find ftp n
             else
                begin 
                    Hashtbl.add ftp n (perrin (n-2) + perrin (n-3));
                    Hashtbl.find ftp  n
                 end;;


print_int (perrin n);;
print_newline ();;

该功能适用​​于小数字。但是对于大数字开始在结果中返回负数。有谁知道如何解决这个问题? 例如:

perrin 6443;;

输出:返回意外结果

1 个答案:

答案 0 :(得分:3)

简而言之,这是因为整数溢出。 perrin编号6443非常大,不适合标准的OCaml表示。您可以切换到int64类型,但很快就会达到最大值。如果您想计算任意长度的perrin数,那么您应该切换到一些提供任意大数字的库,例如Zarith

以下是相同算法的示例,它使用任意精度数计算perrin数(使用Zarith库):

 let ftp = Hashtbl.create 1

  let (+) = Z.add

  let rec perrin n =
    match n with
    | 0 -> Z.of_int 3
    | 1 -> Z.of_int 0
    | 2 -> Z.of_int 2
    |_ -> if Hashtbl.mem ftp n
      then Hashtbl.find ftp n
      else
        begin
          Hashtbl.add ftp n (perrin (n-2) + perrin (n-3));
          Hashtbl.find ftp  n
        end

以下是结果:

# #install_printer Z.pp_print;;
# perrin 6443;;
- : Z.t =
6937727487481534145345362428447384488478299624972546803624695551910667531554047522814387413304226129434527926499509496229770899828053746244703038130158033495659756925642507460705324476565619563726313143585381473818236243926914534542432440183345586679670347146768666345957457035004600496858722149019370892348066080092386227405747647480490430105430719428536606680584617305233160609609912020683184996768739606851007812320606992975981778299643926692143069608878875765580902743031572791438636355138605019665803104979890697923714757674707178907100143056837109943637042907642787339851137110850937972239227931113199614637067827389939915715964263895232644082473556841869600234790536494644702234455771939854947229042244627157330814752633389708917381476591438570001576028511405244641287078061574227
# 

您可能会注意到这个数字确实很大,并且没有机会适应32位甚至64位。实际上,它需要2614位:

# Z.numbits (perrin 6443);;
- : int = 2614

如果您不想安装zarith库并添加额外的依赖项,那么您可以使用OCaml builtin Big_int模块获取任意精度数字。以下是基于Big_int模块的实现:

  open Big_int

  let ftp = Hashtbl.create 1

  let (+) = add_big_int

  let rec perrin n =
    match n with
    | 0 -> big_int_of_int 3
    | 1 -> big_int_of_int 0
    | 2 -> big_int_of_int 2
    |_ -> if Hashtbl.mem ftp n
      then Hashtbl.find ftp n
      else
        begin
          Hashtbl.add ftp n (perrin (n-2) + perrin (n-3));
          Hashtbl.find ftp  n
        end;;