【问题标题】:Using Data.Memocombinators to implement edit distance algorithm使用 Data.Memocombinators 实现编辑距离算法
【发布时间】:2011-03-26 23:28:16
【问题描述】:

假设我想为Levensthein distance(编辑距离)实现通常的动态编程算法。想出递归很容易:

editDistance [] ys = length ys
editDistance xs [] = length xs
editDistance (x:xs) (y:ys) 
  | x == y = editDistance xs ys
  | otherwise = minimum [
      1 + editDistance xs (y:ys),
      1 + editDistance (x:xs) ys,
      1 + editDistance xs ys]

这会受到指数运行时间的影响,因此有必要记住该函数。我想通过使用 Data.Memocombinators 来做到这一点,并且我尝试了几种方法。这是我目前的尝试:

import qualified Data.MemoCombinators as M

memLength str = 
  M.wrap 
    (\i -> drop i str) 
    (\xs -> length str - length xs)
    (M.arrayRange (0,length str))

elm xs ys = (M.memo2 memListx memListy editDistance) xs ys
  where
    memListx = memLength xs
    memListy = memLength ys

然而,记忆化似乎没有任何效果,尽管我希望任何记忆化都能显着改善运行时间,因为它至少是多项式的。我的实施有什么问题?如何在尽可能多地保留编辑距离的高级定义的同时获得良好的运行时间?

【问题讨论】:

    标签: haskell memoization levenshtein-distance


    【解决方案1】:

    如果您发布的代码实际上是您正在执行的操作,那么 您做错了! :-)。如果你要记忆一个递归函数,你需要将递归版本的调用回调到记忆的版本中。例如:

    editDistance (x:xs) (y:ys)
      | x == y = elm xs ys
      | ...
    

    否则,您将执行完整的递归计算并仅存储最终结果。您需要存储中间结果。

    这里还有一个问题。 elm 的备忘录表不应该依赖于它的参数(理想情况下你甚至不应该提到参数,所以你不依赖编译器足够聪明来找出依赖关系)。如果备忘录表依赖于参数,那么您必须为每个不同的参数构建一个新表,并且我们需要为所有参数共享一个表。你可以尝试一些愚蠢的事情,比如记住论点的整个结构:

    elm = M.memo2 (M.list M.char) (M.list M.char)
    

    看看这是否加快了速度(结合前一个技巧)。然后,您可以继续尝试仅使用长度而不是整个列表结构来获得额外的提升。

    希望有所帮助。

    【讨论】:

    • 你不知道吗,这实际上是问题所在,现在看起来很明显=D。
    猜你喜欢
    • 2015-12-09
    • 2013-10-13
    • 1970-01-01
    • 2016-11-21
    • 2014-11-29
    • 2012-11-27
    • 1970-01-01
    • 1970-01-01
    • 2015-03-01
    相关资源
    最近更新 更多