繁体   English   中英

在Haskell中记忆的最有效方法是什么?

[英]What's the most efficient way to memoize in Haskell?

在Haskell中记忆递归函数的最快方法是什么?

背景:最近我一直在Haskell中解决Project Euler问题。 许多需要许多递归定义的组合或数理论函数的计算,例如斐波那契数。 如果这些函数被记忆,性能会大大提高,也就是说,函数的结果会被缓存以供以后使用。

我已经看到很多解决这个问题的方法。 最优雅的似乎就是这个。 一个使用Data.IntMap(或哈希表)和State monad。 这个答案中提出了一个基于树的解决方案,这种解决方案似乎很常见。 再举一个例子,请参阅此博客文章 我见过其他使用内置函数的解决方案。 还有一个在第2节在这里fix ,并进一步似乎编译器有时可以按摩到memoizing无需额外的工作。 还有一些预建解决方案

我想知道哪种memoization方法在Project Euler中使用的各种函数的实践中最快。 我的直觉说哈希表库是,因为哈希表似乎是命令式语言中首选的字典结构。 纯粹的功能树解决方案很酷,但我的谷歌搜索告诉我,它们在渐近性能方面严格比哈希表差。

编辑

一些评论说这个问题太宽泛而无法回答,经过反思我同意。 因此,让我给出两个具体的memoize函数示例:一个递归计算第n个Fibonacci数的函数,以及一个递归计算加泰罗尼亚数的函数。 我想为大n计算这些函数很多次。

我知道这些有明确的公式,但让我们忽略它,因为这里的真正要点是使用它们来记录备忘录技术。

当试图找到第n个斐波那契数时,你需要记住的唯一数字是前两个数字。 你可以像(f n-1,fn)这样的元组,并在每个循环上更新这个元组。 请注意,更新元组是通过指针操作完成的,并且计算成本不高。

一个更清洁,更智能的替代方案是:

fibs :: [Integer]
fibs = fibcreator 0 1
  where
    fibcreator a b = a : fibcreator b (a+b)

nth = take n fibs

但我见过的最好的算法之一是:

  1. 我们定义一个矩阵m = [e11 = 1,e12 = 1,e21 = 1,e22 = 0]
  2. 得到第n个斐波那契数,我们计算m'= m ^(n-1)
  3. 矩阵m'中的e11元素是第n个斐波纳契数

现在最棒的是,为了获得17个斐波纳契数,我们可以做到

m' = ((((m^2)^2)^2)^2) * m

这显着减少了计算时间并被动地将记忆嵌入算法中。 重点是Haskell已经使用这种算法来计算幂函数,因此您不需要实现它。 完整的实施是:

data Matrix = Matrix Integer Integer Integer Integer

instance Num Matrix where
  (*) (Matrix a11 a12 a21 a22) (Matrix b11 b12 b21 b22)
   = Matrix (a11*b11 + a12*b21) (a11*b12 + a12*b22) (a21*b11 + a22*b21) (a21*b12 + a22*b22)

fib4 :: Integer -> Integer
fib4 0 = 0
fib4 n = x
  where
    (Matrix x _ _ _) = Matrix 1 1 1 0 ^ (n-1)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM