Задача о выводе в контекстно-свободной грамматике, алгоритм Кока-Янгера-Касами — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Нормальная форма Хомского)
 
(не показаны 24 промежуточные версии 2 участников)
Строка 1: Строка 1:
= Формальная грамматика =
+
#перенаправление [[Алгоритм Кока-Янгера-Касами разбора грамматики в НФХ]]
 +
'''Задача о выводе в контекстно-свободной грамматике''' - задача о том, выводимо ли данное слово в данной контекстно-свободной грамматике. '''Алгоритм Кока-Янгера-Касами''' - алгоритм, решающий эту задачу.
  
'''Формальная грамматика''' - способ описания формального языка, то есть некоторого подмножества слов данного конечного алфавита.  Выделяют ''порождающие'' грамматики, состоящие из следующих компонентов:
+
= Определения =
  
* 1) Множество '''терминальных символов''' ('''терминалов''') - символов алфавита, слова над которым определяет грамматика, то есть символов, непосредственно присутствующих в словах языка.
+
== Контекстно-свободная грамматика ==
* 2) Множество '''нетерминальных символов''' ('''нетерминалов''') - объектов, выражающих некоторые структурные части языка, не имеющие конкретного представления как слова над алфавитом (таких, как формула или часть программы).
 
* 3) Множество '''правил вывода''' ('''продукций''') - правил вида L → R, где:
 
** L - непустая последовательность терминальных и нетерминальных символов, содержищий по крайней мере один нетерминал.
 
** R - любая (возможно, пустая) последовательность терминальных и нетерминальных символов.
 
* 4) S - стартовый нетерминал.
 
  
'''Выводом''' называется последовательность строк из терминалов и нетерминалов, такая, что:
+
{{Определение
* Первая строка состоит из стартового нетерминала
+
|definition=
* Каждая следующая строка получена из предыдущей путем замена некоторой подстроки по некоторому правилу
+
'''[[Контекстно-свободные грамматики, вывод, лево- и правосторонний вывод, дерево разбора|Контекстно-свободная грамматика]]''' ('''КС-грамматика''', '''бесконтекстная грамматика''') — способ описания формального языка, задающийся:
* Последняя строка состоит только из терминалов (и, следовательно, не может быть преобразована по правилу грамматики).
 
  
Существование в грамматике вывода для получения конкретного слова - критерий принадлежности слова языку, определяемому грамматикой.
+
* Множеством <tex>\Sigma</tex> терминальных символов
 +
* Множеством <tex>N</tex> нетерминальных символов
 +
* Стартовым нетерминалом <tex>S \in N</tex>
 +
* Множеством продукций вида <tex>A \rightarrow B_1 B_2 ... B_n</tex>, где <tex>A \in N</tex>, <tex>B_i \in \Sigma \cup N</tex>, то есть у которых левые части - одиночные нетерминалы, а правые - последовательности терминалов и нетерминалов.
 +
}}
  
 
=== Пример ===
 
=== Пример ===
  
Терминалы: a, b. Нетерминалы: S, A, B. Продукции:
+
Терминалы: {(, )}. Нетерминалы: {S}. Продукции:
* S &rarr; AB
 
* A &rarr; AB
 
* AB &rarr; ba
 
* A &rarr; a
 
* B &rarr; b
 
 
 
Слова, выводимые в данной грамматике: ab, ba, abb, bab, abbb, babb, ...
 
 
 
Слова, невыводимые в данной грамматике: a, b, baa, baba, ...
 
 
 
= Контекстно-свободная грамматика =
 
 
 
'''Контекстно-свободная грамматика''' ('''КС-грамматика''', '''бесконтекстная грамматика''') — частный случай формальной грамматики, у которой левые части всех правил являются одиночными нетерминалами, то есть все её продукции имеют вид L &rarr; R, где L - нетерминал, а R - последовательность терминалов и нетерминалов.
 
 
 
=== Пример ===
 
 
 
Терминалы: (, ). Нетерминалы: S. Продукции:
 
 
* S &rarr; SS
 
* S &rarr; SS
 
* S &rarr; ()
 
* S &rarr; ()
 
* S &rarr; (S)
 
