【问题标题】:Haskell: effective computation of recursively-defined function valuesHaskell:递归定义函数值的有效计算
【发布时间】:2012-05-24 20:23:30
【问题描述】:

我想计算递归定义的函数值r(i,j),由

定义
r i j  | i<0 || j<0   = 0
       | i==0 && j==0 = 1
       | otherwise    = (i-1) * r (i-2) j + r (i-1) (j-1)

显然,这些系数的NxN 表可以在O(N^2) 中计算。 不幸的是,直接的评估,就像

[[r i j | j <-[0..50]]| i <- [0..50]]

以极其无效的方式执行(指数复杂度)。显然,Haskell 为每个 r i j 构建了整个递归树,并忽略了先前计算的 r (i-1) (j-1) 等的值。

计算这样一个表的优雅而有效的方法是什么?

【问题讨论】:

  • ...我不知道haskell,但似乎您想要动态编程。 (谷歌)
  • 你想要memoization,见haskell.org/haskellwiki/Memoization
  • 如果您只想要一个值,而不是整个表,您可能想要计算递归系列的生成函数。我不是这方面的专家,但我曾经在关于离散数学/组合学的讲座中听说过。也许 math.stackexchange.com 的小伙伴们能够在这方面为您提供帮助。我与这种数学相关的词是“本影演算”、“gosper 算法”和“Zeilberger 算法”。也许有帮助。

标签: algorithm haskell functional-programming complexity-theory recurrence


【解决方案1】:

正如 FUZxxl 所说,这是一个记忆问题。

r i j | i < 0 || j < 0 = 0
      | otherwise      = rss !! i !! j

rss = [[r' i j | j <- [0..]] | i <- [0..]]
  where r' 0 0 = 1
        r' i j = (i-1) * r (i-2) j + r (i-1) (j-1)

如果您需要精确到 50 的值的显式表,您可以使用 take 51 (map (take 51) rss)[[r i j | j &lt;-[0..50]]| i &lt;- [0..50]],正如您所提到的。否则,您可以直接致电r 或参考rss

【讨论】:

  • 这仍然比它可能的慢,因为访问将是 O(i+j)。如果您将自己限制为给定的大小,则数组更适合(惰性数组,未拆箱)O(1) 访问,如果您想要无限制,则 trie of trie (IntMap) 仍应比列表更快列表。
  • 如果你有品位,讨厌数组库 API,那么请改用 vector。
猜你喜欢
  • 2020-05-08
  • 2015-07-28
  • 2019-06-10
  • 1970-01-01
  • 2020-03-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-14
  • 2019-02-28
相关资源
最近更新 更多