Выравнивание данных - подробное объяснение алгоритма расстояния редактирования (расстояние Левенштейна)

алгоритм

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

Один: Введение

В теории информации, лингвистике и информатике расстояние Левенштейна — это строковая метрика, используемая для измерения разницы между двумя строками. Неформально расстояние Левенштейна между двумя словами — это наименьший шаг редактирования одного символа (вставка, удаление или замена), необходимый для замены одного слова другим.

Он назван в честь советского математика Владимира Левенштейна, предложившего алгоритм в 1965 году.

Расстояние Левенштейна также можно назвать расстоянием редактирования, хотя этот термин также может относиться к более широкому семейству показателей расстояния.

Расстояние Левенштейна тесно связано с попарным выравниванием строк.

Основной контент здесь мойLevenshtein distanceАнглийский перевод также добавил некоторые мои мысли~

Второе: определение алгоритма

1: Определение

Расстояние Левенштейна между двумя строками a и b определяется следующим образом:

在这里插入图片描述

в

在这里插入图片描述
Равен 0, когда ai = bj, 1 в противном случае,
在这里插入图片描述
Представляет расстояние от первых i байтов a до первых j байтов b.

Среди них относительно изменения строки b:

  • 在这里插入图片描述
    : удалить байт от имени a для соответствия b
  • 在这里插入图片描述
    : добавить байт от имени a для соответствия b
  • 在这里插入图片描述
    : означает совпадение или отсутствие совпадения, в зависимости от того, одинаковы ли все символы

2: маленький случай

Рассчитаем расстояние редактирования между котенком и сидящим

  • kиттен →sitten (замените «k» -> «s»)
  • sitteп → сидетьiп (заменить «е» -> «я»)
  • сидеть → сидетьg(вставьте «г»).

Количество шагов, необходимых для вышеуказанного процесса изменения, является минимальным количеством шагов, поэтому расстояние редактирования между ними равно «3».

3: Верхняя и нижняя границы алгоритма

