В информатике алгоритм Вагнера–Фишера — это динамический алгоритм программирования , который вычисляет расстояние редактирования между двумя строками символов.
Алгоритм Вагнера-Фишера имеет историю множественных изобретений . Наварро перечисляет следующих его изобретателей с указанием даты публикации и признает, что список неполный: [1] : 43
Алгоритм Вагнера-Фишера вычисляет расстояние редактирования на основе наблюдения, что если мы зарезервируем матрицу для хранения расстояний редактирования между всеми префиксами первой строки и всеми префиксами второй, то мы сможем вычислить значения в матрице путем ее заполнения и, таким образом, найти расстояние между двумя полными строками как последнее вычисленное значение.
Простая реализация в виде псевдокода для функции Distance , которая принимает две строки, s длины m и t длины n , и возвращает расстояние Левенштейна между ними, выглядит следующим образом. Входные строки имеют единичный индекс, а матрица d имеет нулевой индекс и [i..k]
является замкнутым диапазоном.
function Distance ( char s [ 1 .. m ] , char t [ 1 .. n ]) : // для всех i и j, d[i,j] будет содержать расстояние между // первыми i символами s и первыми j символами t // обратите внимание, что d имеет (m+1)*(n+1) значений declare int d [ 0 .. m , 0 .. n ] устанавливает каждый элемент в d в ноль // исходные префиксы можно преобразовать в пустую строку, // отбросив все символы for i от 1 до m : d [ i , 0 ] := i // целевые префиксы можно получить из пустого исходного префикса , // вставив каждый символ for j от 1 до n : d [ 0 , j ] := j for j от 1 до n : for i от 1 до m : if s [ i ] = t [ j ] : substitutionCost := 0 else : стоимость замены := 1 d [ i , j ] := minimum ( d [ i - 1 , j ] + 1 , // удаление d [ i , j - 1 ] + 1 , // вставка d [ i - 1 , j - 1 ] + substitutionCost ) // подстановка return d [ m , n ]
Два примера результирующей матрицы (при наведении курсора на подчеркнутое число отображается операция, выполненная для получения этого числа):
|
|
Инвариант , поддерживаемый на протяжении всего алгоритма, заключается в том, что мы можем преобразовать начальный сегмент s[1..i]
в t[1..j]
с помощью минимума d[i,j]
операций. В конце нижний правый элемент массива содержит ответ.
Как упоминалось ранее, инвариант заключается в том, что мы можем преобразовать начальный сегмент s[1..i]
в t[1..j]
с использованием минимума d[i,j]
операций. Этот инвариант выполняется, поскольку:
s[1..i]
может быть преобразовано в пустую строку t[1..0]
, просто отбрасывая все i
символы. Аналогично, мы можем преобразовать s[1..0]
в t[1..j]
, просто добавляя все j
символы.s[i] = t[j]
, и мы можем преобразовать s[1..i-1]
в t[1..j-1]
с помощью k
операций, то мы можем сделать то же самое с s[1..i]
и просто оставить последний символ в покое, задав k
операции.s[1..i]
в операции, то мы можем просто добавить их впоследствии, чтобы получить t[1..j-1]
операции ( вставку).k
t[j]
t[1..j]
k+1
s[1..i-1]
за t[1..j]
операций k
, то мы можем удалить s[i]
, а затем выполнить то же самое преобразование, всего за k+1
операций (удаление).s[1..i-1]
в t[1..j-1]
за k
операций, то мы можем сделать то же самое с s[1..i]
и впоследствии обменять исходное число s[i]
на t[j]
за всего за k+1
операций (подстановка).s[1..n]
в t[1..m]
, конечно же, равно числу, необходимому для преобразования всех из s
во все из t
, и поэтому d[n,m]
мы получаем наш результат.Это доказательство не подтверждает, что число, помещенное в , d[i,j]
на самом деле является минимальным; это сложнее доказать, и оно включает в себя аргумент от противного, в котором мы предполагаем, d[i,j]
что меньше минимального из трех, и используем это, чтобы показать, что одно из трех не является минимальным.
Возможные модификации этого алгоритма включают:
j
.[0,1]
.cost
значения можно вычислять параллельно, и алгоритм можно адаптировать для выполнения minimum
функции поэтапно, чтобы устранить зависимости.Инициализируя первую строку матрицы нулями, мы получаем вариант алгоритма Вагнера–Фишера, который можно использовать для нечеткого поиска строки в тексте. [1] Эта модификация дает конечную позицию совпадающих подстрок текста. Чтобы определить начальную позицию совпадающих подстрок, количество вставок и удалений можно хранить отдельно и использовать для вычисления начальной позиции из конечной позиции. [4]
Полученный алгоритм ни в коем случае не является эффективным, но на момент своей публикации (1980) он был одним из первых алгоритмов, выполнявших приближенный поиск. [1]