[英]What's the most efficient way to memoize in Haskell?
What's the fastest way to memoize a recursive function in Haskell? 在Haskell中记忆递归函数的最快方法是什么?
Background: Recently I have been solving Project Euler problems in Haskell. 背景:最近我一直在Haskell中解决Project Euler问题。 Many require many computations of a recursively defined combinatorial or number-theoretical function, for example the Fibonacci numbers.
许多需要许多递归定义的组合或数理论函数的计算,例如斐波那契数。 Performance improves dramatically if such functions are memoized, that is, the results of the function are cached for later use.
如果这些函数被记忆,性能会大大提高,也就是说,函数的结果会被缓存以供以后使用。
I have seen many solutions to this problem. 我已经看到很多解决这个问题的方法。 The most elegant seems to be this.
最优雅的似乎就是这个。 One uses Data.IntMap (or hash tables) and the State monad.
一个使用Data.IntMap(或哈希表)和State monad。 A tree based solution suggested in this answer , and such solutions seem fairly common.
在这个答案中提出了一个基于树的解决方案,这种解决方案似乎很常见。 To give another example, see this blog post .
再举一个例子,请参阅此博客文章 。 I've seen other solutions using built-in functions.
我见过其他使用内置函数的解决方案。 There's one in section 2 here with the
fix
, and further it seems that the compiler can sometimes be massaged into memoizing without additional work. 还有一个在第2节在这里与
fix
,并进一步似乎编译器有时可以按摩到memoizing无需额外的工作。 There's also several prebuilt solutions . 还有一些预建解决方案 。
I'm wondering which memoization method is fastest in practice for the kinds of functions used in Project Euler. 我想知道哪种memoization方法在Project Euler中使用的各种函数的实践中最快。 My intuition says the hashtables library is, since hash tables seem to be the preferred dictionary structure in imperative languages.
我的直觉说哈希表库是,因为哈希表似乎是命令式语言中首选的字典结构。 The purely functional tree solutions are cool, but my Googling tells me they're strictly worse than hash tables in terms of asymptotic performance.
纯粹的功能树解决方案很酷,但我的谷歌搜索告诉我,它们在渐近性能方面严格比哈希表差。
A few comments said this question is too broad to answer, and upon reflection I agree. 一些评论说这个问题太宽泛而无法回答,经过反思我同意。 So let me give two concrete examples of functions to memoize: a function that computes the n'th Fibonacci number recursively, and one that computes the Catalan numbers recursively.
因此,让我给出两个具体的memoize函数示例:一个递归计算第n个Fibonacci数的函数,以及一个递归计算加泰罗尼亚数的函数。 I would like to compute these functions many times for large n.
我想为大n计算这些函数很多次。
I'm aware there are explicit formulas for these, but let's ignore that, because the real point here is to use them to benchmark memoization techniques. 我知道这些有明确的公式,但让我们忽略它,因为这里的真正要点是使用它们来记录备忘录技术。
When Trying to find the nth fibonacci number the only numbers that you need to memoize are the two previous numbers. 当试图找到第n个斐波那契数时,你需要记住的唯一数字是前两个数字。 you can do it as a tuple like (f n-1, fn ) and on each loop update this tuple.
你可以像(f n-1,fn)这样的元组,并在每个循环上更新这个元组。 note that updating the tuple is done through pointer manipulation and is not computationally expensive.
请注意,更新元组是通过指针操作完成的,并且计算成本不高。
a cleaner and slightly smarter alternative would be: 一个更清洁,更智能的替代方案是:
fibs :: [Integer]
fibs = fibcreator 0 1
where
fibcreator a b = a : fibcreator b (a+b)
nth = take n fibs
But one of the best algorithms that I have seen is this: 但我见过的最好的算法之一是:
Now what is great here is that in order to get 17 fibonacci number we can do 现在最棒的是,为了获得17个斐波纳契数,我们可以做到
m' = ((((m^2)^2)^2)^2) * m
This significantly reduces the computaion time and passively embeds the memoization within the algorithm. 这显着减少了计算时间并被动地将记忆嵌入算法中。 The punchline is that Haskell already uses this algorithm to compute the power function, so you don't need to implement it.
重点是Haskell已经使用这种算法来计算幂函数,因此您不需要实现它。 the full implementation is :
完整的实施是:
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.