Алгоритм Кока-Янгера-Касами разбора грамматики в НФХ — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Псевдокод)
м (Алгоритм)
Строка 5: Строка 5:
  
 
== Алгоритм ==
 
== Алгоритм ==
=== Описание ===
+
'''Алгоритм Кока-Янгера-Касами''' (''Cocke — Younger — Kasami algorithm'', '''CYK - алгоритм''') - универсальный алгоритм, позволяющий по слову узнать, выводимо ли оно в заданной КС-грамматике в нормальной форме Хомского.
Пусть <tex>a_{A, i, j} = true</tex>, если из нетерминала <tex>A</tex> можно вывести подстроку <tex>w[i..j]</tex>. Иначе <tex>a_{A, i, j} = false</tex>:
+
Будем решать задачу [[Динамическое_программирование|динамическим программированием]]. Заведем трехмерный массив d, состоящий из логических значений, и <tex>d[A][i][j] = true</tex> тогда и только тогда, когда из нетерминала <tex>A</tex> правилами грамматики можно вывести подстроку <tex>w[i..j]</tex>.
  
<tex>a_{A, i, j} =  
+
Рассмотрим все пары <tex>\lbrace \langle j, i \rangle | j-i=m \rbrace</tex>, где <tex>m</tex> - константа и <tex>m < n</tex>.
\begin{cases}
 
true,&\text{$A \Rightarrow^{*} w[i..j]$;}\\
 
false,&\text{else.}
 
\end{cases}
 
</tex>.
 
  
Будем динамически заполнять матрицу <tex>a_{A, i, j}</tex> следующим алгоритмом (индукция по <tex>m = j - i</tex>):
+
=== Шаг 1. База ===
 +
<tex>m = 0</tex>. В таком случае <tex>i = j</tex>.
  
*'''База'''. <tex>m = 0</tex>. Ячейки <tex>a_{A, i, i}</tex> заполняются значением <tex>true</tex>, если правило <tex>A \rightarrow w[i]</tex> принадлежит множеству правил <tex>P</tex> грамматики <tex>\Gamma</tex>: <tex>a_{A, i, i} = \lbrack A \rightarrow w[i] \in P \rbrack</tex>.
+
Инициализируем массив для всех нетерминалов, из которых выводится какой-либо символ строки <tex>w</tex>. В таком случае:
 +
: <tex>d[A][i][i] = true</tex>, если в грамматике <tex>\Gamma</tex> присутствует правило <tex>A \rightarrow w[i]</tex>. Иначе <tex>d[A][i][i] = false</tex>.
  
*'''Переход'''. Рассмотрим все пары <tex>\lbrace \langle j, i \rangle | j-i=m \rbrace</tex>. Значения для всех нетерминалов и пар <tex>\lbrace \langle j', i' \rangle | j-i<m \rbrace</tex> уже вычислены, так что: <tex>a_{A, i, j} = \bigvee\limits_{k=i}^{j-1} \bigvee\limits_{A \rightarrow BC} \left( a_{B, i, k} \wedge a_{C, k+1, j}  \right)</tex>.
+
=== Шаг 2. Переход ===
 +
[[Файл:CYK_rule_2.jpg|thumb|300px]]
  
[[Файл:CYK_rule_2.jpg]]
+
<tex>m = j - i</tex>. Значения для всех нетерминалов и пар <tex>\lbrace \langle j', i' \rangle | j' - i' < m \rbrace</tex> уже вычислены, поэтому <tex>d[A][i][j] = \bigvee\limits_{A \rightarrow BC}\bigvee\limits_{k = i}^{j-1} d[B][i][k] \wedge d[C][k+1][j]</tex>. То есть, подстроку <tex>w[i \dots j]</tex> можно вывести из нетерминала <tex>A</tex>, если существует продукция вида <tex>A \rightarrow BC</tex> и такое <tex>k</tex>, что подстрока <tex>w[i \dots k]</tex> выводима из <tex>B</tex>, а подстрока <tex>w[k + 1 \dots j]</tex> - из <tex>C</tex>.
  
*'''Завершение'''. После окончания работы ответ содержится в ячейке <tex>a_{S, 1, n}</tex>, где <tex>n = |w|</tex>.
+
 
 +
 
 +
=== Завершение ===
 +
После окончания работы ответ содержится в ячейке <tex>a_{S, 1, n}</tex>, где <tex>n = |w|</tex>.
 +
Значение <tex>d[S,1,n]</tex> содержит ответ на вопрос, выводима ли данная строка в данной грамматике.
 +
 
 +
== Модификации ==
 +
Заметим, что если массив будет хранить целые числа, а формулу заменить на <tex>d[A][i][j] = \sum\limits_{A \rightarrow BC}\sum\limits_{k = i}^{j-1} d[B,i,k] \cdot d[C,k+1,j]</tex>, то <tex>d[A,i,j]</tex> - количество способов получить подстроку <tex>w[i \dots j]</tex> из нетерминала <tex>A</tex>.
 +
 
 +
Пусть <tex>P_{A \rightarrow BC}</tex> - ''стоимость'' вывода по правилу <tex>A \rightarrow BC</tex>. Тогда, если использовать формулу <tex>d[A,i,j] = \min\limits_{A \rightarrow BC} \min\limits_{k = i}^{j-1}  ( d[B,i,k] + d[C,k+1,j] + P_{A \rightarrow BC} )</tex>, то <tex>d[A,i,j]</tex> - минимальная стоимость вывода подстроки <tex>a_i...a_j</tex> из нетерминала <tex>A</tex>.
 +
 
 +
