Я играю с вычислением расстояний Левенштейна в Haskell и немного расстроен следующей производительностью проблема. Если вы реализуете это наиболее «обычным» способом для Haskell, как показано ниже (dist), все работает просто отлично:
dist :: (Ord a) => [a] -> [a] -> Int
dist s1 s2 = ldist s1 s2 (L.length s1, L.length s2)
ldist :: (Ord a) => [a] -> [a] -> (Int, Int) -> Int
ldist _ _ (0, 0) = 0
ldist _ _ (i, 0) = i
ldist _ _ (0, j) = j
ldist s1 s2 (i+1, j+1) = output
where output | (s1!!(i)) == (s2!!(j)) = ldist s1 s2 (i, j)
| otherwise = 1 + L.minimum [ldist s1 s2 (i, j)
, ldist s1 s2 (i+1, j)
, ldist s1 s2 (i, j+1)]
Но если немного напрячь мозг и реализовать его как dist', он будет работать НАМНОГО быстрее (примерно в 10 раз).
dist' :: (Ord a) => [a] -> [a] -> Int
dist' o1 o2 = (levenDist o1 o2 [[]])!!0!!0
levenDist :: (Ord a) => [a] -> [a] -> [[Int]] -> [[Int]]
levenDist s1 s2 arr@([[]]) = levenDist s1 s2 [[0]]
levenDist s1 s2 arr@([]:xs) = levenDist s1 s2 ([(L.length arr) -1]:xs)
levenDist s1 s2 arr@(x:xs) = let
n1 = L.length s1
n2 = L.length s2
n_i = L.length arr
n_j = L.length x
match | (s2!!(n_j-1) == s1!!(n_i-2)) = True | otherwise = False
minCost = if match then (xs!!0)!!(n2 - n_j + 1)
else L.minimum [(1 + (xs!!0)!!(n2 - n_j + 1))
, (1 + (xs!!0)!!(n2 - n_j + 0))
, (1 + (x!!0))
]
dist | (n_i > n1) && (n_j > n2) = arr
| n_j > n2 = []:arr `seq` levenDist s1 s2 $ []:arr
| n_i == 1 = (n_j:x):xs `seq` levenDist s1 s2 $ (n_j:x):xs
| otherwise = (minCost:x):xs `seq` levenDist s1 s2 $ (minCost:x):xs
in dist
Я испробовал все обычные seq
трюки в первой версии, но, похоже, ничего не ускоряет. Меня это немного не устраивает, потому что я ожидал, что первая версия будет быстрее, потому что ей не нужно оценивать всю матрицу, а только те ее части, которые ей нужны.
Кто-нибудь знает, можно ли заставить эти две реализации работать одинаково, или я просто пожинаю плоды оптимизации хвостовой рекурсии в последнем, и поэтому мне нужно жить с его нечитаемостью, если я хочу производительности?
Спасибо, Орион
!!
там, где этого можно избежать. В частности, каждоеsomeList !! 0
можно заменить наhead someList
. - person Antal Spector-Zabusky   schedule 30.09.2010someList !! 0
должно быть таким же, какhead someList
, ноsomeList !! bigNumber
— это O(bigNumber)? - person jdo   schedule 30.09.2010