【问题标题】:How to speed up (or memoize) a series of mutually recursive functions如何加速(或记忆)一系列相互递归的函数
【发布时间】:2012-04-25 11:46:12
【问题描述】:

我有一个程序可以生成一系列函数 fg,如下所示:

step (f,g) = (newF f g, newG f g)

newF f g x = r (f x) (g x)
newG f g x = s (f x) (g x)

foo = iterate step (f0,g0)

其中 r 和 s 是 f xg x 的一些无趣的函数。我天真地希望让foo 成为一个列表意味着当我调用第 n 个f 时,它不会重新计算第 (n-1) 个 f,如果它已经计算过了(如果fg 不是函数)。有没有办法在不将整个程序拆开的情况下记住这一点(例如,对所有相关参数进行评估 f0g0 然后向上工作)?

【问题讨论】:

  • 重复重新计算第 n 个 f 不会重复重新计算第 (n-1) 个 f... 但请记住,重新计算函数并不意味着重新计算带参数调用函数的结果!

标签: haskell memoization mutual-recursion


【解决方案1】:

您可能会发现Data.MemoCombinators 很有用(在 data-memocombinators 包中)。

你没有说你的 fg 采用什么参数类型 --- 如果它们都采用整数值,那么你会像这样使用它:

import qualified Data.MemoCombinators as Memo

foo = iterate step (Memo.integral f0, Memo.integral g0)

如果需要,您也可以记住每个步骤的输出

step (f,g) = (Memo.integral (newF f g), Memo.integral (newG f g))

我希望您不要认为这会破坏整个程序。


回复您的评论:

这是我能想到的最好的。它未经测试,但应该按照正确的方式工作。

我担心在DoubleRational 之间进行转换是不必要的低效——如果Double 有一个Bits 实例,我们可以改用Memo.bits。所以这最终可能对你没有任何实际用途。

import Control.Arrow ((&&&))
import Data.Ratio (numerator, denominator, (%))

memoV :: Memo.Memo a -> Memo.Memo (V a)
memoV m f = \(V x y z) -> table x y z
  where g x y z = f (V x y z)
        table = Memo.memo3 m m m g

memoRealFrac :: RealFrac a => Memo.Memo a
memoRealFrac f = Memo.wrap (fromRational . uncurry (%))
                           ((numerator &&& denominator) . toRational)
                           Memo.integral

另一种方法。

你有

step :: (V Double -> V Double, V Double -> V Double)
     -> (V Double -> V Double, V Double -> V Double)

你把它改成怎么样

step :: (V Double -> (V Double, V Double))
     -> (V Double -> (V Double, V Double))
step h x = (r fx gx, s fx gx)
  where (fx, gx) = h x

还有变化

foo = (fst . bar, snd . bar)
  where bar = iterate step (f0 &&& g0)

希望共享的fxgx 应该会加快速度。

【讨论】:

  • 感谢您的出色回答。我在这种方法中看到的唯一问题是 fg 的类型为 V Double -> V Double 其中 data V a = V a a a 并且我不清楚我将如何写一个“Memo.v”或者这是否可能。
【解决方案2】:

有什么方法可以在不将整个程序拆开的情况下记住这一点(例如,根据所有相关参数评估 f0 和 g0,然后向上工作)?

这可能是您所说的“将整个程序拆开”的意思,但这里有一个解决方案,其中(我相信但无法测试 ATM)fooX 可以共享。

nthFooOnX :: Integer -> Int -> (Integer, Integer)
nthFooOnX x = 
    let fooX = iterate step' (f0 x, g0 x)
     in \n-> fooX !! n

step' (fx,gx) = (r fx gx, s fx gx)

-- testing definitions:
r = (+)
s = (*)
f0 = (+1)
g0 = (+1)

我不知道这是否保留了您最初实施的精神。

【讨论】:

    猜你喜欢
    • 2014-10-31
    • 2012-11-26
    • 2021-10-23
    • 2010-09-20
    • 2022-12-03
    • 2012-04-01
    • 1970-01-01
    • 2019-08-02
    • 1970-01-01
    相关资源
    最近更新 更多