- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试在 Haskell 中实现 levenshtein 距离(或编辑距离),但是当字符串长度增加时,它的性能会迅速下降。
我对 Haskell 还是很陌生,所以如果你能给我一些关于如何改进算法的建议,那就太好了。我已经尝试过“预先计算”值(初始化),但由于它没有改变任何东西,所以我恢复了那个改变。
我知道已经有 editDistance在 Hackage 上实现,但我需要它来处理任意 token 列表,不一定是字符串。另外,我觉得它有点复杂,至少与我的版本相比。
所以,这里是代码:
-- 两个列表之间的标准 levenshtein 距离
editDistance::Eq a => [a] -> [a] -> Int
编辑距离 s1 s2 = 编辑距离' 1 1 1 s1 s2
-- 加权 levenshtein 距离
-- ins、sub 和 del 是各种操作的成本
editDistance'::Eq a => Int -> Int -> Int -> [a] -> [a] -> Int
editDistance' _ _ ins s1 [] = ins * 长度 s1
编辑距离'_ _ ins [] s2 = ins * 长度s2
editDistance' del sub ins s1 s2
| last s1 == last s2 = editDistance' del sub ins (init s1) (init s2)
|否则 = 最小值 [editDistance' del sub ins s1 (init s2) + del -- 删除
, editDistance' del sub ins (init s1) (init s2) + sub -- 替换
, editDistance' del sub ins (init s1) s2 + ins -- 插入
]
这似乎是一个正确的实现,至少它给出了与 online tool 完全相同的结果.
在此先感谢您的帮助!如果您需要任何其他信息,请告诉我。
问候,
bzn
最佳答案
忽略这是一个糟糕的算法(应该是内存,我到了那一秒)......
使用 O(1) 原语而不是 O(n)
一个问题是您对列表使用了一大堆 O(n) 调用(haskell 列表是单链表)。更好的数据结构将为您提供 O(1) 操作,我使用 Vector :
import qualified Data.Vector as V
-- standard levenshtein distance between two lists
editDistance :: Eq a => [a] -> [a] -> Int
editDistance s1 s2 = editDistance' 1 1 1 (V.fromList s1) (V.fromList s2)
-- weighted levenshtein distance
-- ins, sub and del are the costs for the various operations
editDistance' :: Eq a => Int -> Int -> Int -> V.Vector a -> V.Vector a -> Int
editDistance' del sub ins s1 s2
| V.null s2 = ins * V.length s1
| V.null s1 = ins * V.length s2
| V.last s1 == V.last s2 = editDistance' del sub ins (V.init s1) (V.init s2)
| otherwise = minimum [ editDistance' del sub ins s1 (V.init s2) + del -- deletion
, editDistance' del sub ins (V.init s1) (V.init s2) + sub -- substitution
, editDistance' del sub ins (V.init s1) s2 + ins -- insertion
]
str2 = replicate 15 'a' ++ replicate 25 'b'
str1 = replicate 20 'a' ++ replicate 20 'b'
main = print $ editDistance str1 str2
editDistance
算法。
Ord a
),您基本上可以毫不费力地获得内存。编码:
import qualified Data.Vector as V
import Control.Monad.Memo
-- standard levenshtein distance between two lists
editDistance :: (Eq a, Ord a) => [a] -> [a] -> Int
editDistance s1 s2 = startEvalMemo $ editDistance' (1, 1, 1, (V.fromList s1), (V.fromList s2))
-- weighted levenshtein distance
-- ins, sub and del are the costs for the various operations
editDistance' :: (MonadMemo (Int, Int, Int, V.Vector a, V.Vector a) Int m, Eq a) => (Int, Int, Int, V.Vector a, V.Vector a) -> m Int
editDistance' (del, sub, ins, s1, s2)
| V.null s2 = return $ ins * V.length s1
| V.null s1 = return $ ins * V.length s2
| V.last s1 == V.last s2 = memo editDistance' (del, sub, ins, (V.init s1), (V.init s2))
| otherwise = do
r1 <- memo editDistance' (del, sub, ins, s1, (V.init s2))
r2 <- memo editDistance' (del, sub, ins, (V.init s1), (V.init s2))
r3 <- memo editDistance' (del, sub, ins, (V.init s1), s2)
return $ minimum [ r1 + del -- deletion
, r2 + sub -- substitution
, r3 + ins -- insertion
]
Int
。然后它只是使用“备忘录”功能即插即用您想要内存的值。
$ time ./so # the memoized vector version
12
real 0m0.003s
$ time ./so3 # the non-memoized vector version
12
real 1m33.122s
String
和
Vector
之间的差异在内存版本中没有那么大,但是当距离达到 200 左右时,仍然会增长到 2 倍,所以仍然值得。
[ editDistance' ... s1 (V.init s2) + del
, editDistance' ... (V.init s1) (V.init s2) + sub
, editDistance' ... (V.init s1) s2 + ins]
editDistance' s1 s2
的调用会导致对
editDistance'
的 3 次调用......每个调用
editDistance'
又调用了 3 次......又调用了 3 次......和 AHHH!指数爆炸!幸运的是,大多数电话都是相同的!例如(使用
-->
表示“通话”,使用
eD
表示
editDistance'
):
eD s1 s2 --> eD s1 (init s2) -- The parent
, eD (init s1) s2
, eD (init s1) (init s2)
eD (init s1) s2 --> eD (init s1) (init s2) -- The first "child"
, eD (init (init s1)) s2
, eD (init (init s1)) (init s2)
eD s1 (init s2) --> eD s1 (init (init s2))
, eD (init s1) (init s2)
, eD (init s1) (init (init s2))
ed (init s1) (init s2)
执行了 3 次。另一个 child 也与 parent 分享电话,所有 child 彼此分享许多电话(和他们的 child ,提示 Monty Python 短剧)。
runMemo
的函数来返回所使用的缓存结果的数量将是一个有趣的,也许是有启发性的练习。
关于performance - Haskell 中的编辑距离算法 - 性能调优,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5515025/
我是一名优秀的程序员,十分优秀!