* S &rarr; (S)
  
Очевидно, что данная грамматика задает язык правильных скобочных последовательностей. Например, последовательность (()(())) может быть выведена следующим образом:
+
Данная грамматика задает язык правильных скобочных последовательностей. Например, последовательность (()(())) может быть выведена следующим образом:
* S &rarr; (S) &rarr; (SS) &rarr; (()(S)) &rarr; (()(()))
+
* <tex> S \Rightarrow (S) \Rightarrow (SS) \Rightarrow (()(S)) \Rightarrow (()(())) </tex>
  
= Нормальная форма Хомского =
+
== Нормальная форма Хомского ==
  
'''Нормальная форма Хомского''' - нормальная форма КС-грамматик, в которой все продукции имеют вид:
+
'''[[Нормальная форма Хомского]]''' - нормальная форма КС-грамматик, в которой все продукции имеют вид:
 
* A &rarr; a, где ''A'' - нетерминал, а ''a'' - терминал
 
* A &rarr; a, где ''A'' - нетерминал, а ''a'' - терминал
 
* A &rarr; BC, где ''A'', ''B'', ''C'' - нетерминалы, причем ''B'' и ''C'' не являются начальными нетерминалами
 
* A &rarr; BC, где ''A'', ''B'', ''C'' - нетерминалы, причем ''B'' и ''C'' не являются начальными нетерминалами
 
* S &rarr; ε, где S - начальный нетерминал и ε - пустая строка (данная продукция необходима, если в языке присуствует пустая строка)
 
* S &rarr; ε, где S - начальный нетерминал и ε - пустая строка (данная продукция необходима, если в языке присуствует пустая строка)
  
Покажем, что любую КС-грамматику можно привести к нормальной форме Хомского. Рассмотрим продукцию этой грамматики: <tex>A \Rightarrow A_1 A_2 A_3 ... A_n</tex>, где <tex>A_i</tex> - терминалы или нетерминалы. Добавим к грамматике нетерминалы <tex>B_1 ... B_n</tex>, <tex>C_k</tex> для таких k, что <tex>A_k</tex> - нетерминал, и продукции вида
+
[[Нормальная форма Хомского|Можно показать]], что любую КС-грамматику можно привести к нормальной форме Хомского.
* <tex>A \Rightarrow B_1</tex>
 
* <tex>B_i \Rightarrow A_i B_{i+1}</tex> если <tex>A_i</tex> - нетерминал
 
* <tex>B_i \Rightarrow C_i B_{i+1}</tex> и <tex>C_i \Rightarrow A_i</tex>, если <tex>A_i</tex> - терминал
 
 
 
Очевидно, что добавленные элементы в совокупности дают рассмотренную продукцию. Проделав данную процедуру ко всем продукциям, мы и получим нормальную форму Хомского для данной грамматики.
 
  
 
= Алгоритм Кока-Янгера-Касами =
 
= Алгоритм Кока-Янгера-Касами =
 
'''Алгоритм Кока-Янгера-Касами''' (''Cocke — Younger — Kasami algorithm'', '''CYK - алгоритм''') - универсальный алгоритм, позволяющий по слову узнать, выводимо ли оно в заданной КС-грамматике в нормальной форме Хомского.
 
'''Алгоритм Кока-Янгера-Касами''' (''Cocke — Younger — Kasami algorithm'', '''CYK - алгоритм''') - универсальный алгоритм, позволяющий по слову узнать, выводимо ли оно в заданной КС-грамматике в нормальной форме Хомского.
  
