Функция Левенштейна Насонов Денис Данная статья достаточно подробно рассматривает задачу неточного совпадения 2-х строк, обычно решаемую с помощью динамического программирования, с использованием меры похожести, известной, как функция Левенштейна1. В жизни нередко требуется найти различие, или расстояние между двумя цепочками символов. Наиболее распространенными примерами могут служить операции в текстовых базах данных, в эволюционных, структуральных и функциональных исследованиях биологических строк, в методах проверки правописания и т. д. Так что же такое неточное совпадение строк, или редакционное расстояние? формализации данного понятия. Самый Существует несколько способов распространенный из них основан на преобразованиях одной строки в другую серией операций редактирования, выполняемых над отдельными символами. Существуют 3 основные редакционные операции: удаление символа из строки, вставка символа в строку, замена символа в строке. Введем некоторые фундаментальные понятия. Определение: Оптимальным предписанием будем называть минимальное число операций, необходимых для преобразования строки S1 (Source) в строку S2 (Target). Определение: Редакционное предписание между 2-мя строками определяется как минимальное число редакционных операций, т.е. вставок, удалений, замен, необходимых для преобразования одной строки (S1) в другую (S2). Замечание: нужно отметить, что оптимальных (в том числе и редакционных) предписаний может быть больше одного. Прежде, чем приступить к формальному описанию и реализации задачи, рассмотрим некоторые области применения нашей функции. 1 Владимир Иосифович Левенштейн (родился в 1935 году) – видный российский ученый, доктор физикоматематических наук, окончил МГУ им. М.В. Ломоносова, Механико-математический факультет. В настоящее время работает ведущим научным сотрудником в Институте Прикладной Математики им. М.В. Келдыша. Благодаря введенному им в 1965 году понятию дистанции редактирования, названной его именем, доктор Левенштейн обрёл всемирную известность. ________________________________________________________________________________ © Насонов Денис © http://rain.ifmo.ru/cat 1 Правописание слов. Расстояние редактирования является базовым параметром, активно использующимся в различных грамматических приложениях; таких, к примеру, как правописание в MS Office, или в других подобных программных продуктах. Метод работы данного сервиса можно представить себе, рассмотрев следующий пример: Расстояние редактирования Источник Эталон Самовар самовар Самавар Самовар 0 (строки идентичны) 1 Самимавр Самовар 4 Здесь, при проверке на правильность написания некорректно введенного слова, служба осуществляет поиск по базовому словарю на неточное совпадение с «эталонами». Функция Левенштейна же играет роль фильтра, заведомо отбрасывающего неприемлемые варианты (у которых значение функции больше некоторой заданной константы). Похожий алгоритм применяется и в распознавании речи, и в молекулярной биологии (работа со структурой ДНК), и в “Fuzzy search”, используемом интернет- поисковыми системами (как более гибкий поиск). Схема проста: web-сервер осуществляет точный поиск по заданному выражению и, в случае неудовлетворительного результата, поиск повторяется с использованием неточного совпадения. Однако значительным минусом такого поиска является время его выполнения. Иногда, для большей наглядности, в редакционном предписании используют обозначения следующего вида: I – insert D – delete R – replace M – match Тогда для 2-х строк “ANALYSES” и “ANNULUSE” можно построить следующую таблицу преобразований: M I A A N M R M R M M D N A L Y S E S N U L U S E В итоге, можно не только подсчитать функцию Левенштейна, но также показать одно из преобразований. ________________________________________________________________________________ © Насонов Денис © http://rain.ifmo.ru/cat 2 Задача о редакционном расстоянии Теперь рассмотрим задачу вычисления функции Левенштейна и ее редакционного предписания с помощью динамического программирования. Основы динамического программирования, вероятно, хорошо известны читателю. Поэтому мы рассмотрим его специфическое приложение к задаче о редакционном расстоянии. Определение: Для двух строк S1 и S2 значение D(i, j) определим, как редакционное расстояние между S1[1..i] и S2[1..j]. Т.е. D(i, j) определяет минимальное число операций, необходимых для преобразо- вания первых i символов S1, в первые j символов S2. Но чтобы найти D(i, j), необходимо решить более общую задачу, а именно: вычислить D(i`, j`) для всех комбинаций i` и j`, где i` меняется от 0 до i, а j` от 0 до j, т. е. применить стандартный метод динамического программирования, содержащий три основных компоненты: рекуррентное соотношение, табличная форма вычислений и обратный проход. Поясним каждую компоненту. Рекуррентное соотношение Рекуррентное соотношение задает рекурсивное отношение между значением D(i, j) для положительных i и j и значениями D с индексными парами, меньшими i, j. Когда меньших индексов нет, значение D(i, j) должно получаться явно из условий, которые будем называть базой, т. е.: D(i, 0) = i, D(0, j) = j. Очевидно, что выше написанные выражения логически верны. Действительно, в первом случае, единственным способом получения из i символов S1 0 символов S2 является удаление этих i символов, аналогично и для D(0, j) только не удаление, а вставка. Теперь убедимся в корректности всей рекурсии благодаря следующим двум леммам, использующим концепцию редакционного предписания. Лемма 1. Значение D(i, j) должно совпадать с D(i-1, j) + 1 , D(i, j-1) + 1 или D(i-1, j1) + price(i, j), где price(i, j) = 0, если S1(i) = S2(j), и равно 1 - в противном случае. Других возможностей нет. ________________________________________________________________________________ © Насонов Денис © http://rain.ifmo.ru/cat 3 Доказательство: Рассмотрим редакционное предписание для преобразования S1[1..i] в S2[1..j] с минимальным числом редакционных операций и проанализируем последний символ этого предписания. По определению, он должен быть равен I, D, R или М. Рассмотрим каждый случай по порядку. Если это операция I, то на последнем шаге была произведена вставка символа S2(j) в конец (преобразуемой) первой строки. Но тогда символы в предписании до этого I должны обеспечивать минимальное число операций, преобразующих S1[1..i] в S2[1..j-1] (иначе такое представление S1[1..i] в S2[1..j] не являлось бы оптимальным). Однако, последнее преобразование требует D(i, j-1) редакционных операций и, если, на данном шаге, конечный символ в предписании равен I, то D(i, j) =D(i, j-1) + 1. Аналогично, если конечный символ в предписании равен D, то последняя редакционная операция была удалением S1(i), а, следовательно, символы в предписании, влево от этого D, должны задавать минимальное число редакционных операций для преобразования S1[1..i-1] в S2[1..j]. По определению, это последнее преобразование требует D(i–1, j) редакционных операций. Таким образом, если последний символ в предписании равен D, то D(i, j) = D(i-1, j) + 1. Теперь, пусть последний символ в предписании равен R, тогда последняя редакционная операция заменяет S1(i) на S2(j), а символы, влево от R, определяют минимальное число редакционных операций для преобразования S1[1 ..i-1] в S2[1..j-1]. В этом случае D(i, j) = D(i-1, j-1) + 1. Наконец, по аналогичным причинам, если последний символ в предписании равен М, то S1(i) = S2(j) и D(i, j) =D(i-1, j-1). Используя введенную переменную price(i, j), мы можем соединить два последних случая в один, т. е. для М price(i, j) = 0, и тогда (для M, R): D(i, j) = D(i-1, j-1) + price(i, j). Теперь докажем не менее важную лемму, которая также поможет в доказательстве основной теоремы. Лемма 2. D(i, j) ≤ min {D(i-1, j) + 1 , D(i, j-1) + 1, D(i-1, j-1) + price(i, j)}. Доказательство: Рассуждения в доказательстве будут очень похожими на предложенные рассуждения в предыдущей лемме, но их цель несколько отлична. Сейчас мы намерены конструктивно показать существование преобразований, на которых достигается каждое из трех значений, выписанных в неравенстве. И так как каждое из трех значений достижимо, их минимум также будет достижим. Во-первых, редакционных можно преобразовать операций. Нужно S1[1..i] просто в S2[1..j] преобразовать ровно за S1[1..i] в D(i, j-1) + S2[1..j-1] 1 за минимальное число операций и использовать еще одну операцию, чтобы вставить символ S2(j) в конце. По определению, число редакционных операций в этом конкретном способе ________________________________________________________________________________ © Насонов Денис © http://rain.ifmo.ru/cat 4 преобразования S1 в S2 равно D(i, j-1) + 1. Далее, можно преобразовать S1[1..i] в S2[1..j] ровно за D(i-1, j) + 1 операций: преобразовать S1[1..i-1] в S2[1..j] за наименьшее число операций и затем удалить S1(i), что и даст в итоге D(i-1, j-1) + 1. Очевидно, какие преобразования необходимы для D(i-1, j-1) + price(i, j) операций. Теорема. Рекуррентное отношение задано корректно и D(i, j) = min {D(i-1, j) + 1, D(i, j-1) + 1, D(i-1, j-1) + price(i, j)}, когда i и j > 0. Доказательство: Доказательство тривиально следует из лемм 1 и 2. Табличная форма вычислений Теперь поговорим об эффективном вычислении самого значения D(m, n). Естественно, можно напрямую запрограммировать рекуррентное соотношение в виде самой процедурной рекурсии и получить необходимый результат, так называемым, нисходящим методом. Однако, при достаточно больших значениях m, n число рекурсивных вызовов будет расти экспоненциально, хотя различных комбинаций существует только (n+1)*(m+1). Все дело в принципиальном подходе, а именно: отличия нисходящего и восходящего методов. При восходящих расчетах, на каждом шаге происходит вычисление очередного значения таблицы, получаемого исключительно из уже заполненных ячеек. Такой нехитрый способ требует всего (n+1)*(m+1) шагов и будет достаточно эффективным. Пример заполнения таблицы: начальный этап промежуточные расчеты окончательное состояние ________________________________________________________________________________ © Насонов Денис © http://rain.ifmo.ru/cat 5 Вот одна из реализаций нашей задачи: псевдо-алгоритм с краткими пояснениями, как видно, время его работы O(n*m): 1. n = Length(S); 2. m = Length(T); 3. если n = 0, то расстояние редактирования равно m и завершается работа алгоритма; 4. если m = 0, то расстояние редактирования равно n и завершается работа алгоритма; 5. создаем массив mtr размером [m+1][n+1]; 6. инициализируем первую строку и столбец массива mtr: for (i = 0; i ≤ n; i++) mtr[0][i] = i; for (i = 1; i ≤ m; i++) mtr[i][0] = i; 7. само вычисление расстояния редактирования: for (i = 1; i ≤ n; i++){ s = S[i]; for (j = 1; j ≤ m; j++){ t = T[j]; price = (t == s) ? 0 : 1; mtr[j][i] = Min (mtr[j][i-1] + 1, mtr[j-1][i] + 1, mtr[j-1][i-1] + price); } } 8. расстояние редактирования равно ячейке массива mtr[m][n]. Осталось рассмотреть последнюю компоненту ― обратный проход. Обратный проход Нам необходимо определить: каким образом, после вычисления редакционного расстояния, можно найти соответствующее оптимальное предписание? Простейший путь заключается в том, чтобы, по мере вычисления значений в клетках, хранить указатель на ту ячейку, откуда данное значение было получено. Если рассматривать подробней, то при вычислении значения в клетке (i, j) необходимо установить в ней указатель на (i, j-1) ячейку, при D(i, j) = D(i, j-1) + 1 и D(i, j) = D(i-1, j) + 1, и указатель на (i-1, j-1) ячейку, при D(i, j) = D(i-1, j-1)+ price(i, j). Это правило применяется также к клеткам в нулевой строке и нулевом столбце. Таким образом, по завершению заполнения таблицы, можно построить одно из оптимальных предписаний, просто пробегая по указателям, начиная с ячейки (m, n). ________________________________________________________________________________ © Насонов Денис © http://rain.ifmo.ru/cat 6 Дополнительные сведения Операционно-взвешенное редакционное расстояние Неким обобщением функции Левенштейна является возможность использования произвольной весовой стоимости, приписываемой каждой редакционной операции, в том числе и совпадению. Таким образом, любое включение или удаление имеет вес d, замена имеет вес r, а совпадение – e. При таких условиях определение редакционного расстояния немного изменяется. операционно-взвешенном При редакционном произвольных расстоянии весах операций называется задачей задача о об поиске редакционного предписания, которое переводит строку S1 в S2 с минимальным полным весом операций. Определенно, понятно тогда, как выглядит сама рекуррентная формула. Можно отметить также существование такого понятия, как алфавитно-взвешенное редакционное расстояние. Его особенность заключается в различном весовом значении функции замены. К примеру, вес можно символов, упорядоченных в лексикографическом задать как разность номеров двух порядке. Такая конструкция часто применяется в молекулярной биологии, в частности, в исследованиях цепочек ДНК. Источники 1. Гасфилд Д. Строки, деревья и последовательности в алгоритмах: Информатика и вычислительная биология / Пер. с англ. И.В. Романовского. ― СПб.: Невский Диалект, 2003. ― 654 с. 2. Романовский И. В. Дискретный анализ: Учебное пособие для студентов специализирующихся по прикладной математике и информатике / 3-е изд., перераб. и доп. ― СПб.: Невский Диалект, 2003. ― 320 с. 3. Функция Левенштейна (http://aforge.ibd.lv/) ________________________________________________________________________________ © Насонов Денис © http://rain.ifmo.ru/cat 7