Haskell和其他函数式编程语言是在不维护状态的前提下构建的。我还不熟悉函数式编程的工作原理和概念,所以我想知道是否有可能以FP方式实现DP算法。
可以使用哪些函数式编程结构来做到这一点?
答案 0 :(得分:17)
执行此操作的常见方法是通过延迟记忆。在某种意义上,递归的斐波纳契函数可以被认为是动态编程,因为它计算重叠子问题的结果。我意识到这是一个疲惫的例子,但这是一种品味。它使用data-memocombinators库进行延迟记忆。
import qualified Data.MemoCombinators as Memo
fib = Memo.integral fib'
where
fib' 0 = 0
fib' 1 = 1
fib' n = fib (n-1) + fib (n-2)
fib
是记忆版本,而fib'
只是“强力”解决问题,但使用记忆的fib
计算其子问题。其他DP算法使用不同的备忘录结构以相同的风格编写,但同样的想法是以简单的功能方式计算结果并进行记忆。
编辑:我终于放弃并决定提供一个可记忆的类型类。这意味着现在便于记忆:
import Data.MemoCombinators.Class (memoize)
fib = memoize fib'
where
fib' :: Integer -> Integer -- but type sig now required
...
需要遵循类型的Instaead,你可以memoize
任何东西。如果你愿意,你仍然可以使用旧方法。
答案 1 :(得分:16)
答案 2 :(得分:10)
如果要使用带有2个或3个参数的DP(例如,处理字符串时),可以使用不可变数组:
import Data.Array.IArray
answer :: String -> Int
answer s = table ! (1, l)
where
l = length s
--signatyres are needed, because GHC doesn't know what kind of Array we need
--string is stored in Array because we need quick access to individual chars
a :: Array Int Char
a = listArray (1, l) s
table :: Array (Int, Int) Int
table = listArray ((1, 1), (l, l)) [f i j | i <- [1..l], j <- [1..l]]
f i j | i > j = 0
| i == j = 1
| (a ! i) == (a ! j) = 2 + table ! (i+1, j-1)
| otherwise = maximum [table ! (i+1, j), table ! (i, j-1)]
此代码解决了以下任务:给定一个字符串S,找到最大长度为S的子序列,这将是一个palyndrome(子序列不需要连续)。
基本上,'f'是resursive函数,array'table'是所有可能值的矩阵。因为Haskell是惰性的,所以只需要计算“f”的答案值。换句话说,这是带有memoization的递归。所以使用Data.Memocombinators,它们是相同的,但已经由其他人编写:)
答案 3 :(得分:7)
由于懒惰,haskell中的动态编程可以优雅地表达,请参阅this page上的第一个示例
答案 4 :(得分:1)
动态编程算法通常利用将问题简化为更简单的问题的想法。它的问题可以表述为一些基本事实(比如,从方形单元到自身的最短路径长度为0)加上一组循环规则,它们准确地显示了如何减少问题“从单元格中找到最短路径{{1 }} (i,j)
“ to problem ”查找从单元格(0,0)
,(i-1,j)
到(i,j-1)
的最短路径;选择最佳“ 。 AFAIK这可以很容易地在功能风格的程序中表达出来;没有涉及的州。