【问题标题】:How does this memoized fibonacci function work?这个记忆的斐波那契函数是如何工作的?
【发布时间】:2013-03-10 18:20:44
【问题描述】:

在我正在做的函数式编程课程的当前练习作业中,我们必须制作一个给定函数的记忆版本。为了解释memoization,给出下面的例子:

fiblist = [ fibm x | x <- [0..]]

fibm 0 = 0
fibm 1 = 1
fibm n = fiblist !! (n-1) + fiblist !! (n-2)

但我不完全理解这是如何工作的。

让我们打电话给fibm 3

fibm 3
--> fiblist !! 2 + fibList 1
--> [fibm 0, fibm 1, fibm 2] !! 2 + [fibm 0, fibm 1] !! 1
--> fibm 2 + fibm 1
--> (fiblist !! 1 + fiblist 0) + 1
--> ([fibm 0, fibm 1] !! 1 + [fibm 0] !! 0) + 1
--> (fibm 1 + fibm 0) + 1
--> 1 + 0 + 1
--> 2

从其他问题/答案和谷歌搜索中,我了解到不知何故,评估的 fiblist 在呼叫之间共享。

这是否意味着,例如,对于fiblist !! 2 + fiblist !! 1,列表值只为fiblist !! 2 计算一次,然后只为fiblist !! 1 重复使用?

那么这两个斐波那契数每次调用只计算一次,因此没有指数调用次数。但是fiblist 函数中调用的“较低”级别呢?他们如何从原始fibm 调用中计算出的fiblist 中受益?

【问题讨论】:

  • 一个相关的问题,当然已经在 S.O. - 现在看 - 是关于惰性评估。考虑if (f x) &gt; 0 then f x else 0,其中f x 是一些昂贵的函数调用。如果 if 条件为真,是否会重新计算 f x,还是会简单地重用该值?
  • 关于您的相关问题see this.
  • 啊,就是这样。谢谢!
  • 顺便说一句,即使列表已经被评估,fiblist !! n 也将是O(n),我相信第一次计算fibm n 会为每个i &lt; n 评估fiblist !! i,这是O(n^2)。你可以做得更好...
  • a 边界重复相关问题:stackoverflow.com/questions/11466284/….

标签: haskell fibonacci memoization


【解决方案1】:

这里的关键部分是列表是惰性求值的,这意味着该元素直到第一次被请求时才被计算。然而,一旦它被评估,它就在那里供其他任何东西查找。因此,在您的示例中,您说得对,这些值只为fiblist !! 2 计算一次,然后为fiblist !! 1 重复使用。

fiblist 函数的“较低级别”以相同的方式工作。我第一次调用fiblist !! 1 时,它将通过调用fibm 1 进行评估,这只是1,然后这个值将保留在列表中。当您尝试获取更高的斐波那契数时,fiblist 将调用fibm,它将在fiblist 的较低位置(可能已经评估)中查找这些值。

【讨论】:

    【解决方案2】:

    让我们逐步完成评估。除了显示当前表达式之外,我们还显示了内存中fiblist 的当前评估状态。在那里,我写&lt;expr&gt; 表示未计算的表达式(通常称为thunk),&gt;expr&lt; 表示当前正在计算的未计算表达式。您可以看到惰性评估的实际应用。该列表仅根据需要进行评估,完成的子计算将被共享以供将来重用。

       Current expression                       Current evaluation state of fiblist
    
       fibm 3                                   <[ fibm x | x <- [0..] ]>
    
    ->   (simple expansion of the definition)
    
       fiblist !! (3-1) + fiblist !! (3-2)      <[ fibm x | x <- [0..] ]>
    
    ->   ((+) has to evaluate both its arguments to make progress, let's assume
         it starts with the left argument; (!!) traverses the list up to the given
         element and returns the element it finds)
    
       fibm 2 + fiblist !! (3-2)                <fibm 0> : <fibm 1> : >fibm 2< : <[ fibm x | x <- [3..] ]>
    
    ->   (simple expansion of the definition)
    
       (fiblist !! (2-1) + fiblist !! (2-2)) + fiblist !! (3-2)
                                                <fibm 0> : <fibm 1> : >fibm 2< : <[ fibm x | x <- [3..] ]>
    
    ->   (we again start with the first argument to (+),
         computing the result of (!!) does not cause any
         further evaluation of fiblist)
    
       (fibm 1 + fiblist !! (2-2)) + fiblist !! (3-2)
                                                <fibm 0> : >fibm 1< : >fibm 2< : <[ fibm x | x <- [3..] ]>
    
    ->   (expanding fibm 1 returns a result immediately;
         this concludes the computation of fibm 1,
         and the thunk is updated with the result)
    
       (1 + fiblist !! (2-2)) + fiblist !! (3-2)
                                                <fibm 0> : 1 : >fibm 2< : <[ fibm x | x <- [3..] ]>
    
    ->   (now we compute fiblist !! (2-2))
    
       (1 + fibm 0) + fiblist !! (3-2)          >fibm 0< : 1 : >fibm 2< : <[ fibm x | x <- [3..] ]>
    
    ->   (expanding fibm 0 returns 0 immediately, and the
         corresponding thunk can be updated)
    
       (1 + 0) + fiblist !! (3-2)               0 : 1 : >fibm 2< : <[fibm x | x <- [3..] ]>
    
    ->   (we can compute the (+), yielding the result of
         fibm 2; the corresponding thunk is updated)
    
       1 + fiblist !! (3-2)                     0 : 1 : 1 : <[fibm x | x <- [3..] ]>
    
    ->   (now the right argument of (+) has to be evaluated, but (!!)
         will return the already evaluated list element directly)
    
       1 + 1                                    0 : 1 : 1 : <[fibm x | x <- [3..] ]>
    
    ->   (arithmetic; note that as we called fibm 3 directly in the
         beginning, instead of fiblist !! 3, the list is unaffected
         by this final result)
    
       2                                        0 : 1 : 1 : <[fibm x | x <- [3..] ]>
    

    由于 fiblist 是一个全局常量(通常称为“常量应用形式”的 CAF),列表的部分评估状态将持续存在,并且未来对 fibm 的调用将重用列表中已评估的元素。不过,列表最终会变得越来越大,消耗越来越多的内存。

    【讨论】:

      猜你喜欢
      • 2023-03-11
      • 2021-11-17
      • 2021-05-21
      • 1970-01-01
      • 2018-06-12
      • 2019-06-04
      • 2015-10-19
      • 2011-12-14
      • 2020-08-19
      相关资源
      最近更新 更多