Оптимизация версии алгоритма Левенштейна для damerau лучше, чем O (n * m) - PullRequest
4 голосов
/ 28 марта 2012

Вот алгоритм (в рубине)

#http://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
  def self.dameraulevenshtein(seq1, seq2)
      oneago = nil
      thisrow = (1..seq2.size).to_a + [0]
      seq1.size.times do |x|
          twoago, oneago, thisrow = oneago, thisrow, [0] * seq2.size + [x + 1]
          seq2.size.times do |y|
              delcost = oneago[y] + 1
              addcost = thisrow[y - 1] + 1
              subcost = oneago[y - 1] + ((seq1[x] != seq2[y]) ? 1 : 0)
              thisrow[y] = [delcost, addcost, subcost].min
              if (x > 0 and y > 0 and seq1[x] == seq2[y-1] and seq1[x-1] == seq2[y] and seq1[x] != seq2[y])
                  thisrow[y] = [thisrow[y], twoago[y-2] + 1].min
              end
          end
      end
      return thisrow[seq2.size - 1]
  end

Моя проблема заключается в том, что при использовании seq1 длиной 780 и seq2 длины 7238 для запуска на ноутбуке i7 требуется около 25 секунд.В идеале, я хотел бы сократить это примерно до секунды, так как он работает как часть веб-приложения.

Я обнаружил, что есть способ оптимизировать расстояние ванильного Левенштейна , напримерчто время выполнения уменьшается с O (n * m) до O (n + d ^ 2), где n - длина более длинной строки, а d - расстояние редактирования.Итак, мой вопрос: можно ли применить ту же оптимизацию к имеющейся у меня версии damerau (см. Выше)?

1 Ответ

0 голосов
/ 05 мая 2012

Да, оптимизация может быть применена к версии damereau. Вот код haskell для этого (я не знаю Ruby):

distd :: Eq a => [a] -> [a] -> Int
distd a b
    = last (if lab == 0 then mainDiag
            else if lab > 0 then lowers !! (lab - 1)
                 else{- < 0 -}   uppers !! (-1 - lab))
    where mainDiag = oneDiag a b (head uppers) (-1 : head lowers)
          uppers = eachDiag a b (mainDiag : uppers) -- upper diagonals
          lowers = eachDiag b a (mainDiag : lowers) -- lower diagonals
          eachDiag a [] diags = []
          eachDiag a (bch:bs) (lastDiag:diags) = oneDiag a bs nextDiag lastDiag : eachDiag a bs diags
              where nextDiag = head (tail diags)
          oneDiag a b diagAbove diagBelow = thisdiag
              where doDiag [_] b nw n w = []
                    doDiag a [_] nw n w = []
                    doDiag (apr:ach:as) (bpr:bch:bs) nw n w = me : (doDiag (ach:as) (bch:bs) me (tail n) (tail w))
                        where me = if ach == bch then nw else if ach == bpr && bch == apr then nw else 1 + min3 (head w) nw (head n)
                    firstelt = 1 + head diagBelow
                    thisdiag = firstelt : doDiag a b firstelt diagAbove (tail diagBelow)
          lab = length a - length b
          min3 x y z = if x < y then x else min y z

distance :: [Char] -> [Char] -> Int
distance a b = distd ('0':a) ('0':b)

Код выше является адаптацией этого кода .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...