以下使用memoization的函数示例显示在this page:
memoized_fib :: Int -> Integer
memoized_fib = (map fib [0..] !!)
where fib 0 = 0
fib 1 = 1
fib n = memoized_fib (n-2) + memoized_fib (n-1)
但是,如果我们想要记住多参数功能怎么办?例如,我们可以创建一个'乘法斐波那契',它将被定义为f(m,n) = m*f(m,n-2) + m*f(m,n-1)
。我修改了上面的代码,用于“乘法斐波那契”函数,如下所示:
mult_fib :: Integer -> Int -> Integer
mult_fib mult = (map (m_fib mult) [0..] !!)
where m_fib _ 0 = 0
m_fib _ 1 = 1
m_fib m n = m*(mult_fib m (n-2)) + m*(mult_fib m (n-1))
修改后的函数的运行时是指数的,即使原始是线性的。为什么这种技术在第二种情况下不起作用?另外,如何修改函数以使用memoization(不使用库函数)?
答案 0 :(得分:6)
如Vitus所说,在你的例子中可以很简单地完成。正确实施这一想法:
multFib :: Integer -> Int -> Integer
multFib mult = multFib'
where multFib' = (map m_fib [0..] !!)
m_fib 0 = 0
m_fib 1 = 1
m_fib n = mult * (multFib' $ n-2) + mult * (multFib' $ n-1)
然而,备忘录并不像你的例子那样强大:它只会用于优化单个函数调用,但结果列表通常不会在multFib
的后续调用之间存储。相同的mult
参数。
要实现这一点,您需要在两个参数上索引memmentation查找,如下所示:
multFibFullMemo :: Int -> Int -> Integer
multFibFullMemo = \mult n -> memo_table !! mult !! n
where memo_table = [ [m_fib mult' n' | n'<-[0..]] | mult' <- [0..] ]
m_fib _ 0 = 0
m_fib _ 1 = 1
m_fib mult n = m * (multFibFullMemo mult $ n-2) + m * (multFibFullMemo mult $ n-1)
where m = toInteger mult
很明显,如果您打算将它用于更大的mult
参数,这将无效:它总是需要跳到具有该数字长度的列表。
诸如MemoTrie之类的图书馆提供了没有这些问题的更复杂的备忘录。
答案 1 :(得分:1)
以下是使用MemoTrie的版本:
import Data.MemoTrie
multFib :: Integer -> Int -> Integer
multFib = memo2 multFib'
where multFib' m 0 = 0
multFib' m 1 = 1
multFib' m n = m * (multFib m (n-2)) + m * (multFib m (n-1))