Самая длинная общая подпоследовательность

Алгоритмическая задача на парах последовательностей
Сравнение двух версий файла-примера на основе их самой длинной общей подпоследовательности (черный)

Самая длинная общая подпоследовательность ( LCS ) — это самая длинная подпоследовательность , общая для всех последовательностей в наборе последовательностей (часто всего двух последовательностей). Она отличается от самой длинной общей подстроки : в отличие от подстрок, подпоследовательности не обязаны занимать последовательные позиции в исходных последовательностях. Задача вычисления самой длинной общей подпоследовательности — это классическая задача компьютерной науки , основа программ сравнения данных , таких как diffутилита , и имеет приложения в вычислительной лингвистике и биоинформатике . Она также широко используется системами контроля версий, такими как Git, для согласования множественных изменений, внесенных в контролируемую ревизией коллекцию файлов.

Например, рассмотрим последовательности (ABCD) и (ACBAD). У них есть пять общих подпоследовательностей длины 2: (AB), (AC), (AD), (BD) и (CD); две общие подпоследовательности длины 3: (ABD) и (ACD); и больше нет общих подпоследовательностей. Таким образом, (ABD) и (ACD) являются их самыми длинными общими подпоследовательностями.

Сложность

Для общего случая произвольного числа входных последовательностей задача является NP-трудной . [1] Когда число последовательностей постоянно, задача решается за полиномиальное время с помощью динамического программирования .

При наличии последовательностей длин наивный поиск будет проверять каждую из подпоследовательностей первой последовательности, чтобы определить, являются ли они также подпоследовательностями оставшихся последовательностей; каждая подпоследовательность может быть проверена за время, линейно зависящее от длин оставшихся последовательностей, поэтому время для этого алгоритма будет Н {\displaystyle N} н 1 , . . . , н Н {\displaystyle n_{1},...,n_{N}} 2 н 1 {\displaystyle 2^{n_{1}}}

О ( 2 н 1 я > 1 н я ) . {\displaystyle O\left(2^{n_{1}}\sum _{i>1}n_{i}\right).}

Для случая двух последовательностей из n и m элементов время выполнения подхода динамического программирования составляет O ( n × m ). [2] Для произвольного числа входных последовательностей подход динамического программирования дает решение за

О ( Н я = 1 Н н я ) . {\displaystyle O\left(N\prod _{i=1}^{N}n_{i}\right).}

Существуют методы с меньшей сложностью [3], которые часто зависят от длины LCS, размера алфавита или и того, и другого.

LCS не обязательно уникален; в худшем случае число общих подпоследовательностей экспоненциально зависит от длины входных данных, поэтому алгоритмическая сложность должна быть как минимум экспоненциальной. [4]

Решение для двух последовательностей

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

Префиксы

Префикс S n слова S определяется как первые n символов слова S. [ 5] Например, префиксы слова S = (AGCA) следующие:

С 0 = ()
С 1 = (А)
С 2 = (АГ)
С 3 = (АГС)
С 4 = (АГКА).

Пусть LCS ( X , Y ) — функция, которая вычисляет самую длинную подпоследовательность, общую для X и Y. Такая функция обладает двумя интересными свойствами.

Первая недвижимость

LCS ( X ^ A , Y ^ A ) = LCS ( X , Y )^ A , для всех строк X , Y и всех символов A , где ^ обозначает конкатенацию строк. Это позволяет упростить вычисление LCS для двух последовательностей, заканчивающихся одним и тем же символом. Например, LCS ("BANANA","ATANA") = LCS ("BANAN","ATAN")^"A", Продолжая для оставшихся общих символов, LCS ("BANANA","ATANA") = LCS ("BAN","AT")^"ANA".

Второе свойство

Если A и B — различные символы ( AB ), то LCS (X^A,Y^B) — одна из строк максимальной длины в наборе { LCS ( X ^ A , Y ), LCS ( X , Y ^ B ) } для всех строк X , Y .

Например, LCS ("ABCDEFG","BCDGK") является самой длинной строкой среди LCS ("ABCDEFG","BCDG") и LCS ("ABCDEF","BCDGK"); если бы обе имели одинаковую длину, одну из них можно было бы выбрать произвольно.

