【问题标题】:Memoization / dynamic programming in Haskell on 2 or 3 argumentsHaskell 中基于 2 或 3 个参数的记忆/动态编程
【发布时间】:2014-02-19 15:59:12
【问题描述】:

这是一个简单的 Haskell 记忆函数 f1 接受一个参数(是的,斐波那契):

f1 = [calc n | n <- [0..]]
     where calc 0 = 0
           calc 1 = 1
           calc n = f1 !! (n-1) + f1 !! (n-2)

现在,对于接受 2 个参数的函数 f2 或接受 3 个参数的函数 f3,如何做到这一点?

对于 f2,最好的方法是列表吗?或者可以使用不同的数据结构吗?

f2 = [[calc n m | n <- [0..]] | m <- [0..]]
     where calc 0 0 = 0
           calc a b = // ...something using (f2 !! a !! b)

对于f3 a b c,鉴于max_a * max_b * max_c 是可管理的,这种记忆/动态编程将如何工作?

我正在寻找最简单/最直接的方法,尽可能使用标准 Haskell 库。

编辑

正如 Chris Taylor 的回答所建议的那样,我尝试使用 MemoCombinators.hs v0.5.1 但它对我来说失败了,如下所示:

Could not find module `Data.IntTrie'
Use -v to see a list of the files searched for.

Illegal symbol '.' in type
Perhaps you intended -XRankNTypes or similar flag
to enable explicit-forall syntax: forall <tvs>. <type>

我需要它在“普通”haskell 中运行,这个版本:GHCi, version 7.6.3

有什么小窍门吗?

【问题讨论】:

    标签: haskell dynamic-programming memoization


    【解决方案1】:

    我可以想到两种方法-

    1。备忘录组合器

    创建通用记忆函数的最简单方法可能是使用data-memocombinators 库。假设您有以下两个参数函数。

    f :: Int -> Int -> Integer
    f 0 _ = 1
    f _ 0 = 1
    f a b = f (a-1) b + f a (b-1)
    

    您可以尝试拨打f 20 20,但请准备好稍等片刻。您可以使用

    轻松编写一个记忆版本
    import Data.MemoCombinators
    
    f :: Int -> Int -> Integer
    f = memo2 integral integral f'
      where
        f' 0 _ = 1
        f' _ 0 = 1
        f' a b = f (a-1) b + f a (b-1)
    

    请注意,在辅助函数f' 中,递归调用不是f' 而是到记忆化的函数f,这一点很重要。呼叫f 20 20 现在几乎立即返回。

    2。列表列表...

    如果您知道函数的参数是Int,并且您需要使用所有0..n0..m 来计算f (n+1) (m+1),那么使用列表方法可能是有意义的。但是,请注意,这与函数参数的数量有很大关系(特别是,如果您有超过 2 个参数,则很难一眼看出函数在做什么)。

    flist :: [[Integer]]
    flist = [[f' n m | m <- [0..]] | n <- [0..]]
     where
      f' _ 0 = 1
      f' 0 _ = 1
      f' a b = flist !! (a-1) !! b + flist !! a !! (b-1)
    
    f :: Int -> Int -> Integer
    f a b = flist !! a !! b
    

    【讨论】:

    • 嗨,克里斯,非常感谢您的回答,我想我需要使用选项 1。并从 data-memocombinators 包中提取最少的代码,看起来我只需要 type Memo还有memo2memo3,听起来对吗?
    • @vikingsteve 您还需要来自 MemoCombinators 的integral。我会看看你的错误。看起来您没有 data-inttrie 库。如果您运行cabal install data-memocombinators,它应该会自动安装,但您也可以尝试cabal install data-inttrie 来获取它。这有帮助吗?
    • @vikingsteve 请注意,data-memocombinators 使用两个扩展,Rank2Types 和 ScopedTypeVariables (package description here)。如果您不想依赖使用扩展的包,那么这将不合适。但是,您不需要在自己的模块中启用这些扩展,因此您可能可以接受。
    • 谢谢克里斯。我想我会去阅读data-inttrie库(它似乎是一些超高效的按位数据结构,也许我可以用Data.Map做类似的事情?)然后使用type Memowrap,@987654348 @ 和 memo2 拥有我需要的自定义“无外部库”实现。
    • @vikingsteve 你也应该看看this page on memoization。我使用Data.Map 进行记忆的经验是,很难让它用于具有超过 2 个参数的函数,因为我无法让地图变得足够懒惰。但也许你会有更好的运气!
    【解决方案2】:

    因为 Haskell 是惰性的,你可以通过调用它自己来记忆一个函数。

    例如,Haskell 中的一个斐波那契生成器是这样的:

    fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
    

    (来自http://www.haskell.org/haskellwiki/The_Fibonacci_sequence

    其中,使用结果列表作为自己的状态存储。

    【讨论】:

    • 好的,谢谢。我可以将这种惰性记忆与具有 2 个或 3 个参数并返回一个 Int 的函数一起使用吗?
    猜你喜欢
    • 2013-05-11
    • 2011-07-29
    • 1970-01-01
    • 2013-08-20
    • 1970-01-01
    • 2021-10-01
    • 1970-01-01
    • 2016-03-30
    相关资源
    最近更新 更多