我最近开始在Scala中解决Project Euler问题,但是当我遇到问题14时,我得到了StackOverflowError,所以我在F#中重写了我的解决方案,因为(我被告知)F#编译器,不像Scala(生成Java)字节码),将递归调用转换为循环。 因此,我的问题是,在达到113000以上的某个数字之后,下面的代码怎么可能抛出StackOverflowException?我认为递归不一定是 tail 递归才能被翻译/优化,是吗? 我尝试了几次代码重写,但没有成功。我真的不想用循环编写命令式的代码,我认为我不能将 len 函数转为尾递归,即使这是防止问题它从优化。
module Problem14 =
let lenMap = Dictionary<'int,'int>()
let next n =
if n % 2 = 0 then n/2
else 3*n+1
let memoize(num:int, lng:int):int =
lenMap.[num]<-lng
lng
let rec len(num:int):int =
match num with
| 1 -> 1
| _ when lenMap.ContainsKey(num) -> lenMap.[num]
| _ -> memoize(num, 1+(len (next num)))
let cand = seq{ for i in 999999 .. -1 .. 1 -> i}
let tuples = cand |> Seq.map(fun n -> (n, len(n)))
let Result = tuples |> Seq.maxBy(fun n -> snd n) |> fst
注意:我知道下面的代码远非最佳,而且几行可能更简单,但我对F#并不十分精通,并且没有费心寻找简化它并使其更优雅的方法(尚未)。
谢谢。
答案 0 :(得分:2)
如果我将所有int
更改为int64
并在每个数字文字后附加L
(例如-1L
),那么您当前的代码会正常运行并获得正确的结果。如果实际问题是你溢出了32位整数,我不确定为什么会得到StackOverflowException。
module Problem14 =
let lenMap = System.Collections.Generic.Dictionary<_,_>()
let next n =
if n % 2L = 0L then n/2L
else 3L*n+1L
let memoize(num, lng) =
lenMap.[num]<-lng
lng
let rec len num =
match num with
| 1L -> 1L
| _ when lenMap.ContainsKey(num) -> lenMap.[num]
| _ -> memoize(num, 1L+(len (next num)))
let cand = seq{ for i in 999999L .. -1L .. 1L -> i}
let tuples = cand |> Seq.map(fun n -> (n, len(n)))
let Result = tuples |> Seq.maxBy(fun n -> snd n) |> fst