Таким образом, задача о выводе в КС-грамматике в нормальной форме Хомского является обобщением задачи динамического программирования на подотрезке.
  
 
== Псевдокод ==
 
== Псевдокод ==

Версия 23:11, 4 ноября 2014

Задача:
Пусть дана контекстно-свободная грамматика грамматика [math]\Gamma[/math] в нормальной форме Хомского и слово [math]w \in \Sigma^{*}[/math]. Требуется выяснить, выводится ли это слово в данной грамматике.


Алгоритм

Алгоритм Кока-Янгера-Касами (Cocke — Younger — Kasami algorithm, CYK - алгоритм) - универсальный алгоритм, позволяющий по слову узнать, выводимо ли оно в заданной КС-грамматике в нормальной форме Хомского. Будем решать задачу динамическим программированием. Заведем трехмерный массив d, состоящий из логических значений, и [math]d[A][i][j] = true[/math] тогда и только тогда, когда из нетерминала [math]A[/math] правилами грамматики можно вывести подстроку [math]w[i..j][/math].

Рассмотрим все пары [math]\lbrace \langle j, i \rangle | j-i=m \rbrace[/math], где [math]m[/math] - константа и [math]m \lt n[/math].

Шаг 1. База

[math]m = 0[/math]. В таком случае [math]i = j[/math].

Инициализируем массив для всех нетерминалов, из которых выводится какой-либо символ строки [math]w[/math]. В таком случае:

[math]d[A][i][i] = true[/math], если в грамматике [math]\Gamma[/math] присутствует правило [math]A \rightarrow w[i][/math]. Иначе [math]d[A][i][i] = false[/math].

Шаг 2. Переход

CYK rule 2.jpg

[math]m = j - i[/math]. Значения для всех нетерминалов и пар [math]\lbrace \langle j', i' \rangle | j' - i' \lt m \rbrace[/math] уже вычислены, поэтому [math]d[A][i][j] = \bigvee\limits_{A \rightarrow BC}\bigvee\limits_{k = i}^{j-1} d[B][i][k] \wedge d[C][k+1][j][/math]. То есть, подстроку [math]w[i \dots j][/math] можно вывести из нетерминала [math]A[/math], если существует продукция вида [math]A \rightarrow BC[/math] и такое [math]k[/math], что подстрока [math]w[i \dots k][/math] выводима из [math]B[/math], а подстрока [math]w[k + 1 \dots j][/math] - из [math]C[/math].


Завершение

После окончания работы ответ содержится в ячейке [math]a_{S, 1, n}[/math], где [math]n = |w|[/math]. Значение [math]d[S,1,n][/math] содержит ответ на вопрос, выводима ли данная строка в данной грамматике.

Модификации

Заметим, что если массив будет хранить целые числа, а формулу заменить на [math]d[A][i][j] = \sum\limits_{A \rightarrow BC}\sum\limits_{k = i}^{j-1} d[B,i,k] \cdot d[C,k+1,j][/math], то [math]d[A,i,j][/math] - количество способов получить подстроку [math]w[i \dots j][/math] из нетерминала [math]A[/math].

Пусть [math]P_{A \rightarrow BC}[/math] - стоимость вывода по правилу [math]A \rightarrow BC[/math]. Тогда, если использовать формулу [math]d[A,i,j] = \min\limits_{A \rightarrow BC} \min\limits_{k = i}^{j-1} ( d[B,i,k] + d[C,k+1,j] + P_{A \rightarrow BC} )[/math], то [math]d[A,i,j][/math] - минимальная стоимость вывода подстроки [math]a_i...a_j[/math] из нетерминала [math]A[/math].

Таким образом, задача о выводе в КС-грамматике в нормальной форме Хомского является обобщением задачи динамического программирования на подотрезке.

Псевдокод

boolean CYK(char[] w, list [math]\Gamma[/math], int S)
   int n = length(w)
   boolean d[[math]|\Gamma|[/math]][n][n]
   for i = 1 ... n
      for (A [math]\rightarrow[/math] w[i] [math]\in[/math] [math]\Gamma[/math])
         d[A,i,i] = true
   for len = 1 .. n - 1
      for i = 1 .. n - len
         for (A [math]\rightarrow[/math] BC [math]\in[/math] [math]\Gamma[/math])
            for k = i .. i + len - 1
               d[A][i][i + len] = d[A][i][i + len] or d[B][i][k] and d[C][k + 1][i + len]
return d[S][1][n]

Асимптотика

Необходимо вычислить [math]n^2[/math] булевых величин. На каждую требуется затратить [math]n \cdot |P_A|[/math] операций, где [math]|P_A|[/math] – количество правил. Суммируя по всем правилам получаем конечную сложность [math]O \left( n^3 \cdot |\Gamma| \right)[/math].

Алгоритму требуется [math]n^2 \cdot |N|[/math] памяти, где [math]|N|[/math] — количество нетерминалов грамматики.

Пусть, [math]n[/math] - длина входной строки, а [math]m[/math] - количество правил вывода в грамматике.

Обработка правил вида [math]A \rightarrow a_i[/math] выполняется за [math]O(nm)[/math].

Проход по всем подстрокам выполняется за [math]O(n^2)[/math]. В обработке подстроки присутствует цикл по всем правилам вывода и по всем разбиениям на две подстроки, следовательно обработка работает за [math]O(nm)[/math]. В итоге - [math]O(n^3 m)[/math].

Следовательно, общее время работы алгоритма - [math]O(n^3 m)[/math]. Кроме того, алгоритму требуется память (на массив [math]d[/math]) объемом [math]O(n^2 m)[/math].

Недостаток алгоритма заключается в том, что изначально грамматику необходимо привести к НФХ.

См. также

Источники информации