项目Euler#14尝试失败并出现StackOverflowException

时间:2010-12-29 23:14:23

标签: f# stack-overflow

我最近开始在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#并不十分精通,并且没有费心寻找简化它并使其更优雅的方法(尚未)。

谢谢。

1 个答案:

答案 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