Алгоритм Эрли, доказательство оценки O(n^2) для однозначной грамматики — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 17: Строка 17:
 
         <tex>I_j' = I_j''</tex>
 
         <tex>I_j' = I_j''</tex>
 
         <tex>I_j'' = \varnothing</tex>
 
         <tex>I_j'' = \varnothing</tex>
         for <tex>[B \rightarrow \eta \cdot , i] \in I_j'</tex>
+
         for <tex>[B \rightarrow \eta \cdot , i] \in I_j'</tex> # (*)
 
             for <tex>[A \rightarrow \alpha \cdot B \beta, k] \in I_{i}</tex>
 
             for <tex>[A \rightarrow \alpha \cdot B \beta, k] \in I_{i}</tex>
                 <tex>I_j</tex> &cup;= <tex>[A \rightarrow \alpha B \cdot \beta, k]</tex> # Правило (2)
+
                 <tex>I_j''</tex> &cup;= <tex>[A \rightarrow \alpha B \cdot \beta, k]</tex> # Правило (2)
 
              
 
              
         for <tex>[B \rightarrow \alpha \cdot A \eta, k] \in I_j'</tex>
+
         for <tex>[B \rightarrow \alpha \cdot A \eta, k] \in I_j'</tex> # (**)
 
             for <tex>\beta : (A \rightarrow \beta) \in P</tex>
 
             for <tex>\beta : (A \rightarrow \beta) \in P</tex>
                 <tex>I_j</tex> &cup;= <tex>[A \rightarrow \cdot \beta, j]</tex> # Правило (3)
+
                 <tex>I_j''</tex> &cup;= <tex>[A \rightarrow \cdot \beta, j]</tex> # Правило (3)
 
         <tex>I_j</tex> &cup;= <tex>I_j''</tex>
 
         <tex>I_j</tex> &cup;= <tex>I_j''</tex>
  
 +
В циклах, помеченных <tex>(*)</tex> и <tex>(**)</tex> просматривается не весь список <tex>I_j</tex>, а только те ситуации, которые еще не были просмотрены. Данная модификация является корректной.
 +
# Рассмотрим цикл <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>.
 
==Время работы для однозначной грамматики==
 
==Время работы для однозначной грамматики==
 
