Самый эффективный способ вычислить расстояние Левенштейна

В StudentTest есть много ошибок (например, нет точек с запятой для окончаний), но конкретный вопрос OP касался класса main.

Класс Student имеет main, определяемый как:

private static void main(String[] args) {

. По мере появления ошибки подпись должна быть общедоступной , поэтому ее следует изменить на:

[111 ]

решит эту конкретную проблему. Однако в других местах есть и другие проблемы, такие как конструктор:

   //This is the Constructor of the 
public Student(String name) {
    this.forName = forName;
}

Поскольку параметр равен name, это должно быть:

this.forName = name;

(или изменить имя параметра, либо одно).

23
задан The Guy with The Hat 30 May 2018 в 20:55
поделиться

5 ответов

В статье википедии о расстоянии Левенштейна есть полезные предложения по оптимизации вычислений - наиболее применимым в вашем случае является то, что если вы можете поставить границу k на максимальном интересующем расстоянии (все, что выходит за пределы этого, может быть бесконечностью!), Вы можете уменьшить вычисление до O (n умножить на k) вместо O (n в квадрате) (в основном отказом, как только минимально возможное расстояние станет > k ).

Поскольку вы ищете наиболее близкое соответствие, вы можете постепенно уменьшать k до расстояния до наилучшего найденного на данный момент совпадения - это не повлияет на поведение в худшем случае (поскольку совпадения может быть в порядке убывания расстояния, что означает, что вы никогда не выручитесь раньше), но средний случай должен улучшиться.

Я считаю, что, если вам нужно получить существенно лучшую производительность, вам, возможно, придется пойти на какой-то сильный компромисс, который вычисляет более приблизительное расстояние (и, таким образом, получает «достаточно хорошее совпадение», а не обязательно оптимальный).

23
ответ дан 29 November 2019 в 02:11
поделиться

Я модифицировал VBA-функцию расстояния Левенштейна, найденную на этом посте , чтобы использовать одномерный массив. Это работает намного быстрее.

'Calculate the Levenshtein Distance between two strings (the number of insertions,
'deletions, and substitutions needed to transform the first string into the second)

Public Function LevenshteinDistance2(ByRef s1 As String, ByRef s2 As String) As Long
Dim L1 As Long, L2 As Long, D() As Long, LD As Long 'Length of input strings and distance matrix
Dim i As Long, j As Long, ss2 As Long, ssL As Long, cost As Long 'loop counters, loop step, loop start, and cost of substitution for current letter
Dim cI As Long, cD As Long, cS As Long 'cost of next Insertion, Deletion and Substitution
Dim L1p1 As Long, L1p2 As Long 'Length of S1 + 1, Length of S1 + 2

L1 = Len(s1): L2 = Len(s2)
L1p1 = L1 + 1
L1p2 = L1 + 2
LD = (((L1 + 1) * (L2 + 1))) - 1
ReDim D(0 To LD)
ss2 = L1 + 1

For i = 0 To L1 Step 1: D(i) = i: Next i                'setup array positions 0,1,2,3,4,...
For j = 0 To LD Step ss2: D(j) = j / ss2: Next j        'setup array positions 0,1,2,3,4,...

For j = 1 To L2
    ssL = (L1 + 1) * j
    For i = (ssL + 1) To (ssL + L1)
        If Mid$(s1, i Mod ssL, 1) <> Mid$(s2, j, 1) Then cost = 1 Else cost = 0
        cI = D(i - 1) + 1
        cD = D(i - L1p1) + 1
        cS = D(i - L1p2) + cost

        If cI <= cD Then 'Insertion or Substitution
            If cI <= cS Then D(i) = cI Else D(i) = cS
        Else 'Deletion or Substitution
            If cD <= cS Then D(i) = cD Else D(i) = cS
        End If
    Next i
Next j

LevenshteinDistance2 = D(LD)
End Function

Я протестировал эту функцию со строкой 's1' длиной 11,304 и 's2' длиной 5665 (> 64 миллиона сравнений символов). При использовании вышеуказанной версии функции с одним измерением время выполнения на моей машине составляет ~ 24 секунды. Исходная двумерная функция, на которую я ссылался в приведенной выше ссылке, требует ~ 37 секунд для тех же строк. Я дополнительно оптимизировал одномерную функцию, как показано ниже, и для тех же строк требуется ~ 10 секунд.

'Calculate the Levenshtein Distance between two strings (the number of insertions,
'deletions, and substitutions needed to transform the first string into the second)
Public Function LevenshteinDistance(ByRef s1 As String, ByRef s2 As String) As Long
Dim L1 As Long, L2 As Long, D() As Long, LD As Long         'Length of input strings and distance matrix
Dim i As Long, j As Long, ss2 As Long                       'loop counters, loop step
Dim ssL As Long, cost As Long                               'loop start, and cost of substitution for current letter
Dim cI As Long, cD As Long, cS As Long                      'cost of next Insertion, Deletion and Substitution
Dim L1p1 As Long, L1p2 As Long                              'Length of S1 + 1, Length of S1 + 2
Dim sss1() As String, sss2() As String                      'Character arrays for string S1 & S2

L1 = Len(s1): L2 = Len(s2)
L1p1 = L1 + 1
L1p2 = L1 + 2
LD = (((L1 + 1) * (L2 + 1))) - 1
ReDim D(0 To LD)
ss2 = L1 + 1

For i = 0 To L1 Step 1: D(i) = i: Next i                    'setup array positions 0,1,2,3,4,...
For j = 0 To LD Step ss2: D(j) = j / ss2: Next j            'setup array positions 0,1,2,3,4,...

ReDim sss1(1 To L1)                                         'Size character array S1
ReDim sss2(1 To L2)                                         'Size character array S2
For i = 1 To L1 Step 1: sss1(i) = Mid$(s1, i, 1): Next i    'Fill S1 character array
For i = 1 To L2 Step 1: sss2(i) = Mid$(s2, i, 1): Next i    'Fill S2 character array

For j = 1 To L2
    ssL = (L1 + 1) * j
    For i = (ssL + 1) To (ssL + L1)
        If sss1(i Mod ssL) <> sss2(j) Then cost = 1 Else cost = 0
        cI = D(i - 1) + 1
        cD = D(i - L1p1) + 1
        cS = D(i - L1p2) + cost
        If cI <= cD Then 'Insertion or Substitution
            If cI <= cS Then D(i) = cI Else D(i) = cS
        Else 'Deletion or Substitution
            If cD <= cS Then D(i) = cD Else D(i) = cS
        End If
    Next i
Next j

LevenshteinDistance = D(LD)
End Function
3
ответ дан 29 November 2019 в 02:11
поделиться

Commons-lang имеет довольно быструю реализацию. См. http://web.archive.org/web/20120526085419/http://www.merriampark.com/ldjava.htm .

Вот мой перевод этого в Scala:

// The code below is based on code from the Apache Commons lang project.
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with this
 * work for additional information regarding copyright ownership. The ASF
 * licenses this file to You under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
/**
* assert(levenshtein("algorithm", "altruistic")==6)
* assert(levenshtein("1638452297", "444488444")==9)
* assert(levenshtein("", "") == 0)
* assert(levenshtein("", "a") == 1)
* assert(levenshtein("aaapppp", "") == 7)
* assert(levenshtein("frog", "fog") == 1)
* assert(levenshtein("fly", "ant") == 3)
* assert(levenshtein("elephant", "hippo") == 7)
* assert(levenshtein("hippo", "elephant") == 7)
* assert(levenshtein("hippo", "zzzzzzzz") == 8)
* assert(levenshtein("hello", "hallo") == 1)
*
*/
def levenshtein(s: CharSequence, t: CharSequence, max: Int = Int.MaxValue) = {
import scala.annotation.tailrec
def impl(s: CharSequence, t: CharSequence, n: Int, m: Int) = {
  // Inside impl n <= m!
  val p = new Array[Int](n + 1) // 'previous' cost array, horizontally
  val d = new Array[Int](n + 1) // cost array, horizontally

  @tailrec def fillP(i: Int) {
    p(i) = i
    if (i < n) fillP(i + 1)
  }
  fillP(0)

  @tailrec def eachJ(j: Int, t_j: Char, d: Array[Int], p: Array[Int]): Int = {
    d(0) = j
    @tailrec def eachI(i: Int) {
      val a = d(i - 1) + 1
      val b = p(i) + 1
      d(i) = if (a < b) a else {
        val c = if (s.charAt(i - 1) == t_j) p(i - 1) else p(i - 1) + 1
        if (b < c) b else c
      }
      if (i < n)
        eachI(i + 1)
    }
    eachI(1)

    if (j < m)
      eachJ(j + 1, t.charAt(j), p, d)
    else
      d(n)
  }
  eachJ(1, t.charAt(0), d, p)
}

val n = s.length
val m = t.length
if (n == 0) m else if (m == 0) n else {
  if (n > m) impl(t, s, m, n) else impl(s, t, n, m)
}

}

2
ответ дан 29 November 2019 в 02:11
поделиться

Согласно комментарию к этому блогу, Ускорение Левенштейна , вы можете использовать VP-Trees и достичь O (nlogn). Другой комментарий к тому же блогу указывает на Python-реализацию VP-Trees и Levenshtein . Сообщите нам, работает ли это.

7
ответ дан 29 November 2019 в 02:11
поделиться

В статье Википедии обсуждается ваш алгоритм и различные улучшения. Однако, похоже, что, по крайней мере в общем случае, O(n^2) - это лучшее, что вы можете получить.

Однако есть некоторые улучшения, если вы можете ограничить вашу задачу (например, если вас интересует только расстояние, если оно меньше d, сложность будет O(dn) - это может иметь смысл, так как совпадение, расстояние которого близко к длине строки, вероятно, не очень интересно). Попробуйте использовать специфику вашей проблемы...

2
ответ дан 29 November 2019 в 02:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: