Изменения

Перейти к: навигация, поиск
Алгоритм
==Алгоритм==
Для начала модифицируем [[Алгоритм Эрли|алгоритм Эрли]]. Главным отличием от базовой версии алгоритма является функция <tex>\mathtt{rulesLoop}</tex>, внутри которой мы, как и в базовой версии, просматриваем второе и третье правило, однако, в отличие от базовой версии, где при каждом изменении <tex>D_j</tex> мы просматривали весь список <tex>D_j</tex> и применяли к нему второе и третье правило, в модифицированной версии мы применяем правила внутри <tex>\mathtt{rulesLoop}</tex>, просматривая только те ситуации, которые были добавлены на предыдущей итерации цикла <tex>\mathtt{while}</tex>.
Будем рассматривать грамматику [[Удаление eps-правил из грамматики|без &epsilon;-правил]] и [[Удаление бесполезных символов из грамматики|бесполезных символов]].
'''function''' <tex>I_0\mathtt{earleyMod}(G, w)</tex> : <font color= green>// Инициализация </font> <tex>D_{0} = \{lbrace [S' \rightarrow \cdot S, 0]\}rbrace </tex> # Правило (0) — инициализация useful_loop rulesLoop(0) '''for i ''' j = 1..n '''for ''' <tex>[A \rightarrow \alpha \cdot a_{j} \beta, i] \in I_D_{j-1}</tex> <tex>I_jD_j</tex> &<tex> \cup;</tex> = <tex>[A \rightarrow \alpha a_{j} \cdot \beta, i]</tex> # Правило (1) <font color=green>// Первое правило </font> useful_loop rulesLoop(j)
'''function useful_loop''' <tex>\mathtt{rulesLoop(j)}</tex>: <tex>I_jD_j'' = I_jD_j</tex> '''while ''' <tex>I_jD_j'' \ne \varnothing</tex> <tex>I_jD_j' = I_jD_j''</tex> <tex>I_jD_j'' = \varnothing</tex> '''for ''' <tex>[B \rightarrow \eta \cdot , i] \in I_jD_j'</tex> # <font color=green>// Цикл (*)</font> '''for ''' <tex>[A \rightarrow \alpha \cdot B \beta, k] \in I_D_{i}</tex> <tex>I_jD_j''</tex> &<tex> \cup;</tex> = <tex>[A \rightarrow \alpha B \cdot \beta, k] </tex> # Правило (2)<font color=green>// Второе правило </font>
'''for ''' <tex>[B \rightarrow \alpha \cdot A \eta, k] \in I_jD_j'</tex> # <font color=green>// Цикл (**)</font> '''for ''' <tex>\beta : (A \rightarrow \beta) \in P</tex> <tex>I_jD_j''</tex> &<tex> \cup;</tex> = <tex>[A \rightarrow \cdot \beta, j]</tex> # Правило (3) <font color=green>// Третье правило </font> <tex>I_jD_j</tex> &<tex> \cup;</tex> = <tex>I_jD_j''</tex>
== Доказательство эквивалентности == В циклах, помеченных <tex>(*)</tex> и <tex>(**)</tex>, просматривается не весь список <tex>I_jD_j</tex>, а только те ситуации, которые были добавлены на предыдущей итерации цикла <codetex>\mathrm{while}</codetex>. Данная модификация является корректной.
# Рассмотрим цикл <tex>(*)</tex>. Если в текущей ситуации <tex>[B \rightarrow \eta \cdot, i]</tex> этого цикла <tex>i \ne j</tex>, то во внутреннем цикле просматривается список с меньшим индексом, в который новые ситуации больше не добавляются. Поэтому после первого просмотра этого списка будут добавлены все ситуации, удовлетворяющие условию, и больше ситуацию <tex>[B \rightarrow \eta \cdot, i]</tex> в цикле <tex>(*)</tex> рассматривать не нужно. Если же <tex>i = j</tex>, то <tex>\eta \Rightarrow^* \varepsilon</tex>, что возможно, только если <tex>B = S', \eta = \varepsilon</tex>. Тогда во внутреннем цикле не будет добавлено ни одной ситуации, так как <tex>S'</tex> не встречается в правых частях правил.
# Теперь рассмотрим цикл <tex>(**)</tex>. Так как для каждой ситуации <tex>[B \rightarrow \alpha \cdot A \eta, k]</tex> в список добавляется новая ситуация, соответствующая правилу из грамматики, а грамматика фиксирована, то после первого просмотра будут добавлены все возможные ситуации для <tex>[B \rightarrow \alpha \cdot A \eta, k]</tex>.
|about=1
|statement=
<tex>\forall\,j: 1 \le leqslant j \le leqslant n</tex> в списке <tex>I_jD_j</tex> находится <tex>O(j)</tex> ситуаций.
|proof=
Так как грамматика фиксирована, то <tex>\forall i</tex> количество ситуаций вида <tex>[A \rightarrow \alpha \cdot \beta, i]</tex> не больше некоторой константы. Таким образом, поскольку в <tex>I_jD_j</tex> находятся ситуации, у которых <tex>0 \le leqslant i \le leqslant j</tex>, всего в <tex>I_jD_j</tex> будет <tex>O(j)</tex> ситуаций.
}}
|about=2
|statement=
Пусть <tex>G \Gamma = (N, \Sigma, P, S)</tex> {{---}} однозначная КС-грамматика без непорождающих нетерминалов и <tex>a_1 \dots a_n</tex> {{---}} цепочка из <tex>\Sigma^*</tex>. Тогда алгоритм Эрли пытается включить <tex>[A \rightarrow \alpha \cdot \beta, i]</tex> в <tex>I_jD_j</tex> не более одного раза, если <tex>\alpha \ne \varepsilon</tex>.
|proof=
Ситуацию <tex>[A \rightarrow \alpha \cdot \beta, i]</tex> можно включить в <tex>I_jD_j</tex> только по правилам <tex>(1)</tex> (если последний символ <tex>\alpha</tex> — терминал) и <tex>(2)</tex> (если нетерминал). В первом случае результат очевиден. Во втором случае допустим, что <tex>[A \rightarrow \alpha'B \cdot \beta, i]</tex> включается в <tex>I_jD_j</tex>, когда рассматриваются две ситуации <tex>[B \rightarrow \eta_1 \cdot, k_1]</tex> и <tex>[B \rightarrow \eta_2 \cdot, k_2]</tex> (они различны, так как в цикле <tex>(*)</tex> каждая ситуация из каждого списка рассматривается по одному разу). Тогда ситуация <tex>[A \rightarrow \alpha' \cdot B\beta, i]</tex> должна оказаться одновременно в <tex>I_D_{k_1}</tex> и в <tex>I_D_{k_2}</tex>. Таким образом, получаем:
* <tex>\alpha' \Rightarrow^* a_{i+1} \ldots a_{k_1}</tex> и <tex>\alpha' \Rightarrow^* a_{i+1} \ldots a_{k_2}</tex>;
* <tex>\eta_1 \Rightarrow^* a_{k_1+1} \ldots a_j</tex> и <tex>\eta_2 \Rightarrow^* a_{k_2+1} \ldots a_j</tex>.
Следовательно, <tex>\alpha' \eta_1 \Rightarrow^* a_{i+1} \ldots a_j</tex> и <tex>\alpha' \eta_2 \Rightarrow^* a_{i+1} \ldots a_j</tex>.<br/>
Заметим, что <tex>S \Rightarrow^* \gamma A \delta \Rightarrow^* a_1 \ldots a_i A \delta \Rightarrow a_1 \ldots a_i \alpha' B \beta \delta</tex>. Предположим, что <tex>\beta \delta \Rightarrow^* w'</tex> (ведь в грамматике нет непорождающих нетерминалов). Тогда <tex>S \Rightarrow^* a_1 \ldots a_i \alpha' \eta_1 w'</tex> и аналогично <tex>S \Rightarrow^* a_1 \ldots a_i \alpha' \eta_2 w'</tex>.<br/>
Таким образом, если <tex>k_1 \ne k_2</tex>, то подстрока <tex>a_{i+1} \ldots a_j</tex> выводится двумя различными способами из <tex>\alpha' \eta_1</tex> и <tex>\alpha' \eta_2</tex> (поскольку в первом случае <tex>\alpha' \Rightarrow^* a_{i+1} \ldots a_{k_1}</tex>, а во втором <tex>\alpha' \Rightarrow^* a_{i+1} \ldots a_{k_2}</tex>), то есть у строки <tex>a_1 \ldots a_jw'</tex> есть два различных вывода, что противоречит однозначности грамматики. Если же <tex>k_1 = k_2</tex>, то <tex>\eta_1 \ne \eta_2</tex>, что приводит к аналогичному противоречию. Суммируя выше сказанное, отметим, что противоречие получается из того факта, что в некоторый момент времени (то есть для подстроки <tex>a_1 \dots a_i</tex>) мы получаем два различных дерева вывода. Поэтому, в дальнейшем, при выводе суффикса <tex>a_{i+1} \dots a_n</tex>, каким образом мы его не получим, деревьев вывода будет как минимум два, поскольку они будут получаться заменой какого-то листа (терминального символа) на какое-то правило (поддерево из нетерминалов и терминалов),таким образом, получаем противоречие с однозначностью (по определению [[Существенно_неоднозначные_языки | неоднозачной грамматики]])
}}
Если входная грамматика однозначна, то время выполнения алгоритма Эрли для слова длины <tex>n</tex> составляет <tex>O(n^2)</tex>.
|proof=
Орагнизуем каждый список разбора <tex>I_jD_j</tex> таким образом, чтобы по любому символу <tex>x \in \Sigma \cup N</tex>, можно было за <tex>O(1)</tex> получить список тех и только тех ситуаций, содержащихся в <tex>I_jD_j</tex>, которые имеют вид <tex>[A \rightarrow \alpha \cdot x \beta, j]</tex>.
Время построения <tex>I_0D_0</tex> не зависит от входной строки.
Рассмотрим <tex>I_jD_j, \, j > 0</tex>.
# При включении ситуаций по правилу <tex>(1)</tex> необходимо лишь просмотреть предыдущий список и для каждого его элемента выполнить константное число операций.
# Из леммы Рассмотрим правило <tex>(2 следует)</tex>. Можно считать, что любая ситуация, рассматриваемая в правиле внутри цикла <tex>(2*)</tex>рассматриваются те и только те ситуации, должна быть добавлена которые удовлетворяют условию (так как она удовлетворяет условию и ее еще нет список таких ситуаций можно по нетерминалу получить за <tex>O(1)</tex> следующим образом: каждый раз, когда мы добавляем ситацаию вида <tex>[A \rightarrow \alpha \cdot B \beta, i]</tex> в <tex>D_j</tex>, мы просмотрим в заранее заготовленном массиве для <tex>D_j</tex>, есть ли в <tex>D_j</tex> ситуации вида <tex>[B \rightarrow \eta \cdot, j]</tex>. Если да, то добавим <tex>[A \rightarrow \alpha B \cdot \beta, i]</tex> в списке<tex>D_j</tex>.). Кроме тогоТогда каждая такая ситуация будет добавлена в список и, исходя из леммы 2, попытка добавления будет единственной. А так как по нетерминалу можно за лемме 1 всего в списке <tex>D_j</tex> находится <tex>O(1j)</tex> получить список ситуаций, удовлетворяющих условию, то суммарно за все итерации внешнего цикла while внутри цикла <tex>(*)</tex> будут приводить к добавлению в список новых ситуаций. Поэтому затрачиваемое время можно отнести на счет добавляемых ситуаций (на каждую из них будет затрачено константное время), а не на счет ситуации, рассматриваемой в цикле рассмотрено <tex>O(*j)</tex>ситуаций.
# Так как грамматика фиксирована, то при применении правила <tex>(3)</tex> при рассмотрении любой ситуации количество включаемых ситуаций не превосходит некоторой константы, поэтому для каждой рассмотренной ситуации будет выполнено <tex>O(1)</tex> операций.
Таким образом, на каждую ситуацию в каждом списке тратится построение списка <tex>D_j</tex> будет потрачено <tex>O(1j)</tex> операций. Тогда, учитывая лемму 1, получаем, что время работы алгоритма составляет <tex>O(n^2)</tex>.
}}
==ЛитератураСм. также ==* [[Алгоритм_Эрли | Алгоритм Эрли]]* [[Алгоритм_Кока-Янгера-Касами_разбора_грамматики_в_НФХ | Алгоритм Кока-Янгера-Касами разбора грамматики в НФХ ]] == Источники информации==*А. Ахо, Дж. Ульман. Теория синтакcического анализа, перевода и компиляции. Том 1. Синтакcический анализ.Издательство "Мир", Москва, 1978г., стр. 364-366 [[Категория: Теория формальных языков]][[Категория: Контекстно-свободные грамматики]][[Категория: Алгоритмы разбора]]
317
правок

Навигация