Для реализации имущества различают два случая:

  • Если LCS ("ABCDEFG","BCDGK") заканчивается на "G", то конечная "K" не может быть в LCS, следовательно, LCS ("ABCDEFG","BCDGK") = LCS ("ABCDEFG","BCDG").
  • Если LCS ("ABCDEFG","BCDGK") не заканчивается на "G", то конечная "G" не может быть в LCS, следовательно, LCS ("ABCDEFG","BCDGK") = LCS ("ABCDEF","BCDGK").

ЛКСфункция определена

Пусть две последовательности определены следующим образом: и . Префиксами являются ; префиксами являются . Пусть представляет собой набор наиболее длинных общих подпоследовательностей префиксов и . Этот набор последовательностей задается следующим образом. Х = ( х 1 х 2 х м ) {\displaystyle X=(x_{1}x_{2}\cdots x_{m})} И = ( у 1 у 2 у н ) {\displaystyle Y=(y_{1}y_{2}\cdots y_{n})} Х {\displaystyle X} Х 0 , Х 1 , Х 2 , , Х м {\displaystyle X_{0},X_{1},X_{2},\точки ,X_{м}} И {\displaystyle Y} И 0 , И 1 , И 2 , , И н {\displaystyle Y_{0},Y_{1},Y_{2},\dots,Y_{n}} Л С С ( Х я , И дж ) {\displaystyle {\mathit {LCS}}(X_{i},Y_{j})} Х я {\displaystyle X_{i}} И дж {\displaystyle Y_{j}}

Л С С ( Х я , И дж ) = { ϵ если  я = 0  или  дж = 0 Л С С ( Х я 1 , И дж 1 ) ^ х я если  я , дж > 0  и  х я = у дж макс { Л С С ( Х я , И дж 1 ) , Л С С ( Х я 1 , И дж ) } если  я , дж > 0  и  х я у дж . {\displaystyle {\mathit {LCS}}(X_{i},Y_{j})={\begin{cases}\epsilon &{\mbox{if }}i=0{\mbox{ or }}j=0\\{\mathit {LCS}}(X_{i-1},Y_{j-1}){\hat {}}x_{i}&{\mbox{if }}i,j>0{\mbox{ and }}x_{i}=y_{j}\\\operatorname {\max } \{{\mathit {LCS}}(X_{i},Y_{j-1}),{\mathit {LCS}}(X_{i-1},Y_{j})\}&{\mbox{if }}i,j>0{\mbox{ and }}x_{i}\neq y_{j}.\end{cases}}}

Чтобы найти НКП и , сравните и . Если они равны, то последовательность расширяется на этот элемент, . Если они не равны, то сохраняется самая длинная из двух последовательностей, , и . (Если они имеют одинаковую длину, но не идентичны, то сохраняются обе.) Базовым случаем, когда либо или пусто, является пустая строка , . X i {\displaystyle X_{i}} Y j {\displaystyle Y_{j}} x i {\displaystyle x_{i}} y j {\displaystyle y_{j}} L C S ( X i 1 , Y j 1 ) {\displaystyle {\mathit {LCS}}(X_{i-1},Y_{j-1})} x i {\displaystyle x_{i}} L C S ( X i , Y j 1 ) {\displaystyle {\mathit {LCS}}(X_{i},Y_{j-1})} L C S ( X i 1 , Y j ) {\displaystyle {\mathit {LCS}}(X_{i-1},Y_{j})} X i {\displaystyle X_{i}} Y i {\displaystyle Y_{i}} ϵ {\displaystyle \epsilon }

Рабочий пример

Будет найдена самая длинная подпоследовательность, общая для R = (GAC) и C = (AGCAT). Поскольку функция LCS использует «нулевой» элемент, удобно определить нулевые префиксы, которые являются пустыми для этих последовательностей: R 0 = ε; и C 0 = ε. Все префиксы помещаются в таблицу с C в первой строке (делая ее заголовком столбца ) и R в первом столбце (делая ее заголовком строки ).

Строки LCS
εАГСАТ
εεεεεεε
Гε
Аε
Сε

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

LCS ( R 1 , C 1 ) определяется путем сравнения первых элементов в каждой последовательности. G и A не одинаковы, поэтому эта LCS получает (используя «второе свойство») самую длинную из двух последовательностей, LCS ( R 1 , C 0 ) и LCS ( R 0 , C 1 ). Согласно таблице, обе они пусты, поэтому LCS ( R 1 , C 1 ) также пуста, как показано в таблице ниже. Стрелки указывают, что последовательность исходит как из ячейки выше, LCS ( R 0 , C 1 ), так и из ячейки слева, LCS ( R 1 , C 0 ).

LCS ( R 1 , C 2 ) определяется путем сравнения G и G. Они совпадают, поэтому G добавляется к верхней левой последовательности, LCS ( R 0 , C 1 ), которая является (ε), давая (εG), которая является (G).

Для LCS ( R 1 , C 3 ), G и C не совпадают. Последовательность выше пуста; та, что слева, содержит один элемент, G. Выбираем самую длинную из них, LCS ( R 1 , C 3 ) — это (G). Стрелка указывает влево, так как это самая длинная из двух последовательностей.

LCS ( R 1 , C 4 ) также является (G).

LCS ( R 1 , C 5 ) также является (G).

Строка "G" завершена
εАГСАТ
εεεεεεε
Гε     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} ε   {\displaystyle {\overset {\nwarrow }{\ }}} (Г)   {\displaystyle {\overset {\ }{\leftarrow }}} (Г)   {\displaystyle {\overset {\ }{\leftarrow }}} (Г)   {\displaystyle {\overset {\ }{\leftarrow }}} (Г)
Аε
Сε