Значение расстояния Левенштейна содержит несколько верхних и нижних границ

  • Минимальное расстояние — это разница в длине двух строк.
  • Расстояние не больше длины более длинной из двух строк.
  • длина 0 тогда и только тогда, когда строки одинаковы
  • Когда строки имеют одинаковую длину, максимальная длина расстояния равна расстоянию Хэмминга (описано ниже).
  • Расстояние между двумя строками меньше или равно сумме расстояний от другой строки (уравнение треугольника a+b

Расстояние Хэмминга состоит в том, чтобы сравнить две строки одинаковой длины с самого начала, чтобы увидеть, одинаковы ли значения соответствующих позиций символов двух строк, если они не совпадают, добавить 1 к расстоянию, а конечный результат результат - расстояние Хэмминга Например, расстояние между abcd и abhg равно 2, а расстояние между abcd и bcda равно 4.

Три: сценарии применения

1: выравнивание данных

Когда автор работает над связанным сетевым проектом, в фоновом режиме есть два вида специальных данных: адрес и компания.Эти два вида данных представляют собой данные, введенные пользователем, поэтому характерно то, что в нем может быть много разных строк. одно и то же место, например, одно и то же место.Одно местоположение: «ИТ-индустриальный парк, район Чаоян, Пекин», может быть ряд данных, таких как «ИТ-индустриальный парк, район Чаоян, Пекин» или «ИТ-парк, район Чаоян , Пекин» в фоновых данных, и мы не можем выполнять нечеткие запросы (поскольку узел Связь между данными и ребрами составляет десятки миллионов. Нечеткие запросы могут соответствовать большому количеству узлов и возвращать большое количество данных, что будет влияют на стабильность проекта.) Мы используем выравнивание данных для решения этой проблемы.Когда пользователь вводит адрес, мы с помощью алгоритма расстояния редактирования можем получить и отобразить другие соответствующие данные, и можно добиться лучшего эффекта. Конкретные этапы реализации здесь не представлены.

2: Орфографическая коррекция

Компания, в которой работает автор, имеет компонент исправления орфографических ошибок, предоставляемый компанией, а некоторые из них используют алгоритм расстояния редактирования. Вот краткое введение в компоненты:

Исправление ошибок в основном решает ситуацию с ошибками ввода в запросе, например, запрос пиньинь, запрос содержит омофонные опечатки или другие слова с другим звучанием, пропущенные слова и так далее. Это исправление ошибок в основном основано на двух правилах: исправление ошибок пиньинь и исправление ошибок расстояния редактирования. В основном в автономном режиме создаются два словаря, а именно словарь пиньинь и словарь расстояния редактирования. Исходный словарь в основном состоит из данных cmc, данных ячеек, данных верхнего запроса и белого списка. Создайте словарь пиньинь и отредактируйте словарь расстояний с помощью скрипта ****. После выполнения скрипта данные словаря будут сгенерированы в каталоге ***. Генерация словаря пиньинь в основном заключается в преобразовании слов исходного словаря в пиньинь, а генерация словаря расстояния редактирования в основном заключается в пропуске определенного слова или определенной буквы пиньинь. Код для создания словаря находится в разделе tool. Логика оперативного исправления ошибок Динамическую библиотеку в каталоге so можно сгенерировать, скомпилировав код с помощью make. Внешне предоставляется служба java RPC, связывающая библиотеку динамической компоновки С++ через java jni.

Основная логика исправления ошибок выглядит следующим образом: сначала разберите запрос и оцените, является ли он полным пиньинь или содержит китайский язык. Если это все пиньинь, он сразу перейдет к соответствующему исправлению ошибок пиньинь и результату отзыва.Если это не может быть решено с помощью пиньинь, перейдите к отзыву расстояния редактирования, чтобы решить ситуацию с пропущенными буквами; если это часть китайского или все китайские запросы, исправление пиньинь будет выполнено в первую очередь.Неправильно, решите проблему омофонических опечаток, если отзыва нет, то сначала выполните сегментацию слов и соединение соседних терминов вместе, чтобы вспомнить пиньинь и отредактировать расстояние.

Четыре: другие алгоритмы редактирования расстояния

Существует также множество популярных алгоритмов расстояния редактирования, которые отличаются от алгоритма расстояния Левенштейна тем, что используют разные методы преобразования строк.

  • Расстояние Дамерау-Левенштейна: можно вставлять, удалять, заменять и обмениваться между двумя соседними символами в строке.
  • расстояние до самой длинной общей подпоследовательности (LCS): разрешить только вставку, удаление и замену строк
  • Расстояние Хэмминга: позволяет заменять строки, может использоваться только для расчета расстояния редактирования двух строк одинаковой длины.
  • Расстояние Джаро: разрешен обмен только строками

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

Пятое: реализация алгоритма

1: Рекурсивная реализация

Реализация этого алгоритма относительно проста, то есть логика может быть выведена в соответствии с верхней и нижней границами, описанными выше.

//实现方法
private static int distance(String a, int len_a, String b, int len_b) {
    //递归回归点
    if (len_a == 0)
        return len_b;
    if (len_b == 0)
        return len_a;
    
    int cos;
    if (a.charAt(len_a-1) == b.charAt(len_b-1))
        cos = 0;
    else
        cos = 1;

    int re1 = distance(a, len_a - 1, b, len_b) + 1;
    int re2 = distance(a, len_a, b, len_b - 1) + 1;
    int re3 = distance(a, len_a - 1, b, len_b - 1) + cos;
    //返回在a中删除一个字符、在b中删除一个字符、ab中均删除一个字符获得结果中取最小值
    return re1 < re2 ? (re1 < re3 ? re1 : re3) : (re2 < re3 ? re2 : re3);
}
//测试
public static void main(String[] args) {
    String a = "kitten";
    String b = "sitting";
    int re = distnace(a, a.length(), b, b.length());
    System.out.println(re);
    //输出:3
}

Этот метод имеет высокую временную сложность и низкую эффективность, а также многократно вычисляет множество строк.Для реализации ниже используется алгоритм динамического программирования.

2: Реализация динамического программирования

Принципиальная часть алгоритма заимствована из https://www.cnblogs.com/sumuncle/p/5632032.html части статьи блоггера =.=

Обоснование алгоритма: предположим, что мы можем использовать d[ i , j ] шагов (которые можно хранить в двумерном массиве), представляя минимум, необходимый для преобразования строки s[ 1…i ] в строку t [ 1…j ]. количество шагов

Тогда в самом простом случае, то есть когда i равно 0, то есть строка s пуста, тогда соответствующее d[0,j] должно добавить j символов, так что s преобразуется в t, и когда j равно 0 , то есть строка t пуста, тогда соответствующее d[i,0] должно сократить i символов, чтобы s было преобразовано в t.

Затем рассмотрим общую ситуацию и добавим небольшое представление о динамическом программировании.Если мы хотим, чтобы s[1..i] преобразовывалось в t[1..j] после минимального количества добавлений, удалений или операции замены, то мы должны В прошлом ее можно было добавить, удалить или заменить с минимальным количеством операций, так что теперь для строки s и строки t достаточно сделать еще одну операцию или не завершить преобразование из s[1..i] в t[1..j] преобразовать. Так называемое «до» делится на следующие три ситуации:

  • 1) Мы можем преобразовать s[1…i] в t[1…j-1] за k операций
  • 2) Мы можем преобразовать s[1..i-1] в t[1..j] за k операций
  • 3) Мы можем преобразовать s[1…i-1] в t[1…j-1] за k шагов