{{Лемма
 
{{Лемма

Версия 09:01, 19 января 2012

Алгоритм

Для начала модифицируем алгоритм Эрли.

Удалим из входной грамматики [math]\varepsilon[/math]-правила и бесполезные символы. В итоге получится грамматика [math]G = (N, \Sigma, P, S)[/math].

[math]I_0[/math] = [math]\{[S \rightarrow \cdot \alpha, 0] \, | \, S \rightarrow \alpha \in P\}[/math] # Правило (0) — инициализация
useful_loop(0)

for i = 1..n
    for [math][A \rightarrow \alpha \cdot a_{j} \beta, i] \in I_{j-1}[/math]
        [math]I_j[/math] ∪= [math][A \rightarrow \alpha a_{j} \cdot \beta, i][/math] # Правило (1)
    useful_loop(j)
function useful_loop(j):
    [math]I_j'' = I_j[/math]
    while [math]I_j'' \ne \varnothing[/math]
        [math]I_j' = I_j''[/math]
        [math]I_j'' = \varnothing[/math]
        for [math][B \rightarrow \eta \cdot , i] \in I_j'[/math] # (*)
            for [math][A \rightarrow \alpha \cdot B \beta, k] \in I_{i}[/math]
                [math]I_j''[/math] ∪= [math][A \rightarrow \alpha B \cdot \beta, k][/math] # Правило (2)
            
        for [math][B \rightarrow \alpha \cdot A \eta, k] \in I_j'[/math] # (**)
            for [math]\beta : (A \rightarrow \beta) \in P[/math]
                [math]I_j''[/math] ∪= [math][A \rightarrow \cdot \beta, j][/math] # Правило (3)
        [math]I_j[/math] ∪= [math]I_j''[/math]

В циклах, помеченных [math](*)[/math] и [math](**)[/math] просматривается не весь список [math]I_j[/math], а только те ситуации, которые еще не были просмотрены. Данная модификация является корректной.

  1. Рассмотрим цикл [math](*)[/math]. Если в текущей ситуации [math][B \rightarrow \eta \cdot, i][/math] этого цикла [math]i \ne j[/math], то во внутреннем цикле просматривается список с меньшим индексом, в который новые ситуации больше не добавляются. Поэтому после первого просмотра этого списка будут добавлены все ситуации, удовлетворяющие условию, и больше ситуацию [math][B \rightarrow \eta \cdot, i][/math] в цикле [math](*)[/math] рассматривать не нужно. Если же [math]i = j[/math], то [math]\eta \Rightarrow^* \varepsilon[/math], что возможно, только если [math]B = S \, , \eta = \varepsilon[/math]. Тогда во внутреннем цикле не будет добавлено ни одной ситуации, так как [math]S[/math] не встречается в правых частях правил.
  2. Теперь рассмотрим цикл [math](**)[/math].

Время работы для однозначной грамматики

Лемма (1):
[math]\forall\,j: 1 \le j \le n[/math] в списке [math]I_j[/math] находится [math]O(j)[/math] ситуаций.
Доказательство:
[math]\triangleright[/math]
Так как грамматика фиксирована, то [math]\forall i[/math] количество ситуаций вида [math][A \rightarrow \alpha \cdot \beta, i][/math] не больше некоторой константы. Таким образом, поскольку в [math]I_j[/math] находятся ситуации, у которых [math]0 \le i \le j[/math], всего в [math]I_j[/math] будет [math]O(j)[/math] ситуаций.
[math]\triangleleft[/math]


Лемма (2):
Пусть [math]G = (N, \Sigma, P, S)[/math] — однозначная КС-грамматика без непорождающих нетерминалов и [math]a_1 \dots a_n[/math] — цепочка из [math]\Sigma^*[/math]. Тогда алгоритм Эрли пытается включить [math][A \rightarrow \alpha \cdot \beta, i][/math] в [math]I_j[/math] не более одного раза, если [math]\alpha \ne \varepsilon[/math].
Доказательство:
[math]\triangleright[/math]

Ситуацию [math][A \rightarrow \alpha \cdot \beta, i][/math] можно включить в [math]I_j[/math] только по правилам [math](1)[/math] (если последний символ [math]\alpha[/math] — терминал) и [math](2)[/math] (если нетерминал). В первом случае результат очевиден. Во втором случае допустим, что [math][A \rightarrow \alpha'B \cdot \beta, i][/math] включается в [math]I_j[/math], когда рассматриваются две различные ситуации [math][B \rightarrow \eta_1 \cdot, k_1][/math] и [math][B \rightarrow \eta_2 \cdot, k_2][/math]. Тогда ситуация [math][A \rightarrow \alpha' \cdot B\beta, i][/math] должна оказаться одновременно в [math]I_{k_1}[/math] и в [math]I_{k_2}[/math]. Таким образом, получаем:

  • [math]\alpha' \Rightarrow^* a_{i+1} \ldots a_{k_1}[/math] и [math]\alpha' \Rightarrow^* a_{i+1} \ldots a_{k_2}[/math]
  • [math]\eta_1 \Rightarrow^* a_{k_1+1} \ldots a_j[/math] и [math]\eta_2 \Rightarrow^* a_{k_2+1} \ldots a_j[/math]

Следовательно

  • [math]\alpha' \eta_1 \Rightarrow^* a_{i+1} \ldots a_j[/math] и [math]\alpha' \eta_2 \Rightarrow^* a_{i+1} \ldots a_j[/math]

Заметим, что [math]S \Rightarrow^* \gamma A \delta \Rightarrow^* a_1 \ldots a_i A \delta \Rightarrow a_1 \ldots a_i \alpha' B \beta \delta[/math]. Предположим, что [math]\beta \delta \Rightarrow^* w'[/math]. Тогда:

  • [math]S \Rightarrow^* a_1 \ldots a_i \alpha' \eta_1 w'[/math] и [math]S \Rightarrow^* a_1 \ldots a_i \alpha' \eta_2 w'[/math]
Таким образом, если [math]k_1 \ne k_2[/math], то подстрока [math]a_{i+1} \ldots a_j[/math] выводится двумя различными способами из [math]\alpha' \eta_1[/math] и [math]\alpha' \eta_2[/math], то есть у строки [math]a_1 \ldots a_jw'[/math] есть два различных вывода, что противоречит однозначности грамматики. Если же [math]k_1 = k_2[/math], то [math]\eta_1 \ne \eta_2[/math], что приводит к аналогичному противоречию.
[math]\triangleleft[/math]


Теорема:
Если входная грамматика однозначна, то время выполнения алгоритма Эрли для слова длины [math]n[/math] составляет [math]O(n^2)[/math].
Доказательство:
[math]\triangleright[/math]

Орагнизуем каждый список разбора [math]I_j[/math] таким образом, чтобы по любому символу [math]x \in \Sigma \cup N[/math], можно было за [math]O(1)[/math] получить список тех и только тех ситуаций, содержащихся в [math]I_j[/math], которые имеют вид [math][A \rightarrow \alpha \cdot x \beta, j][/math].

Время построения [math]I_0[/math] не зависит от входной строки.

Рассмотрим [math]I_j, \, j \gt 0[/math].

  1. При включении ситуаций по правилу [math](1)[/math] необходимо лишь просмотреть предыдущий список и для каждого его элемента выполнить константное число операций.
  2. Если применяется правило [math](2)[/math], то в некотором списке [math]I_k[/math] для [math]k \le j[/math] надо просмотреть все ситуации, содержащие [math]"\cdot B"[/math] для некоторого конкретного [math]B[/math]. Для каждой такой ситуации в [math]I_j[/math] включается другая ситуация, и это время относится не к рассматриваемой ситуации, а к включаемой. Кроме того, так как по второй лемме для каждой ситуации предпринимается только одна попытка включить ее в список, то не нужно тратить время на проверку того, что включаемая ситуация уже есть в списке.
  3. Так как грамматика фиксирована, то при применении правила [math](3)[/math] при рассмотрении любой ситуации количество включаемых ситуаций не превосходит некоторой константы, поэтому на рассматриваемую ситуацию будет потрачено [math]O(1)[/math] операций.
Таким образом, на каждую ситуацию в каждом списке тратится [math]O(1)[/math] операций. Тогда, учитывая лемму 1, получаем, что время работы алгоритма составляет [math]O(n^2)[/math].
[math]\triangleleft[/math]

Литература

  • А. Ахо, Дж. Ульман. Теория синтакcического анализа, перевода и компиляции. Том 1. Синтакcический анализ.