Для LCS ( R 2 , C 1 ) A сравнивается с A. Два элемента совпадают, поэтому A добавляется к ε, что дает (A).

Для LCS ( R 2 , C 2 ) A и G не совпадают, поэтому используется самая длинная из LCS ( R 1 , C 2 ), которая является (G), и LCS ( R 2 , C 1 ), которая является (A). В этом случае каждая из них содержит один элемент, поэтому этой LCS даются две подпоследовательности: (A) и (G).

Для LCS ( R 2 , C 3 ) A не соответствует C. LCS ( R 2 , C 2 ) содержит последовательности (A) и (G); LCS( R 1 , C 3 ) — это (G), которая уже содержится в LCS ( R 2 , C 2 ). Результатом является то, что LCS ( R 2 , C 3 ) также содержит две подпоследовательности, (A) и (G).

Для LCS ( R 2 , C 4 ) A соответствует A, который добавляется к верхней левой ячейке, давая (GA).

Для LCS ( R 2 , C 5 ) A не соответствует T. Сравнивая две последовательности, (GA) и (G), самая длинная — (GA), поэтому LCS ( R 2 , C 5 ) — это (GA).

Ряды «G» и «A» завершены
εАГСАТ
εεεεεεε
Гε     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} ε   {\displaystyle {\overset {\nwarrow }{\ }}} (Г)   {\displaystyle {\overset {\ }{\leftarrow }}} (Г)   {\displaystyle {\overset {\ }{\leftarrow }}} (Г)   {\displaystyle {\overset {\ }{\leftarrow }}} (Г)
Аε   {\displaystyle {\overset {\nwarrow }{\ }}} (А)     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} (А) и (Г)     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} (А) и (Г)   {\displaystyle {\overset {\nwarrow }{\ }}} (ГА)   {\displaystyle {\overset {\ }{\leftarrow }}} (ГА)
Сε

Для LCS ( R 3 , C 1 ) C и A не совпадают, поэтому LCS ( R 3 , C 1 ) получает самую длинную из двух последовательностей, (A).

Для LCS ( R 3 , C 2 ), C и G не совпадают. Как LCS ( R 3 , C 1 ), так и LCS ( R 2 , C 2 ) имеют по одному элементу. Результатом является то, что LCS ( R 3 , C 2 ) содержит две подпоследовательности, (A) и (G).