В первом случае нам нужно только добавить s[1..i] к t[j] в конце, чтобы завершить сопоставление, поэтому всего требуется k+1 операций. Во втором случае нам нужно только удалить s[i] в ​​конце, а затем выполнить эти k операций, поэтому всего требуется k+1 операций. В третьем случае нам нужно всего лишь заменить s[i] на t[j] в конце, чтобы s[1..i] == t[1..j], что требует k+1 всего операций . А если в третьем случае s[i] точно равно t[j], то мы можем завершить процесс, используя всего k операций.

Наконец, чтобы гарантировать, что количество полученных операций всегда будет наименьшим, мы можем выбрать наименее затратную из трех вышеприведенных случаев, которая является наименьшей операцией, необходимой для преобразования s[1..i] в t[1.. j] частота. Основные шаги алгоритма:

  • (1) Построить матрицу с m+1 строками и n+1 столбцами для хранения количества операций, которые необходимо выполнить для завершения определенного преобразования, и преобразовать строку s[1..n] в строку t[1 …m ] Количество операций, которые необходимо выполнить, равно значению matrix[n][m];
  • (2) Первая строка матрицы инициализации — от 0 до n, а первый столбец — от 0 до m. Matrix[0][j] представляет значение в строке 1, столбце j-1. Это значение представляет собой количество операций, необходимых для преобразования строки s[1...0] в t[1..j]. Очевидно, что Преобразование пустой строки в строку длины j требует всего j операций сложения, поэтому значением matrix[0][j] должно быть j, и так далее для других значений.
  • (3) Проверить каждый символ s[i] от 1 до n;
  • (4) Проверить каждый символ s[i] от 1 до m;
  • (5) Сравнить попарно каждый символ строки s и строки t. Если они равны, пусть стоимость равна 0. Если они не равны, пусть стоимость равна 1 (эта стоимость будет использоваться позже);
  • (6)
    • А. Если мы можем преобразовать s[1..i-1] в t[1..j] за k операций, то мы можем удалить s[i], а затем выполнить эти k операций, так что всего k+1 требуются операции.
    • б) Если мы можем преобразовать s[1…i] в t[1…j-1] за k операций, то есть d[i,j-1]=k, то мы можем преобразовать t[j] плюс s[ 1..i], что в сумме требует k+1 операций.
    • в) Если мы можем преобразовать s[1...i-1] в t[1...j-1] за k шагов, то мы можем преобразовать s[i] в ​​t[j] таким образом, что s[ 1. .i] == t[1..j], что также требует всего k+1 операций. (Здесь добавляется стоимость, потому что если s[i] точно равно t[j], то нет необходимости делать операцию замены, ее можно удовлетворить, если не равно, нужно сделать еще одну операцию замены , то нужно k+ 1 операция)
    • Поскольку мы хотим получить минимальное количество операций, нам, наконец, нужно сравнить количество операций в этих трех случаях и взять минимальное значение в качестве значения d[i,j];
    • г. Затем повторите 3,4,5,6, и окончательный результат будет в d[n,m];
private static int distance(String a, String b) {
    int[][] dis = new int[a.length()+1][b.length()+1];
    for (int i = 1; i <= a.length(); i++)
        dis[i][0] = i;
    for (int j = 1; j <= b.length(); j++)
        dis[0][j] = j;
    int cas;
    for (int j = 1; j <= b.length(); j++) {
        for (int i = 1; i <= a.length(); i++) {
            if (a.charAt(i-1) == b.charAt(j-1))
                cas = 0;
            else
                cas = 1;
            int re = Math.min(dis[i - 1][j] + 1, dis[i][j - 1] + 1);
            dis[i][j] = Math.min(re, dis[i - 1][j - 1] + cas);
        }
    }
    return dis[a.length() - 1][b.length() - 1];
}

public static void main(String[] args) {
    String a = "kitten";
    String b = "sitting";
    int re = distance(a, b);
    System.out.println(re);
    //输出:3
}

Если вы перепечатаете этот пост в блоге, пожалуйста, прикрепите ссылку на эту статью, спасибо за сотрудничество~:Талант /user/407224…

Если вы чувствуете, что эта статья полезна для вас, пожалуйста, нажмите «Нравится» или «Подписаться» на блогера, ваши лайки и внимание будут для меня самой большой мотивацией двигаться вперед!