Пусть дана строка <tex>a_1 a_2 ... a_n</tex>. Заведем трехмерный массив d, состоящий из логических значений, и  <tex>d[A][i,j] = true</tex> тогда и только тогда, когда из нетерминала <tex>A</tex> правилами грамматики можно вывести подстроку <tex>a_i a_{i+1} ... a_j</tex>. Тогда:
+
Пусть дана строка <tex>a_1 a_2 ... a_n</tex>. Заведем трехмерный массив d, состоящий из логических значений, и  <tex>d[A,i,j] = true</tex> тогда и только тогда, когда из нетерминала <tex>A</tex> правилами грамматики можно вывести подстроку <tex>a_i a_{i+1} ... a_j</tex>. Тогда:
* <tex>d[A][i,i] = true</tex>, если в грамматике присутствует правило <tex>A \Rightarrow a_i</tex>, иначе <tex>false</tex>
+
* <tex>d[A,i,i] = true</tex>, если в грамматике присутствует правило <tex>A \rightarrow a_i</tex>, иначе <tex>false</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>a_i...a_j</tex> можно вывести из нетерминала <tex>A</tex>, если существует продукция <tex>A \Rightarrow BC</tex> и такое <tex>k</tex>, что подстрока <tex>a_i...a_k</tex> выводима из <tex>B</tex>, а подстрока <tex>a_{k+1}...a_j</tex> - из <tex>C</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>a_i...a_j</tex> можно вывести из нетерминала <tex>A</tex>, если существует продукция <tex>A \rightarrow BC</tex> и такое <tex>k</tex>, что подстрока <tex>a_i...a_k</tex> выводима из <tex>B</tex>, а подстрока <tex>a_{k+1}...a_j</tex> - из <tex>C</tex>.
  
Значение <tex>d[S][1,n]</tex> содержит ответ на вопрос, выводима ли данная строка в данной грамматике.
+
Значение <tex>d[S,1,n]</tex> содержит ответ на вопрос, выводима ли данная строка в данной грамматике.
  
Очевидно, что алгоритм работает за время <tex>O(n^3)</tex> (где <tex>n</tex> - длина строки) и требует <tex>O(n^2)</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>a_i...a_j</tex> из нетерминала <tex>A</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>a_i...a_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>.
  
Пусть <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>.
+
Таким образом, задача о выводе в КС-грамматике в нормальной форме Хомского является обобщением задачи динамического программирования на подотрезке.
 +
 
 +
=== Сложность алгоритма ===
 +
 
 +
Пусть, <tex>n</tex> - длина входной строки, а <tex>m</tex> - количество правил вывода в грамматике.
  
Таким образом, задача о выводе в КС-грамматике в нормальной форме Хомского является обобщением задачи динамического программирования на подотрезке.
+
Обработка правил вида <tex>A \rightarrow a_i</tex> выполняется за <tex>O(nm)</tex>.
  
=== Псевдокод ===
+
Проход по всем подстрокам выполняется за <tex>O(n^2)</tex>. В обработке подстроки присутствует цикл по всем правилам вывода и по всем разбиениям на две подстроки, следовательно обработка работает за <tex>O(nm)</tex>. В итоге - <tex>O(n^3 m)</tex>.
  
<tex>a_1...a_n</tex> - входная строка. <tex>A_1...A_m</tex> - нетерминалы.
+
Следовательно, общее время работы алгоритма - <tex>O(n^3 m)</tex>. Кроме того, алгоритму требуется память (на массив <tex>d</tex>) объемом <tex>O(n^2 m)</tex>.
<tex>P[i,j,k] = true</tex> если есть продукция <tex>A_i \Rightarrow A_j A_k</tex>.
 
<tex>S(i,c) = true</tex> если есть продукция <tex>A_i \Rightarrow c</tex> (где <tex>c</tex> - терминал).
 
<tex>d[i][j,k]</tex> - можно ли вывести из нетерминала <tex>A_i</tex> подстроку <tex>a_j...a_k</tex>.
 
  
  for i = 1 to m
+
=== Псевдокод ===
    for j = 1 to n
 
      d[i][j,j] = S(i,a[j])
 
  
   // дописать
+
   function CYK (a - строка длины n, G - набор правил вывода грамматики с m нетерминалами, S - стартовый нетерминал) -> bool
 +
  begin
 +
    d : array [1..m,1..n,1..n] of bool
 +
    for i = 1 to n
 +
      if (A -> a[i] - продукция)
 +
        d[A,i,i] = true
 +
    for len = 1 to n-1
 +
      for i = 1 to n-l
 +
        for (A -> BC - продукция)
 +
          for k = i to 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]
 +
  end
  
 
= Ссылки =
 
= Ссылки =
  