Для LCS ( R 3 , C 3 ) C и C совпадают, поэтому C добавляется к LCS ( R 2 , C 2 ), которая содержит две подпоследовательности, (A) и (G), что дает (AC) и (GC).

Для LCS ( R 3 , C 4 ) не совпадают C и A. Объединение LCS ( R 3 , C 3 ), который содержит (AC) и (GC), и LCS ( R 2 , C 4 ), который содержит (GA), дает в общей сложности три последовательности: (AC), (GC) и (GA).

Наконец, для LCS ( R 3 , C 5 ) не совпадают C и T. В результате LCS ( R 3 , C 5 ) также содержит три последовательности: (AC), (GC) и (GA).

Заполненная таблица LCS
εАГСАТ
εεεεεεε
Гε     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} ε   {\displaystyle {\overset {\nwarrow }{\ }}} (Г)   {\displaystyle {\overset {\ }{\leftarrow }}} (Г)   {\displaystyle {\overset {\ }{\leftarrow }}} (Г)   {\displaystyle {\overset {\ }{\leftarrow }}} (Г)
Аε   {\displaystyle {\overset {\nwarrow }{\ }}} (А)     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} (А) и (Г)     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} (А) и (Г)   {\displaystyle {\overset {\nwarrow }{\ }}} (ГА)   {\displaystyle {\overset {\ }{\leftarrow }}} (ГА)
Сε     {\displaystyle {\overset {\ \uparrow }{\ }}} (А)     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} (А) и (Г)   {\displaystyle {\overset {\nwarrow }{\ }}} (AC) и (GC)     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} (AC) и (GC) и (GA)     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} (AC) и (GC) и (GA)

Конечный результат заключается в том, что последняя ячейка содержит все самые длинные подпоследовательности, общие для (AGCAT) и (GAC); это (AC), (GC) и (GA). Таблица также показывает самые длинные общие подпоследовательности для каждой возможной пары префиксов. Например, для (AGC) и (GA) самые длинные общие подпоследовательности — это (A) и (G).

Подход обратного отслеживания

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

Хранение длины, а не последовательности
εАГСАТ
ε000000
Г0     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} 0   {\displaystyle {\overset {\nwarrow }{\ }}} 1   {\displaystyle {\overset {\ }{\leftarrow }}} 1   {\displaystyle {\overset {\ }{\leftarrow }}} 1   {\displaystyle {\overset {\ }{\leftarrow }}} 1
А0   {\displaystyle {\overset {\nwarrow }{\ }}} 1     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} 1     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} 1   {\displaystyle {\overset {\nwarrow }{\ }}} 2   {\displaystyle {\overset {\ }{\leftarrow }}} 2
С0     {\displaystyle {\overset {\ \uparrow }{\ }}} 1     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} 1   {\displaystyle {\overset {\nwarrow }{\ }}} 2     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} 2     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} 2

Фактические подпоследовательности выводятся в процедуре «обратного отслеживания», которая следует за стрелками в обратном направлении, начиная с последней ячейки в таблице. Когда длина уменьшается, последовательности должны иметь общий элемент. Возможны несколько путей, когда в ячейке показаны две стрелки. Ниже приведена таблица для такого анализа с числами, окрашенными в ячейки, где длина собирается уменьшаться. Жирные числа прослеживают последовательность, (GA). [6]

Пример трассировки
εАГСАТ
ε000000
Г0     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} 0   {\displaystyle {\overset {\nwarrow }{\ }}} 1   {\displaystyle {\overset {\ }{\leftarrow }}} 1   {\displaystyle {\overset {\ }{\leftarrow }}} 1   {\displaystyle {\overset {\ }{\leftarrow }}} 1
А0   {\displaystyle {\overset {\nwarrow }{\ }}} 1     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} 1     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} 1   {\displaystyle {\overset {\nwarrow }{\ }}} 2   {\displaystyle {\overset {\ }{\leftarrow }}} 2
С0     {\displaystyle {\overset {\ \uparrow }{\ }}} 1     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} 1   {\displaystyle {\overset {\nwarrow }{\ }}} 2     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} 2     {\displaystyle {\overset {\ \ \uparrow }{\leftarrow }}} 2

