Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Recursion 带有递归调用的F#System.OutOfMemoryException_Recursion_F#_Out Of Memory_Tail Recursion - Fatal编程技术网

Recursion 带有递归调用的F#System.OutOfMemoryException

Recursion 带有递归调用的F#System.OutOfMemoryException,recursion,f#,out-of-memory,tail-recursion,Recursion,F#,Out Of Memory,Tail Recursion,这实际上是在F#中投影Euler的一个解决方案。但是,在尝试计算较大数字的迭代序列时,遇到System.OutOfMemory异常。如您所见,我正在编写带有尾部调用的递归函数 我遇到了StackOverFlowException的问题,因为我正在visual studio中调试(这会禁用尾部调用)。我已经记录下来了。在这里,我是在发布模式下运行的——但当我作为控制台应用程序(在内存为4gb的windows xp上)运行时,内存会出现异常 我真的无法理解我是如何将自己编码到这个内存溢出中的&希望有

这实际上是在F#中投影Euler的一个解决方案。但是,在尝试计算较大数字的迭代序列时,遇到System.OutOfMemory异常。如您所见,我正在编写带有尾部调用的递归函数

我遇到了StackOverFlowException的问题,因为我正在visual studio中调试(这会禁用尾部调用)。我已经记录下来了。在这里,我是在发布模式下运行的——但当我作为控制台应用程序(在内存为4gb的windows xp上)运行时,内存会出现异常

我真的无法理解我是如何将自己编码到这个内存溢出中的&希望有人能用我的方式告诉我这个错误

let E14_interativeSequence x =

  let rec calc acc startNum =
    match startNum with
    | d when d = 1      -> List.rev (d::acc)
    | e when e%2 = 0    -> calc (e::acc) (e/2)
    | _                 -> calc (startNum::acc) (startNum * 3 + 1)

  let maxNum pl=

    let rec maxPairInternal acc pairList =
        match pairList with
        | []        ->  acc
        | x::xs     ->  if (snd x) > (snd acc) then maxPairInternal x xs
                        else maxPairInternal acc xs

    maxPairInternal (0,0) pl
    |> fst

  // if I lower this to like [2..99999] it will work.
  [2..99999] 
  |> List.map (fun n -> (n,(calc [] n)))
  |> List.map (fun pair -> ((fst pair), (List.length (snd pair))))
  |> maxNum
  |> (fun x-> Console.WriteLine(x))

编辑

根据答案给出的建议,我重写了使用惰性列表和Int64

#r "FSharp.PowerPack.dll"

let E14_interativeSequence =

  let rec calc acc startNum =
    match startNum with
    | d when d = 1L         -> List.rev (d::acc) |> List.toSeq
    | e when e%2L = 0L      -> calc (e::acc) (e/2L)
    | _                     -> calc (startNum::acc) (startNum * 3L + 1L)

  let maxNum (lazyPairs:LazyList<System.Int64*System.Int64>) =

    let rec maxPairInternal acc (pairs:seq<System.Int64*System.Int64>) =
        match pairs with
        | :? LazyList<System.Int64*System.Int64> as p ->
            match p with
            | LazyList.Cons(x,xs)->  if (snd x) > (snd acc) then maxPairInternal x xs
                                     else maxPairInternal acc xs
            | _                         ->  acc
        | _ -> failwith("not a lazylist of pairs")

    maxPairInternal (0L,0L) lazyPairs
    |> fst

  {2L..999999L}
  |> Seq.map (fun n -> (n,(calc [] n)))
  |> Seq.map (fun pair -> ((fst pair), (Convert.ToInt64(Seq.length (snd pair)))))
  |> LazyList.ofSeq
  |> maxNum