* [http://www.intuit.ru/department/algorithms/mathformlang/7/]
 
 
* [http://en.wikipedia.org/wiki/CYK_algorithm Википедия - CYK algorithm]
 
* [http://en.wikipedia.org/wiki/CYK_algorithm Википедия - CYK algorithm]
 
* [http://www.ctc.msiu.ru/program/t-system/diploma/node39.html Алгоритм Кока-Янгера-Касами]
 
* [http://www.ctc.msiu.ru/program/t-system/diploma/node39.html Алгоритм Кока-Янгера-Касами]
 +
 +
[[Категория:В разработке]]
 +
[[Категория:Дискретная математика и алгоритмы]]
 +
[[Категория:Динамическое программирование]]
 +
[[Категория:Теория формальных языков]]

Текущая версия на 23:24, 4 ноября 2014

Задача о выводе в контекстно-свободной грамматике - задача о том, выводимо ли данное слово в данной контекстно-свободной грамматике. Алгоритм Кока-Янгера-Касами - алгоритм, решающий эту задачу.

Определения

Контекстно-свободная грамматика

Определение:
Контекстно-свободная грамматика (КС-грамматика, бесконтекстная грамматика) — способ описания формального языка, задающийся:
  • Множеством [math]\Sigma[/math] терминальных символов
  • Множеством [math]N[/math] нетерминальных символов
  • Стартовым нетерминалом [math]S \in N[/math]
  • Множеством продукций вида [math]A \rightarrow B_1 B_2 ... B_n[/math], где [math]A \in N[/math], [math]B_i \in \Sigma \cup N[/math], то есть у которых левые части - одиночные нетерминалы, а правые - последовательности терминалов и нетерминалов.


Пример

Терминалы: {(, )}. Нетерминалы: {S}. Продукции:

  • S → SS
  • S → ()
  • S → (S)

Данная грамматика задает язык правильных скобочных последовательностей. Например, последовательность (()(())) может быть выведена следующим образом:

  • [math] S \Rightarrow (S) \Rightarrow (SS) \Rightarrow (()(S)) \Rightarrow (()(())) [/math]

Нормальная форма Хомского

Нормальная форма Хомского - нормальная форма КС-грамматик, в которой все продукции имеют вид:

  • A → a, где A - нетерминал, а a - терминал
  • A → BC, где A, B, C - нетерминалы, причем B и C не являются начальными нетерминалами
  • S → ε, где S - начальный нетерминал и ε - пустая строка (данная продукция необходима, если в языке присуствует пустая строка)

Можно показать, что любую КС-грамматику можно привести к нормальной форме Хомского.

Алгоритм Кока-Янгера-Касами

Алгоритм Кока-Янгера-Касами (Cocke — Younger — Kasami algorithm, CYK - алгоритм) - универсальный алгоритм, позволяющий по слову узнать, выводимо ли оно в заданной КС-грамматике в нормальной форме Хомского.

Пусть дана строка [math]a_1 a_2 ... a_n[/math]. Заведем трехмерный массив d, состоящий из логических значений, и [math]d[A,i,j] = true[/math] тогда и только тогда, когда из нетерминала [math]A[/math] правилами грамматики можно вывести подстроку [math]a_i a_{i+1} ... a_j[/math]. Тогда:

  • [math]d[A,i,i] = true[/math], если в грамматике присутствует правило [math]A \rightarrow a_i[/math], иначе [math]false[/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]a_i...a_j[/math] можно вывести из нетерминала [math]A[/math], если существует продукция [math]A \rightarrow BC[/math] и такое [math]k[/math], что подстрока [math]a_i...a_k[/math] выводима из [math]B[/math], а подстрока [math]a_{k+1}...a_j[/math] - из [math]C[/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]a_i...a_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].

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

Сложность алгоритма

Пусть, [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].

Псевдокод

 function CYK (a - строка длины n, G - набор правил вывода грамматики с m нетерминалами, S - стартовый нетерминал) -> bool
 begin
   d : array [1..m,1..n,1..n] of bool
   for i = 1 to n
     if (A -> a[i] - продукция)
       d[A,i,i] = true
   for len = 1 to n-1
     for i = 1 to n-l
       for (A -> BC - продукция)
         for k = i to 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]
 end

Ссылки