Связь с другими проблемами

Для двух строк и длина кратчайшей общей суперпоследовательности связана с длиной LCS соотношением [3] X 1 m {\displaystyle X_{1\dots m}} Y 1 n {\displaystyle Y_{1\dots n}}

| S C S ( X , Y ) | = n + m | L C S ( X , Y ) | . {\displaystyle \left|SCS(X,Y)\right|=n+m-\left|LCS(X,Y)\right|.}

Расстояние редактирования , когда разрешены только вставка и удаление (без замены) или когда стоимость замены в два раза превышает стоимость вставки или удаления, составляет:

d ( X , Y ) = n + m 2 | L C S ( X , Y ) | . {\displaystyle d'(X,Y)=n+m-2\cdot \left|LCS(X,Y)\right|.}

Код для решения динамического программирования

Вычисление длины LCS

Функция ниже принимает в качестве входных данных последовательности X[1..m]и Y[1..n], вычисляет НКП между X[1..i]и Y[1..j]для всех 1 ≤ i ≤ mи 1 ≤ j ≤ nи сохраняет их в C[i,j]. C[m,n]будет содержать длину НКП Xи Y. [7]

функция LCSLength(X[1..m], Y[1..n]) C = массив(0..m, 0..n) для i := 0..m С[я,0] = 0 для j := 0..n С[0,j] = 0 для i := 1..m для j := 1..n если X[i] = Y[j] С[i,j] := С[i-1,j-1] + 1 еще C[i,j] := макс(C[i,j-1], C[i-1,j]) вернуть C[м,н]

В качестве альтернативы можно использовать мемоизацию .

Чтение LCS

Следующая функция отслеживает выборы, сделанные при вычислении Cтаблицы. Если последние символы в префиксах равны, они должны быть в LCS. Если нет, проверьте, что дало наибольшее LCS, оставив и , и сделайте тот же выбор. Просто выберите один, если они были одинаковой длины. Вызовите функцию с и . x i {\displaystyle x_{i}} y j {\displaystyle y_{j}} i=mj=n

функция backtrack(C[0..m,0..n], X[1..m], Y[1..n], i, j) если i = 0 или j = 0 вернуть "" если X[i] = Y[j] вернуть backtrack(C, X, Y, i-1, j-1) + X[i] если C[i,j-1] > C[i-1,j] вернуть backtrack(C, X, Y, i, j-1) вернуть backtrack(C, X, Y, i-1, j)

Считывание всех LCS

Если выбор и даст одинаково длинный результат, зачитайте обе полученные подпоследовательности. Это возвращается как набор этой функцией. Обратите внимание, что эта функция не является полиномиальной, так как она может разветвляться почти на каждом шаге, если строки похожи. x i {\displaystyle x_{i}} y j {\displaystyle y_{j}}

функция backtrackAll(C[0..m,0..n], X[1..m], Y[1..n], i, j) если i = 0 или j = 0 вернуть {""} если X[i] = Y[j] вернуть {Z + X[i] для всех Z в backtrackAll(C, X, Y, i-1, j-1)} Р := {} если C[i,j-1] ≥ C[i-1,j] R := backtrackAll(C, X, Y, i, j-1) если C[i-1,j] ≥ C[i,j-1] R := R ∪ backtrackAll(C, X, Y, i-1, j) возврат R

Эта функция будет возвращаться по матрице C и выводить разницу между двумя последовательностями. Обратите внимание, что вы получите другой ответ, если поменяете местами и <, с >и ниже.

функция printDiff(C[0..m,0..n], X[1..m], Y[1..n], i, j) если i >= 0 и j >= 0 и X[i] = Y[j] printDiff(C, X, Y, i-1, j-1) распечатать " " + X[i] иначе, если j > 0 и (i = 0 или C[i,j-1] ≥ C[i-1,j]) printDiff(C, X, Y, i, j-1) распечатать "+ " + Y[j] иначе, если i > 0 и (j = 0 или C[i,j-1] < C[i-1,j]) printDiff(C, X, Y, i-1, j) распечатать "- " + X[i] еще печать ""

Пример

Пусть будет « » и будет « ». Самая длинная общая подпоследовательность между и — это « ». Таблица , показанная ниже, которая сгенерирована функцией , показывает длины самых длинных общих подпоследовательностей между префиксами и . Строка th и столбец th показывают длину LCS между и . X {\displaystyle X} XMJYAUZ Y {\displaystyle Y} MZJAWXU X {\displaystyle X} Y {\displaystyle Y} MJAUCLCSLength X {\displaystyle X} Y {\displaystyle Y} i {\displaystyle i} j {\displaystyle j} X 1.. i {\displaystyle X_{1..i}} Y 1.. j {\displaystyle Y_{1..j}}

01234567
εМЗДж.АВтХУ
0ε00000000
1Х00000011
2М01111111
3Дж.01122222
4И01122222
5А01123333
6У01123334
7З01223334

Выделенные числа показывают путь, по которому функция будет следовать из нижнего правого вbacktrack верхний левый угол при считывании LCS. Если текущие символы в и равны, они являются частью LCS, и мы идем как вверх, так и влево (выделено жирным шрифтом ). Если нет, мы идем вверх или влево, в зависимости от того, какая ячейка имеет большее число. Это соответствует либо взятию LCS между и , либо и . X {\displaystyle X} Y {\displaystyle Y} X 1.. i 1 {\displaystyle X_{1..i-1}} Y 1.. j {\displaystyle Y_{1..j}} X 1.. i {\displaystyle X_{1..i}} Y 1.. j 1 {\displaystyle Y_{1..j-1}}

Оптимизация кода

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

Сократить набор проблем

Матрица C в наивном алгоритме растет квадратично с длиной последовательностей. Для двух последовательностей из 100 элементов потребуется матрица из 10 000 элементов и потребуется выполнить 10 000 сравнений. В большинстве реальных случаев, особенно при diff-сравнениях и исправлениях исходного кода, начало и конец файлов редко меняются, и почти наверняка не оба одновременно. Если в середине последовательности изменилось всего несколько элементов, начало и конец можно исключить. Это снижает не только требования к памяти для матрицы, но и количество сравнений, которые необходимо выполнить.

функция LCS(X[1..m], Y[1..n]) начало := 1 m_end := m n_end := n обрезать соответствующие элементы в начале  , пока начало ≤ m_end и начало ≤ n_end и X[start] = Y[start] начало := начало + 1 обрезать соответствующие элементы в конце  , пока начало ≤ m_end и начало ≤ n_end и X[m_end] = Y[n_end] m_end := m_end - 1 n_end := n_end - 1 C = массив(начало-1..m_конец, начало-1..n_конец) цикл только по измененным элементам  для i := start..m_end для j := start..n_end алгоритм продолжается, как и прежде ...

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

Сократите время сравнения

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

Преобразовать строки в хэши

Для уменьшения размера строк в последовательностях можно использовать хэш -функцию или контрольную сумму . То есть, для исходного кода, где средняя длина строки составляет 60 или более символов, хэш или контрольная сумма для этой строки может быть длиной всего от 8 до 40 символов. Кроме того, рандомизированная природа хэшей и контрольных сумм гарантирует, что сравнения будут выполняться быстрее, поскольку строки исходного кода редко будут изменяться в начале.

У этой оптимизации есть три основных недостатка. Во-первых, необходимо потратить некоторое время заранее на предварительный расчет хэшей для двух последовательностей. Во-вторых, необходимо выделить дополнительную память для новых хэшированных последовательностей. Однако, по сравнению с наивным алгоритмом, используемым здесь, оба этих недостатка относительно минимальны.

Третий недостаток — коллизии . Поскольку контрольная сумма или хэш не гарантированно будут уникальными, есть небольшая вероятность того, что два разных элемента могут быть сведены к одному и тому же хешу. Это маловероятно в исходном коде, но это возможно. Поэтому криптографический хэш будет гораздо лучше подходить для этой оптимизации, так как его энтропия будет значительно больше, чем у простой контрольной суммы. Однако преимущества могут не стоить настройки и вычислительных требований криптографического хеша для небольших длин последовательностей.

Уменьшить необходимое пространство

Если требуется только длина LCS, матрицу можно свести к матрице или к вектору, поскольку подход динамического программирования требует только текущий и предыдущий столбцы матрицы. Алгоритм Хиршберга позволяет построить оптимальную последовательность за то же квадратичное время и линейные границы пространства. [8] 2 × min ( n , m ) {\displaystyle 2\times \min(n,m)} min ( m , n ) + 1 {\displaystyle \min(m,n)+1}

Уменьшение количества промахов кэша

Чоудхури и Рамачандран разработали квадратичный по времени линейно-пространственный алгоритм [9] [10] для нахождения длины LCS вместе с оптимальной последовательностью, который на практике работает быстрее, чем алгоритм Хиршберга, благодаря своей превосходной производительности кэша. [9] Алгоритм имеет асимптотически оптимальную сложность кэша в рамках идеальной модели кэша . [11] Интересно, что сам алгоритм не обращает внимания на кэш [11], что означает, что он не делает никаких выборов на основе параметров кэша (например, размера кэша и размера строки кэша) машины.

Дальнейшая оптимизация алгоритмов

Существует несколько алгоритмов, которые работают быстрее, чем представленный подход динамического программирования. Одним из них является алгоритм Ханта–Шиманского , который обычно работает за время (для ), где — количество совпадений между двумя последовательностями. [12] Для задач с ограниченным размером алфавита можно использовать метод четырех русских , чтобы сократить время работы алгоритма динамического программирования на логарифмический множитель. [13] O ( ( n + r ) log ( n ) ) {\displaystyle O((n+r)\log(n))} n > m {\displaystyle n>m} r {\displaystyle r}

Поведение на случайных строках

Начиная с Chvátal & Sankoff (1975), [14] ряд исследователей изучали поведение длины самой длинной общей подпоследовательности, когда две заданные строки выбираются случайным образом из одного и того же алфавита. Когда размер алфавита постоянен, ожидаемая длина LCS пропорциональна длине двух строк, а константы пропорциональности (зависящие от размера алфавита) известны как константы Chvátal–Sankoff . Их точные значения неизвестны, но были доказаны верхние и нижние границы их значений, [15] и известно, что они растут обратно пропорционально квадратному корню из размера алфавита. [16] Было показано, что упрощенные математические модели задачи о самой длинной общей подпоследовательности контролируются распределением Трейси–Уидома . [17]

Смотрите также

Ссылки

  1. ^ Дэвид Майер (1978). «Сложность некоторых проблем с подпоследовательностями и суперпоследовательностями». J. ACM . 25 (2). ACM Press: 322–336. doi : 10.1145/322063.322075 . S2CID  16120634.
  2. ^ Вагнер, Роберт; Фишер, Майкл (январь 1974). «Проблема коррекции строка-в-строку». Журнал ACM . 21 (1): 168–173. CiteSeerX 10.1.1.367.5281 . doi :10.1145/321796.321811. S2CID  13381535. 
  3. ^ ab L. Bergroth и H. Hakonen и T. Raita (7–29 сентября 2000 г.). Обзор алгоритмов нахождения наиболее длинных общих подпоследовательностей . Труды Седьмого международного симпозиума по обработке строк и поиску информации. SPIRE 2000. A Curuna, Испания: IEEE Computer Society. стр. 39–48. doi :10.1109/SPIRE.2000.878178. ISBN 0-7695-0746-8. S2CID  10375334.
  4. ^ Рональд И. Гринберг (2003-08-06). «Границы числа самых длинных общих подпоследовательностей». arXiv : cs.DM/0301030 .
  5. ^ Ся, Сюхуа (2007). Биоинформатика и клетка: современные вычислительные подходы в геномике, протеомике и транскриптомике . Нью-Йорк: Springer. стр. 24. ISBN 978-0-387-71336-6.
  6. ^ Томас Х. Кормен , Чарльз Э. Лейзерсон , Рональд Л. Ривест и Клиффорд Стайн (2001). "15.4". Введение в алгоритмы (2-е изд.). MIT Press и McGraw-Hill. стр. 350–355. ISBN 0-262-53196-8.{{cite book}}: CS1 maint: multiple names: authors list (link)
  7. ^ Кормен, Томас Х .; Лейзерсон, Чарльз Э .; Ривест, Рональд Л.; Стайн , Клиффорд (2009) [1990]. «Динамическое программирование». Введение в алгоритмы (3-е изд.). MIT Press и McGraw-Hill. стр. 394. ISBN 0-262-03384-4.
  8. ^ Хиршберг, Д.С. (1975). «Линейный пространственный алгоритм для вычисления максимальных общих подпоследовательностей». Сообщения ACM . 18 (6): 341–343. doi : 10.1145/360825.360861 . S2CID  207694727.
  9. ^ ab Chowdhury, Rezaul; Ramachandran, Vijaya (январь 2006). "Динамическое программирование без учета кэша". Труды семнадцатого ежегодного симпозиума ACM-SIAM по дискретным алгоритмам - SODA '06 . стр. 591–600. doi :10.1145/1109557.1109622. ISBN 0898716055. S2CID  9650418.
  10. ^ Chowdhury, Rezaul; Le, Hai-Son; Ramachandran, Vijaya (июль 2010 г.). «Динамическое программирование без учета кэша для биоинформатики». IEEE/ACM Transactions on Computational Biology and Bioinformatics . 7 (3): 495–510. doi :10.1109/TCBB.2008.94. PMID  20671320. S2CID  2532039.
  11. ^ аб Фриго, Маттео; Лейзерсон, Чарльз Э.; Прокоп, Харальд; Рамачандран, Шридхар (январь 2012 г.). «Алгоритмы, не обращающие внимания на кэш». Транзакции ACM на алгоритмах . 8 (1): 1–22. дои : 10.1145/2071379.2071383.
  12. ^ Апостолико, Альберто; Галиль, Цви (1997-05-29). Алгоритмы сопоставления образов. Oxford University Press. ISBN 9780195354348.
  13. ^ Масек, Уильям Дж.; Патерсон, Майкл С. (1980), «Быстрый алгоритм вычисления расстояний редактирования строк», Журнал компьютерных и системных наук , 20 (1): 18–31, doi : 10.1016/0022-0000(80)90002-1 , MR  0566639.
  14. ^ Chvátal, Václáv ; Sankoff, David (1975), «Самые длинные общие подпоследовательности двух случайных последовательностей», Journal of Applied Probability , 12 (2): 306–315, doi :10.2307/3212444, JSTOR  3212444, MR  0405531, S2CID  250345191.
  15. ^ Люкер, Джордж С. (2009), «Улучшенные границы средней длины самых длинных общих подпоследовательностей», Журнал ACM , 56 (3), A17, doi : 10.1145/1516512.1516519, MR  2536132, S2CID  7232681.
  16. ^ Киви, Маркос; Лёбль, Мартин; Матоушек, Йиржи (2005), «Ожидаемая длина самой длинной общей подпоследовательности для больших алфавитов», Advances in Mathematics , 197 (2): 480–498, arXiv : math/0308234 , doi : 10.1016/j.aim.2004.10.012 , MR  2173842.
  17. ^ Маджумдар, Сатья Н.; Нечаев, Сергей (2005), "Точные асимптотические результаты для модели соответствия Бернулли для выравнивания последовательностей", Physical Review E , 72 (2): 020901, 4, arXiv : q-bio/0410012 , Bibcode : 2005PhRvE..72b0901M, doi : 10.1103/PhysRevE.72.020901, MR  2177365, PMID  16196539, S2CID  11390762.
  • Словарь алгоритмов и структур данных: самая длинная общая подпоследовательность
  • Коллекция реализаций самой длинной общей подпоследовательности во многих языках программирования.
  • Найти самую длинную общую подпоследовательность в Python


Retrieved from "https://en.wikipedia.org/w/index.php?title=Longest_common_subsequence&oldid=1192293200"