#r“FSharp.PowerPack.dll”
让E14_交互活动序列=
让rec calc acc startNum重新计算=
将startNum与
|当d=1L->List.rev(d::acc)|>List.toSeq时的d
|e%2L=0L时的e->计算(e::acc)(e/2L)
|>计算(起点::acc)(起点*3L+1L)
let maxNum(懒散对:懒散列表)=
let rec maxPairInternal acc(成对:序号)=
配对
| :? 懒散列表为p->
配p
|Cons(x,xs)->如果(sndx)>(sndacc)那么maxPairInternal x xs
else maxPairInternal acc xs
|->acc
|->failwith(“不是懒散的成对列表”)
最大内部(0升,0升)懒散对
|>fst
{2L..999999 L}
|>序列图(乐趣n->(n,(计算[]n)))
|>Seq.map(有趣的配对->((fst配对),(转换为64(Seq.length(snd配对'))))
|>懒散的
|>最大值

这就解决了问题。不过,我还要看看尹朱的解决方案,它更好。

如果您将List.map更改为Seq.map(并重新使用maxPairInternal对Seq进行迭代),这可能会有所帮助。现在,在处理整个结构以获得单个数字结果之前,您将在一个巨大的结构中同时显示所有数据。通过Seq惰性地执行此操作要好得多,只需创建一行,并将其与下一行进行比较,然后一次创建一行,然后丢弃它


我现在没有时间编写我的建议,但是如果您仍然有问题,请告诉我,我将重新讨论。

正如Brian所提到的,
列表。*
操作在这里不合适。它们占用了太多内存

stackoverflow问题来自另一个地方。堆栈溢出有两种可能:
calc
maxPairInternal
。它必须是第一个,因为第二个深度与第一个深度相同。然后问题就出现在数字上,
3n+1
问题中的数字很容易变得非常大。首先是int32溢出,然后是stackoverflow。这就是原因。将数字更改为64位后,程序开始工作

,在那里你可以看到一个记忆技巧

open System
let E14_interativeSequence x =

  let rec calc acc startNum =
    match startNum with
    | d when d = 1L      -> List.rev (d::acc)
    | e when e%2L = 0L    -> calc (e::acc) (e/2L)
    | _                 -> calc (startNum::acc) (startNum * 3L + 1L)

  let maxNum pl=

    let rec maxPairInternal acc pairList =
        match pairList with
        | []        ->  acc
        | x::xs     ->  if (snd x) > (snd acc) then maxPairInternal x xs
                        else maxPairInternal acc xs

    maxPairInternal (0L,0) pl
    |> fst

  // if I lower this to like [2..99999] it will work.
  [2L..1000000L] 
  |> Seq.map (fun n -> (n,(calc [] n)))
  |> Seq.maxBy (fun (n, lst) -> List.length lst)
  |> (fun x-> Console.WriteLine(x))

不要到处使用列表,这不是Haskell!停止到处写
fst-pair
snd-pair
,这不是Lisp

如果您想在F#中找到一个简单的解决方案,您可以直接这样做,而无需创建任何中间数据结构:

let rec f = function
  | 1L -> 0
  | n when n % 2L = 0L -> 1 + f(n / 2L)
  | n -> 1 + f(3L * n + 1L)

let rec g (li, i) = function
  | 1L -> i
  | n -> g (max (li, i) (f n, n)) (n - 1L)

let euler14 n = g (0, 1L) n
我的上网本大约需要15秒。如果您想要更省时的结果,请通过数组重用以前的结果:

let rec inside (a : _ array) n =
  if n <= 1L || a.[int n] > 0s then a.[int n] else
    let p =
      if n &&& 1L = 0L then inside a (n >>> 1) else
        let n = 3L*n + 1L
        if n < int64 a.Length then inside a n else outside a n
    a.[int n] <- 1s + p
    1s + p
and outside (a : _ array) n =
  let n = if n &&& 1L = 0L then n >>> 1 else 3L*n + 1L
  1s + if n < int64 a.Length then inside a n else outside a n

let euler14 n =
  let a = Array.create (n+1) 0s
  let a = Array.Parallel.init (n+1) (fun n -> inside a (int64 n))
  let i = Array.findIndex (Array.reduce max a |> (=)) a
  i, a.[i]
让rec进入(a:uu数组)n=
如果为n0s,则为[int n]else
让p=
如果n&&1L=0L,则在a(n>>>1)内,否则
设n=3L*n+1L
如果n>1个其他3L*n+1L
1s+如果na内部(int64n))
设i=Array.findIndex(Array.reduce max a |>(=)a
i、 a[我]

这在我的上网本上大约需要0.2秒。

在查找Microsoft.FSharp.Core.Operators.Checked时发现了这个。 我只是在学习F#,所以我想我应该接受Euler 14项目的挑战

它使用递归,但不使用尾部递归。 对我来说大约需要3.1秒,但有一个优势,我几乎可以理解它

let Collatz (n:int64) = if n % 2L = 0L then n / 2L else n * 3L + 1L

let rec CollatzLength (current:int64) (acc:int) =
    match current with 
    | 1L -> acc
    | _ -> CollatzLength (Collatz current) (acc + 1)

let collatzSeq (max:int64) = 
    seq{
        for i in 1L..max do
            yield i, CollatzLength i 0
    }

let collatz = Seq.toList(collatzSeq 1000000L)

let result, steps = List.maxBy snd collatz

正如@Brian指出的,Seq更适合这个问题。事实上,通过研究ProjectEuler中的前45个问题,我发现几乎所有问题都最适合基于Seq的解决方案。如果您感兴趣,下面是我对问题14的解决方案:(当然,您可能希望等到您的计算结果令人满意后再进行比较,或者您可能对算法感到满意,但希望看到基于Seq的实现的外观)。警告,除了OutOfMemoryException之外,你的解决方案至少还有一个问题,如果你看我的解决方案,这个问题可能会被破坏。我注意到OP代码中存在int32溢出,但没有连接到内存不足异常;当我在自己的解决方案中遇到同样的缺陷时,它导致了不终止,因为我的策略不涉及实际构建collatz链,而只是计算它们的长度。是的。我不会想到的@Kevin Won:如果您怀疑或想测试代码中是否发生整数溢出,请将
打开Microsoft.FSharp.Core.Operators.Checked添加到脚本中。这将用溢出时抛出的整数运算符替换整数运算符。它会使你的计算(稍微)变慢,所以当不再需要它时,别忘了删除它。@cfern:很好。我不知道。@jon:除了样式prefs之外,您反对列表和对函数的原因是什么?我试图理解你的观点&为什么你认为我的解决方案是一种滥用。虽然F#肯定不是Haskell或Lisp,但它肯定有一个双亲血统