Języki, automaty i obliczenia/Wykład 8: Dalsze algorytmy dla języków regularnych. Problemy rozstrzygalne: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Matiunreal (dyskusja | edycje)
Nie podano opisu zmian
m Zastępowanie tekstu – „,...,” na „,\ldots,”
 
(Nie pokazano 61 wersji utworzonych przez 5 użytkowników)
Linia 1: Linia 1:
W tym wykładzie  przedstawimy  algorytmy konstrukcji gramatyki regularnej, automatu skończenie stanowego oraz wyrażeń regularnych opisujących ten sam język <math>\displaystyle L</math>. Omówimy także problemy rozstrzygalne algorytmicznie w klasie języków regularnych.
W tym wykładzie  przedstawimy  algorytmy konstrukcji gramatyki regularnej, automatu skończenie stanowego oraz wyrażeń regularnych opisujących ten sam język <math>L</math>. Omówimy także problemy rozstrzygalne algorytmicznie w klasie języków regularnych.
 
__FORCETOC__
==1. Dalsze algorytmy języków regularnych==
==Dalsze algorytmy języków regularnych==


Dowód twierdzenia z ostatniego wykładu, w którym udowodniliśmy  równoważność
Dowód twierdzenia z ostatniego wykładu, w którym udowodniliśmy  równoważność
rozpoznawania języka <math>\displaystyle L</math> i generowania tego języka przez gramatykę regularną
rozpoznawania języka <math>L</math> i generowania tego języka przez gramatykę regularną
daje podstawę do określenia dwóch algorytmów. Algorytmu konstruującego
daje podstawę do określenia dwóch algorytmów. Algorytmu konstruującego
automat skończony w oparciu o daną gramatykę regularną i oczywiście
automat skończony w oparciu o daną gramatykę regularną i oczywiście
Linia 13: Linia 13:


Idea działania algorytmu ''Automat2GReg'' jest następująca: każdy
Idea działania algorytmu ''Automat2GReg'' jest następująca: każdy
symbol nieterminalny <math>\displaystyle v</math> tworzonej
symbol nieterminalny <math>v</math> tworzonej
gramatyki odpowiada pewnemu stanowi <math>\displaystyle s_v</math> automatu. Jeśli w automacie pod wpływem
gramatyki odpowiada pewnemu stanowi <math>s_v</math> automatu. Jeśli w automacie pod wpływem
litery <math>\displaystyle a</math> następuje przejście
litery <math>a</math> następuje przejście
ze stanu <math>\displaystyle s_v</math> do stanu <math>\displaystyle s_w</math>, to do zbioru praw gramatyki dodawane jest prawo
ze stanu <math>s_v</math> do stanu <math>s_w</math>, to do zbioru praw gramatyki dodawane jest prawo
<math>\displaystyle s_v \rightarrow as_w</math>. Ponadto,
<math>s_v \rightarrow as_w</math>. Ponadto,
jeśli stan <math>\displaystyle s_w</math> jest stanem końcowym, to dodajemy także prawo <math>\displaystyle s_w \rightarrow 1</math>,
jeśli stan <math>s_w</math> jest stanem końcowym, to dodajemy także prawo <math>s_w \rightarrow 1</math>,
aby w danym miejscu wywód słowa
aby w danym miejscu wywód słowa
mógł zostać zakończony.
mógł zostać zakończony.


{{algorytm|''Automat2GReg'' -- buduje gramatykę regularną dla
{{algorytm|''Automat2GReg'' - buduje gramatykę regularną dla
zadanego automatu skończonego.||
zadanego automatu skończonego.||


Wejście: <math>\displaystyle \mathcal{A}=(S, A, f, s_0, T)</math> -- automat
  1  Wejście: <math>\mathcal{A}=(S, A, f, s_0, T)</math> - automat niedeterministyczny.
niedeterministyczny.
  2  Wyjście: <math>G=(V_N, V_T, P, v_0)</math> - gramatyka regularna taka, że <math>L(G)=L(\mathcal{A})</math>.
 
  3  <math>V_N \leftarrow \{v_0,v_1,\ldots,v_{|S|-1}\}</math>;
Wyjście: <math>\displaystyle G=(V_N, V_T, P, v_0)</math> -- gramatyka regularna taka,  
  4  <math>V_T \leftarrow A</math>;
że <math>\displaystyle L(G)=L(\mathcal{A})</math>.
  5  <math>v_0 \leftarrow s_0</math>;
 
  6  <math>P \leftarrow \emptyset</math>;
<math>\displaystyle V_N \leftarrow \{v_0,v_1,...,v_{|S|-1}\}</math>;
  7  '''for''' '''each ''' <math>s_i\in S</math> '''do'''
 
  8    '''for''' '''each ''' <math>a\in A</math> '''do'''
<math>\displaystyle V_T \leftarrow A</math>;
  9      '''if''' <math>f(s_i,a)\neq</math>'''NULL''' '''then'''
 
  10        <math>s_j \leftarrow f(s_i,a)</math>;                 <math>\triangleright</math> funkcja <math>f</math> jest określona na <math>(s_i,a)</math>
<math>\displaystyle v_0 \leftarrow s_0</math>;
  11        <math>P \leftarrow P \cup \{s_i \rightarrow as_j\}</math>;
 
  12        '''if''' <math>s_j \in T</math> '''then'''
<math>\displaystyle P \leftarrow \emptyset</math>;
  13          <math>P \leftarrow P \cup \{s_j \rightarrow 1\}</math>;
 
  14        '''end if'''
'''for''' '''each ''' <math>\displaystyle s_i\in S</math>  
  15      '''end if'''
 
  16    '''end for'''
'''for''' '''each ''' <math>\displaystyle a\in A</math>  
  17  '''end for'''
 
  18  '''return''' <math>G</math>;
'''if''' <math>\displaystyle f(s_i,a)\neq</math>'''NULL'''  
<math>\displaystyle s_j \leftarrow f(s_i,a)</math>; <math>\displaystyle \triangleright</math> funkcja
<math>\displaystyle f</math> jest określona na <math>\displaystyle (s_i,a)\displaystyle P \leftarrow P \cup \{s_i \rightarrow as_j\}</math>;
 
'''if''' <math>\displaystyle s_j \in T\displaystyle P \leftarrow P \cup \{s_j \rightarrow 1\}</math>;
 
'''endif''' '''endif'''
 
'''endfor'''
 
'''endfor'''
 
'''return''' <math>\displaystyle G</math>;
 
}}
}}


Oznaczmy przez <math>\displaystyle E(\mathcal{A})</math>  ilość krawędzi w grafie
Oznaczmy przez <math>E(\mathcal{A})</math>  ilość krawędzi w grafie
<math>\displaystyle n</math>-stanowego automatu niedeterministycznego <math>\displaystyle \mathcal{A}</math>.
<math>n</math>-stanowego automatu niedeterministycznego <math>\mathcal{A}</math>.
Złożoność czasowa liczona względem <math>\displaystyle E(\mathcal{A})</math> jest liniowa i
Złożoność czasowa liczona względem <math>E(\mathcal{A})</math> jest liniowa i
równa <math>\displaystyle O(E(\mathcal{A}))</math>. Również złożoność pamięciowa jest liniowa
równa <math>O(E(\mathcal{A}))</math>. Również złożoność pamięciowa jest liniowa
i wynosi <math>\displaystyle O(|S|+E(\mathcal{A}))</math>.
i wynosi <math>O(|S|+E(\mathcal{A}))</math>.


{{przyklad|||
<span id="przyklad_1_1">{{przyklad|1.1||


Niech dany będzie automat <math>\displaystyle \mathcal{A}</math> pokazany na rysunku
Niech dany będzie automat <math>\mathcal{A}</math> pokazany na rysunku 1. Zbudujemy gramatykę, która będzie
[[##ja-lekcja8-w-rys1|Uzupelnic ja-lekcja8-w-rys1|]]. Zbudujemy gramatykę, która będzie
generowała język akceptowany przez <math>\mathcal{A}</math>.}}
generowała język akceptowany przez <math>\displaystyle \mathcal{A}</math>.}}
<center>
<center>
<div class="thumb"><div style="width:250px;">
[[File:ja-lekcja08-w-rys1.svg|250x150px|thumb|center|Rysunek 1]]
<flash>file=ja-lekcja08-w-rys1.swf|width=250|height=150</flash>
<div.thumbcaption>ja-lekcja08-w-rys1</div>
</div></div>
</center>
</center>


Ponieważ <math>\displaystyle f(s_0,a)=s_0</math>, a ponadto <math>\displaystyle s_0</math> jest stanem końcowym, do
Ponieważ <math>f(s_0,a)=s_0</math>, a ponadto <math>s_0</math> jest stanem końcowym, do
<math>\displaystyle P</math> dodajemy produkcje <math>\displaystyle v_0 \rightarrow av_0</math> oraz <math>\displaystyle v_0 \rightarrow
<math>P</math> dodajemy produkcje <math>v_0 \rightarrow av_0</math> oraz <math>v_0 \rightarrow
1</math>. Dodajemy także produkcję <math>\displaystyle v_0 \rightarrow bv_1</math>, gdyż mamy
1</math>. Dodajemy także produkcję <math>v_0 \rightarrow bv_1</math>, gdyż mamy
<math>\displaystyle f(s_0,b)=s_1</math>.
<math>f(s_0,b)=s_1</math>.


Fakt, że <math>\displaystyle f(s_1,b)=s_1</math> oraz <math>\displaystyle f(s_1,a)=s_2</math> sprawia, że do <math>\displaystyle P</math>
Fakt, że <math>f(s_1,b)=s_1</math> oraz <math>f(s_1,a)=s_2</math> sprawia, że do <math>P</math>
dodajemy: <math>\displaystyle v_1\ \rightarrow bv_1</math>, <math>\displaystyle v_1 \rightarrow av_2</math>.
dodajemy: <math>v_1\ \rightarrow bv_1</math>, <math>v_1 \rightarrow av_2</math>.


Ponieważ <math>\displaystyle f(s_2, a)=s_1</math> do <math>\displaystyle P</math> dodajemy: <math>\displaystyle v_2 \rightarrow av_1</math>
Ponieważ <math>f(s_2, a)=s_1</math> do <math>P</math> dodajemy: <math>v_2 \rightarrow av_1</math>
oraz <math>\displaystyle v_2 \rightarrow 1</math>, gdyż <math>\displaystyle s_2 \in T</math>.
oraz <math>v_2 \rightarrow 1</math>, gdyż <math>s_2 \in T</math>.


Symbolem początkowym nowej gramatyki jest symbol
Symbolem początkowym nowej gramatyki jest symbol
<math>\displaystyle v_0</math>. Ostatecznie gramatyka ma postać: {-.5cm}
<math>v_0</math>. Ostatecznie gramatyka ma postać:  


<center><math>\displaystyle \aligned \nonumber v_0 & \rightarrow &  av_0\ |\ bv_1 \ |\ 1\\
<center><math>\begin{align}  v_0 & \rightarrow &  av_0\ |\ bv_1 \ |\ 1\\
\nonumber v_1 & \rightarrow & bv_1\ |\ av_2 \\
v_1 & \rightarrow & bv_1\ |\ av_2 \\
\nonumber v_2 & \rightarrow & av_1\ |\ 1
v_2 & \rightarrow & av_1\ |\ 1
\endaligned</math></center>
\end{align}</math></center>


Zwróćmy uwagę na wygodny zapis produkcji gramatyki, jaki został
Zwróćmy uwagę na wygodny zapis produkcji gramatyki, jaki został
Linia 106: Linia 88:


Zauważmy, że gramatyka podana na wejściu algorytmu nie może zawierać  
Zauważmy, że gramatyka podana na wejściu algorytmu nie może zawierać  
produkcji postaci <math>\displaystyle v \rightarrow x</math>, gdzie <math>\displaystyle v \in V_N</math>, <math>\displaystyle x \in (V_N  
produkcji postaci <math>v \rightarrow x</math>, gdzie <math>v \in V_N</math>, <math>x \in (V_N  
\cup V_T)</math>. Jeśli gramatyka zawiera takie produkcje, to możemy się  
\cup V_T)</math>. Jeśli gramatyka zawiera takie produkcje, to możemy się  
ich łatwo pozbyć, zgodnie z Twierdzeniem 2.2 z Wykładu 7, uzyskując  
ich łatwo pozbyć, zgodnie z Twierdzeniem 2.2 z Wykładu 7 (patrz [[Języki, automaty i obliczenia/Wykład 7: Twierdzenie Kleene'ego. Własności języków i gramatyk regularnych#twierdzenie_2_2|twierdzenie 2.2 wykład 7]]), uzyskując oczywiście gramatykę równoważną.
oczywiście gramatykę równoważną.


Idea działania algorytmu jest podobna do poprzedniej -- każdy
Idea działania algorytmu jest podobna do poprzedniej - każdy
tworzony stan automatu odpowiadać będzie pewnemu symbolowi
tworzony stan automatu odpowiadać będzie pewnemu symbolowi
nieterminalnemu gramatyki wejściowej. Zależnie od postaci produkcji
nieterminalnemu gramatyki wejściowej. Zależnie od postaci produkcji
gramatyki, niektóre stany będą stanami końcowymi.
gramatyki, niektóre stany będą stanami końcowymi.


Zapis <math>\displaystyle f \leftarrow\displaystyle \ </math> w linii 7. symbolicznie oznacza, że
Zapis <math>f \leftarrow\emptyset</math> w linii 7. symbolicznie oznacza, że
funkcja przejść nie jest jeszcze określona.
funkcja przejść nie jest jeszcze określona.


W pętli 8.-15. pozbywamy się produkcji, w których występuje więcej
W pętli 8.-15. pozbywamy się produkcji, w których występuje więcej
niż jeden terminal; każdą taką produkcję "rozbijamy" na sekwencję
niż jeden terminal; każdą taką produkcję "rozbijamy" na sekwencję
produkcji postaci <math>\displaystyle v \rightarrow aw</math>, gdzie <math>\displaystyle v,w \in V_N,\ a \in
produkcji postaci <math>v \rightarrow aw</math>, gdzie <math>v,w \in V_N,\ a \in
V_T</math>.
V_T</math>.


{{algorytm|||
{{algorytm|''GReg2Automat'' - buduje automat dla zadanej
 
gramatyki regularnej.||
{''GReg2Automat'' -- buduje automat dla zadanej
gramatyki regularnej.}
 
[1]
 
Wejście: <math>\displaystyle G=(V_N, V_T, P, v_0)</math> -- gramatyka regularna.
 
Wyjście: <math>\displaystyle \mathcal{A}=(S, A, f, v_0, T)</math> -- automat taki, że
<math>\displaystyle L(\mathcal{A})=L(G)</math>.
 
<math>\displaystyle S \leftarrow\displaystyle V_N</math>;
 
<math>\displaystyle A \leftarrow V_T</math>;
 
<math>\displaystyle T \leftarrow </math>; <math>\displaystyle \triangleright</math> nie ma jeszcze
stanów końcowych
 
<math>\displaystyle s_0 \leftarrow v_0</math>;
 
<math>\displaystyle f \leftarrow </math>; <math>\displaystyle \triangleright</math> funkcja <math>\displaystyle f</math> nie
jest określona dla żadnego argumentu
 
'''for''' '''each ''' <math>\displaystyle (v_i \rightarrow a_1a_2...a_nv_j) \in P</math>
 
'''if''' <math>\displaystyle n>1\displaystyle V_N \leftarrow V_N \cup \{v^{a_1}, ..., v^{a_{n-1}}\}</math>;
<math>\displaystyle \triangleright</math> rozbijamy produkcję na kilka prostszych
 
<math>\displaystyle P \leftarrow P \backslash \{v_i \rightarrow
a_1a_2...a_nv_j\};\displaystyle \triangleright</math> w tym celu usuwamy
produkcję z <math>\displaystyle P\displaystyle P \leftarrow P \cup  \{v_i \rightarrow a_1v^{a_1},
v^{a_{n-1}} \rightarrow a_nv_j\}</math>; <math>\displaystyle \triangleright</math> w zamian
dodając ciąg krótszych
 
<math>\displaystyle P \leftarrow P \cup  \{v^{a_1} \rightarrow a_2v^{a_2},
\ldots, v^{a_{n-2}} \rightarrow a_{n-1}v^{a_{n-1}} \}</math>;
 
'''endif'''
 
'''endfor'''
 
<math>\displaystyle \triangleright</math> wszystkie produkcje są postaci
<math>\displaystyle u\rightarrow a v</math> lub <math>\displaystyle u\rightarrow 1</math>, gdzie <math>\displaystyle u,v\in V_N</math>, <math>\displaystyle a\in
V_T</math>
 
'''for''' '''each '''<math>\displaystyle (s_i \rightarrow as_j) \in P\displaystyle f(s_i, a) \leftarrow s_j</math>;
 
'''endfor'''
 
'''for''' '''each ''' <math>\displaystyle (v_i \rightarrow 1) \in P\displaystyle T \leftarrow T \cup \{v_i\}</math>;
 
'''endfor'''
 
'''return''' <math>\displaystyle \mathcal{A}=(S, A, f, s_0, T)</math>;


}}
  1  Wejście: <math>G=(V_N, V_T, P, v_0)</math> - gramatyka regularna.
  2  Wyjście: <math>\mathcal{A}=(S, A, f, v_0, T)</math> - automat taki, że <math>L(\mathcal{A})=L(G)</math>.
  3  <math>S \leftarrow V_N</math>;
  4  <math>A \leftarrow V_T</math>;
  5  <math>T \leftarrow \emptyset</math>;                                  <math>\triangleright</math> nie ma jeszcze stanów końcowych
  6  <math>s_0 \leftarrow v_0</math>;
  7  <math>f \leftarrow \emptyset</math>;                <math>\triangleright</math> funkcja <math>f</math> nie jest określona dla żadnego argumentu
  8  '''for''' '''each ''' <math>(v_i \rightarrow a_1a_2...a_nv_j) \in P</math> '''do'''
  9    '''if''' <math>n>1</math> '''then'''
  10      <math>V_N \leftarrow V_N \cup \{v^{a_1}, ..., v^{a_{n-1}}\}</math>;    <math>\triangleright</math> rozbijamy produkcję na kilka prostszych
  11      <math>P \leftarrow P \backslash \{v_i \rightarrow a_1a_2...a_nv_j\}</math>;          <math>\triangleright</math> w tym celu usuwamy produkcję z <math>P</math>
  12      <math>P \leftarrow P \cup  \{v_i \rightarrow a_1v^{a_1}, v^{a_{n-1}} \rightarrow a_nv_j\}</math>;    <math>\triangleright</math> w zamian dodając ciąg krótszych
  13      <math>P \leftarrow P \cup  \{v^{a_1} \rightarrow a_2v^{a_2}, \ldots, v^{a_{n-2}} \rightarrow a_{n-1}v^{a_{n-1}} \}</math>;
  14    '''end if'''
  15  '''end for'''
  16    <math>\triangleright</math> wszystkie produkcje są postaci <math>u\rightarrow a v</math> lub <math>u\rightarrow 1</math>, gdzie <math>u,v\in V_N</math>, <math>a\in V_T</math>
  17  '''for''' '''each '''<math>(s_i \rightarrow as_j) \in P</math> '''do'''
  18    <math>f(s_i, a) \leftarrow s_j</math>;
  19  '''end for'''
  20  '''for''' '''each ''' <math>(v_i \rightarrow 1) \in P</math> '''do'''
  21    <math>T \leftarrow T \cup \{v_i\}</math>;
  22  '''end for'''
  23  '''return''' <math>\mathcal{A}=(S, A, f, s_0, T)</math>;
}}</span>


{{przyklad|||
{{przyklad|1.2||
Jako wejście algorytmu rozważmy gramatykę z przykładu
Jako wejście algorytmu rozważmy gramatykę z przykładu 1.1 (patrz [[#przyklad_1_1|przykład 1.1.]])
[[##przyklad_automat2gramatyka|Uzupelnic przyklad_automat2gramatyka|]]. Używając algorytmu
Używając algorytmu
''Greg2Automat'', zbudujemy dla niej automat akceptujący język
''Greg2Automat'', zbudujemy dla niej automat akceptujący język
przez nią generowany.
przez nią generowany.


Mamy <math>\displaystyle S=\{s_0, s_1, s_2\}</math>. W liniach 17. -- 19. określana jest  
Mamy <math>S=\{s_0, s_1, s_2\}</math>. W liniach 17. -- 19. określana jest  
funkcja przejścia: <math>\displaystyle f(s_0, a)=s_0</math>, <math>\displaystyle f(s_0, b)=s_1</math>, <math>\displaystyle f(s_1,  
funkcja przejścia: <math>f(s_0, a)=s_0</math>, <math>f(s_0, b)=s_1</math>, <math>f(s_1,  
b)=s_1</math>, <math>\displaystyle f(s_1, a)=s_2</math> oraz <math>\displaystyle f(s_2, a)=s_1</math>.
b)=s_1</math>, <math>f(s_1, a)=s_2</math> oraz <math>f(s_2, a)=s_1</math>.


Pętla w liniach 20. -- 22. przebiega po dwóch produkcjach: <math>\displaystyle v_0
Pętla w liniach 20. - 22. przebiega po dwóch produkcjach: <math>v_0
\rightarrow 1</math> oraz <math>\displaystyle v_2 \rightarrow 1</math>, dodaje zatem do zbioru <math>\displaystyle T</math>
\rightarrow 1</math> oraz <math>v_2 \rightarrow 1</math>, dodaje zatem do zbioru <math>T</math>
stanów końcowych stany <math>\displaystyle s_0</math> oraz <math>\displaystyle s_2</math>. Szukany automat to automat z poprzedniego przykładu; przedstawiony jest na rysunku
stanów końcowych stany <math>s_0</math> oraz <math>s_2</math>. Szukany automat to automat z poprzedniego przykładu; przedstawiony jest na rysunku 1.
[[##ja-lekcja8-w-rys1|Uzupelnic ja-lekcja8-w-rys1|]].
}}
}}


Automat powstały w wyniku działania algorytmu ''GReg2Automat''
Automat powstały w wyniku działania algorytmu ''GReg2Automat''
nie musi być automatem deterministycznym (wystarczy, że w zbiorze
nie musi być automatem deterministycznym (wystarczy, że w zbiorze
produkcji znajdą się dwie produkcje postaci <math>\displaystyle v_i \rightarrow av_j</math>
produkcji znajdą się dwie produkcje postaci <math>v_i \rightarrow av_j</math>
oraz <math>\displaystyle v_i \rightarrow av_k</math> dla pewnego <math>\displaystyle a \in A</math>), jednak po jego
oraz <math>v_i \rightarrow av_k</math> dla pewnego <math>a \in A</math>), jednak po jego
determinizacji i minimalizacji otrzymujemy minimalny automat
determinizacji i minimalizacji otrzymujemy minimalny automat
deterministyczny akceptujący język, który jest generowany przez
deterministyczny akceptujący język, który jest generowany przez
gramatyke podaną na wejście algorytmu.
gramatyke podaną na wejście algorytmu.


Złożoność czasowa jak i pamięciowa algorytmu wynosi <math>\displaystyle O(p)</math>, gdzie
Złożoność czasowa jak i pamięciowa algorytmu wynosi <math>O(p)</math>, gdzie
<math>\displaystyle p</math> jest liczbą produkcji występujących w zbiorze praw <math>\displaystyle P</math>
<math>p</math> jest liczbą produkcji występujących w zbiorze praw <math>P</math>
gramatyki.
gramatyki.


Linia 219: Linia 169:
regularnego.
regularnego.


Niech <math>\displaystyle a \in A, r,s \in \mathcal{WR}</math>. Najpierw pokażemy, że językom
Niech <math>a \in A, r,s \in \mathcal{WR}</math>. Najpierw pokażemy, że językom
odpowiadającym wyrażeniom regularnym , <math>\displaystyle 1</math>, <math>\displaystyle a</math>, <math>\displaystyle r+s</math>, <math>\displaystyle rs</math> oraz
odpowiadającym wyrażeniom regularnym , <math>1</math>, <math>a</math>, <math>r+s</math>, <math>rs</math> oraz
<math>\displaystyle r^*</math> można przyporządkować automaty akceptujące te języki, a
<math>r^*</math> można przyporządkować automaty akceptujące te języki, a
następnie podamy algorytm konstruowania automatu rozpoznającego
następnie podamy algorytm konstruowania automatu rozpoznającego
dowolne wyrażenie regularne.
dowolne wyrażenie regularne.


Na rysunku [[##ja-lekcja8-w-rys3|Uzupelnic ja-lekcja8-w-rys3|]] przedstawione są trzy automaty.  
Na rysunku 2 przedstawione są trzy automaty.  
Automat a) rozpoznaje język pusty, automat b) -- język <math>\displaystyle \{1\}</math>, a  
Automat a) rozpoznaje język pusty, automat b) - język <math>\{1\}</math>, a  
automat c) -- język <math>\displaystyle \{a\}</math>, dla <math>\displaystyle a \in A</math>.
automat c) - język <math>\{a\}</math>, dla <math>a \in A</math>.


'''RYSUNEK ja-lekcja8-w-rys3'''
<center>
[[File:ja-lekcja08-w-rys3.svg|250x250px|thumb|center|Rysunek 2]]
</center>


Niech dane będą automaty: <math>\displaystyle M_1</math>, akceptujący język opisywany
Niech dane będą automaty: <math>M_1</math>, akceptujący język opisywany
wyrażeniem <math>\displaystyle r</math> oraz <math>\displaystyle M_2</math>, akceptujący język opisywany wyrażeniem
wyrażeniem <math>r</math> oraz <math>M_2</math>, akceptujący język opisywany wyrażeniem
<math>\displaystyle s</math>. Na rysunku [[##ja-lekcja8-w-rys4|Uzupelnic ja-lekcja8-w-rys4|]] przedstawiono konstrukcje
<math>s</math>. Na rysunku 3 przedstawiono konstrukcje
automatów akceptujących wyrażenia regularne <math>\displaystyle r+s</math> (automat a)),
automatów akceptujących wyrażenia regularne <math>r+s</math> (automat a)),
<math>\displaystyle (rs)</math> (automat b)) oraz <math>\displaystyle r^*</math> (automat c)).
<math>(rs)</math> (automat b)) oraz <math>r^*</math> (automat c)).
 
<center>
'''RYSUNEK ja-lekcja8-w-rys4'''
[[File:ja-lekcja08-w-rys4.svg|250x250px|thumb|center|Rysunek 3]]
</center>


W automacie a) stan <math>\displaystyle q_0</math> jest stanem początkowym, stan <math>\displaystyle f_0</math> --
W automacie a) stan <math>q_0</math> jest stanem początkowym, stan <math>f_0</math> -
stanem końcowym, stany <math>\displaystyle q_1,q_2</math> oraz <math>\displaystyle f_1,f_2</math> oznaczają
stanem końcowym, stany <math>q_1,q_2</math> oraz <math>f_1,f_2</math> oznaczają
odpowiednio stany początkowe automatów <math>\displaystyle M_1</math> i <math>\displaystyle M_2</math> oraz stany
odpowiednio stany początkowe automatów <math>M_1</math> i <math>M_2</math> oraz stany
końcowe automatów <math>\displaystyle M_1</math> i <math>\displaystyle M_2</math>.
końcowe automatów <math>M_1</math> i <math>M_2</math>.


W automacie b) stan <math>\displaystyle q_0</math> jest jednocześnie jego stanem początkowym
W automacie b) stan <math>q_0</math> jest jednocześnie jego stanem początkowym
oraz stanem początkowym automatu <math>\displaystyle M_1</math>, stan <math>\displaystyle f_1</math> jest stanem
oraz stanem początkowym automatu <math>M_1</math>, stan <math>f_1</math> jest stanem
końcowym automatu b) i jednocześnie stanem końcowym automatu <math>\displaystyle M_2</math>.
końcowym automatu b) i jednocześnie stanem końcowym automatu <math>M_2</math>.
Stan <math>\displaystyle f_0</math> jest stanem końcowym w <math>\displaystyle M_1</math>, a <math>\displaystyle q_1</math> -- początkowym w
Stan <math>f_0</math> jest stanem końcowym w <math>M_1</math>, a <math>q_1</math> - początkowym w
<math>\displaystyle M_2</math>.
<math>M_2</math>.


W automacie c) stan <math>\displaystyle q_0</math> jest jego stanem początkowym a <math>\displaystyle f_0</math>
W automacie c) stan <math>q_0</math> jest jego stanem początkowym a <math>f_0</math>
końcowym. Stany <math>\displaystyle q_1</math> oraz <math>\displaystyle f_1</math> to odpowiednio początkowy i końcowy
końcowym. Stany <math>q_1</math> oraz <math>f_1</math> to odpowiednio początkowy i końcowy
stan automatu <math>\displaystyle M_1</math>.
stan automatu <math>M_1</math>.


Wyrażenia regularne można przedstawiać w postaci drzewa, w którym
Wyrażenia regularne można przedstawiać w postaci drzewa, w którym
Linia 259: Linia 212:
konkatenację lub iterację, czyli gwiazdkę Kleene'ego.
konkatenację lub iterację, czyli gwiazdkę Kleene'ego.


{{przyklad|||
{{przyklad|1.3||
Rozważmy wyrażenie regularne <math>\displaystyle r=(a^*b)(ab+c)</math>. Drzewo odpowiadające
Rozważmy wyrażenie regularne <math>r=(a^*b)(ab+c)</math>. Drzewo odpowiadające
<math>\displaystyle r</math> przedstawione jest na rysunku [[##ja-lekcja6-w-rys3|Uzupelnic ja-lekcja6-w-rys3|]]. Korzeniem
<math>r</math> przedstawione jest na rysunku 4. Korzeniem
jest wierzchołek z małą wchodzącą strzałką.
jest wierzchołek z małą wchodzącą strzałką.
}}
}}
 
<center>
'''RYSUNEK ja-lekcja8-w-rys5'''
[[File:ja-lekcja08-w-rys5.svg|350x250px|thumb|center|Rysunek 4]]
</center>


Powyższe konstrukcje będą stosowane podczas iteracyjnej budowy
Powyższe konstrukcje będą stosowane podczas iteracyjnej budowy
Linia 272: Linia 226:
będzie przeszukiwane metodą post-order (zaczynając od korzenia),
będzie przeszukiwane metodą post-order (zaczynając od korzenia),
tzn. najpierw rekurencyjnie przeszukiwane są poddrzewa danego węzła
tzn. najpierw rekurencyjnie przeszukiwane są poddrzewa danego węzła
<math>\displaystyle x</math>, a na końcu sam węzeł <math>\displaystyle x</math>. Dzięki temu, wchodząc do węzła <math>\displaystyle x</math>
<math>x</math>, a na końcu sam węzeł <math>x</math>. Dzięki temu, wchodząc do węzła <math>x</math>
drzewa etykietowanego daną operacją na wyrażeniu regularnym oba
drzewa etykietowanego daną operacją na wyrażeniu regularnym oba
poddrzewa <math>\displaystyle P</math> i <math>\displaystyle L</math> wierzchołka <math>\displaystyle x</math>  będą już reprezentowane przez
poddrzewa <math>P</math> i <math>L</math> wierzchołka <math>x</math>  będą już reprezentowane przez
automaty <math>\displaystyle \mathcal{A}_P</math> oraz <math>\displaystyle \mathcal{A}_L</math>. Teraz wystarczy
automaty <math>\mathcal{A}_P</math> oraz <math>\mathcal{A}_L</math>. Teraz wystarczy
zastosować jedną z konstrukcji z rysunku [[##ja-lekcja8-w-rys3|Uzupelnic ja-lekcja8-w-rys3|]] lub
zastosować jedną z konstrukcji z rysunku 2 lub 3. Procedurę powtarzamy do momentu, aż
[[##ja-lekcja8-w-rys4|Uzupelnic ja-lekcja8-w-rys4|]]. Procedurę powtarzamy do momentu, aż
przechodzenie drzewa zakończy się w korzeniu. Szukanym przez nas
przechodzenie drzewa zakończy się w korzeniu. Szukanym przez nas
automatem będzie automat "odpowiadający" korzeniowi drzewa.
automatem będzie automat "odpowiadający" korzeniowi drzewa.
Linia 286: Linia 239:
Wykorzystamy także dwie procedury, mianowicie  
Wykorzystamy także dwie procedury, mianowicie  
''CreateAutomata''(''type'') oraz  
''CreateAutomata''(''type'') oraz  
''JoinAutomata''(''type'',<math>\displaystyle \mathcal{M}_1,\mathcal{M}_2</math>).  
''JoinAutomata''(''type'',<math>\mathcal{M}_1,\mathcal{M}_2</math>).  
Zmienna ''type'' może przyjmować wartości '<math>\displaystyle a</math>', '<math>\displaystyle b</math>' lub '<math>\displaystyle c</math>'.  
Zmienna ''type'' może przyjmować wartości '<math>a</math>', '<math>b</math>' lub '<math>c</math>'.  
Funkcja zwraca automat ''CreateAutomata''(''type'')  
Funkcja zwraca automat ''CreateAutomata''(''type'')  
przedstawiony (zależnie od zmiennej ''type'') na rysunku  
przedstawiony (zależnie od zmiennej ''type'') na rysunku 2. Procedura  
[[##ja-lekcja8-w-rys3|Uzupelnic ja-lekcja8-w-rys3|]]. Procedura  
''JoinAutomata''(''type'',<math>\mathcal{M}_1,\mathcal{M}_2</math>)  
''JoinAutomata''(''type'',<math>\displaystyle \mathcal{M}_1,\mathcal{M}_2</math>)  
natomiast konstruuje na podstawie automatów <math>\mathcal{M}_1</math>,  
natomiast konstruuje na podstawie automatów <math>\displaystyle \mathcal{M}_1</math>,  
<math>\mathcal{M}_2</math> automat z rysunku 2, przy  
<math>\displaystyle \mathcal{M}_2</math> automat z rysunku <math>\displaystyle [[##ja-lekcja8-w-rys3|Uzupelnic ja-lekcja8-w-rys3|]]</math>, przy  
czym dla przypadku ''type''<nowiki>=</nowiki><math>c</math> automat <math>\mathcal{M}_2</math> jest bez  
czym dla przypadku ''type''<nowiki>=</nowiki><math>\displaystyle c</math> automat <math>\displaystyle \mathcal{M}_2</math> jest bez  
znaczenia. Ostatnią wykorzystaną procedurą będzie  
znaczenia. Ostatnią wykorzystaną procedurą będzie  
''BuildTree''(r), tworząca drzewo (binarne) dla wyrażenia  
''BuildTree''(r), tworząca drzewo (binarne) dla wyrażenia  
regularnego. Zakładamy, że studentowi doskonale jest znana Odwrotna  
regularnego. Zakładamy, że studentowi doskonale jest znana Odwrotna  
Notacja Polska i budowa takiego drzewa nie będzie dla niego  
Notacja Polska i budowa takiego drzewa nie będzie dla niego  
stanowiła problemu. Dla ustalenia uwagi zakładamy, że symbol <math>\displaystyle *</math>  
stanowiła problemu. Dla ustalenia uwagi zakładamy, że symbol <math>*</math>  
prowadzi zawsze do lewego dziecka.
prowadzi zawsze do lewego dziecka.


Poniżej przedstawiamy oznaczenia standardowych funkcji operujących
Poniżej przedstawiamy oznaczenia standardowych funkcji operujących
na drzewach. Funkcja <math>\displaystyle \textsc{Root}(T)</math> zwraca korzeń drzewa <math>\displaystyle T</math>,
na drzewach. Funkcja <math>\text{Root}(T)</math> zwraca korzeń drzewa <math>T</math>,
funkcje <math>\displaystyle \textsc{LeftChild}(T,v)</math> oraz <math>\displaystyle \textsc{RightChild}(T,v)</math>
funkcje <math>\text{LeftChild}(T,v)</math> oraz <math>\text{RightChild}(T,v)</math>
zwracają lewe i prawe dziecko wierzchołka <math>\displaystyle v</math> (ew. '''NULL''',  
zwracają lewe i prawe dziecko wierzchołka <math>v</math> (ew. '''NULL''',  
gdy brak lewego lub prawego dziecka), natomiast funkcja  
gdy brak lewego lub prawego dziecka), natomiast funkcja  
<math>\displaystyle \textsc{Label}(T,v)</math> zwraca etykietę wierzchołka <math>\displaystyle v</math> drzewa <math>\displaystyle T</math>.  
<math>\text{Label}(T,v)</math> zwraca etykietę wierzchołka <math>v</math> drzewa <math>T</math>.  
Funkcja <math>\displaystyle \textsc{IsLeaf}(T,v)</math> zwraca wartość <math>\displaystyle \textbf{true}</math>, gdy  
Funkcja <math>\text{IsLeaf}(T,v)</math> zwraca wartość <math>\textbf{true}</math>, gdy  
<math>\displaystyle v</math> jest liściem w drzewie <math>\displaystyle T</math> oraz <math>\displaystyle \textbf{false}</math> w przypadku  
<math>v</math> jest liściem w drzewie <math>T</math> oraz <math>\textbf{false}</math> w przypadku  
przeciwnym.
przeciwnym.


{{algorytm|||
{{algorytm|''Wr2Automat'' -- buduje automat rozpoznający język
 
opisywany wyrażeniem regularnym||
{''Wr2Automat'' -- buduje automat rozpoznający język
opisywany wyrażeniem regularnym}
 
[1]
Wejście: <math>\displaystyle r</math> -- wyrażenie regularne.
 
Wyjście: <math>\displaystyle \mathcal{A}=(S, A, f, s_0, T)</math> -- automat
rozpoznający język opisywany wyrażeniem <math>\displaystyle r</math>.
 
<math>\displaystyle T\leftarrow \textsc{BuildTree}(r)</math>;
 
<math>\displaystyle v_0 \leftarrow \textsc{Root}(T)</math>;
 
<math>\displaystyle \mathcal{A} \leftarrow </math> ''PostOrder''(<math>\displaystyle T,v_0</math>);
 
'''return''' <math>\displaystyle \mathcal{A}</math>;


  1  Wejście: <math>r</math> -- wyrażenie regularne.
  2  Wyjście: <math>\mathcal{A}=(S, A, f, s_0, T)</math> -- automat rozpoznający język opisywany wyrażeniem <math>r</math>.
  3  <math>T\leftarrow \text{BuildTree}(r)</math>;
  4  <math>v_0 \leftarrow \text{Root}(T)</math>;
  5  <math>\mathcal{A} \leftarrow</math> ''PostOrder''(<math>T,v_0</math>);
  6  '''return''' <math>\mathcal{A}</math>;
}}
}}


{{algorytm|||
{{przyklad|1.4||
 
'''procedure''' ''PostOrder'' (<math>\displaystyle T</math>: drzewo, <math>\displaystyle v</math>:
wierzchołek)
 
'''if''' ''IsLeaf''(<math>\displaystyle T,v</math>)
 
'''if''' <math>\displaystyle v</math><nowiki>=</nowiki>'''NULL'''
<math>\displaystyle \mathcal{A}_v \leftarrow</math>
''CreateAutomata''('<math>\displaystyle a</math>');
 
'''else'''
 
'''if''' ''Label''(<math>\displaystyle T,v</math>)<nowiki>=</nowiki>'1'
<math>\displaystyle \mathcal{A}_v \leftarrow</math>
''CreateAutomata''('<math>\displaystyle b</math>');
 
'''else'''
 
<math>\displaystyle \mathcal{A}_v \leftarrow</math>
''CreateAutomata''('<math>\displaystyle c</math>'); <math>\displaystyle \triangleright</math> ''Label''<math>\displaystyle (T,v)\in A</math>
 
'''endif''' '''endif'''
 
'''return''' <math>\displaystyle \mathcal{A}_v</math>;
 
'''else'''
 
<math>\displaystyle \mathcal{A}_L \leftarrow </math>
''PostOrder''(<math>\displaystyle T</math>,<math>\displaystyle \textsc{LeftChild}(T,v)</math>);
 
<math>\displaystyle \mathcal{A}_P \leftarrow </math>
''PostOrder''(<math>\displaystyle T</math>,<math>\displaystyle \textsc{RightChild}(T,v)</math>);
 
'''if''' ''Label''(<math>\displaystyle T,v</math>)<nowiki>=</nowiki>'<math>\displaystyle +</math>'
<math>\displaystyle \mathcal{A}_{LP}\leftarrow</math>''JoinAutomata''('<math>\displaystyle a</math>'<math>\displaystyle ,\mathcal{A}_L,\mathcal{A}_P</math>);
 
'''endif'''
 
'''if''' ''Label''(<math>\displaystyle T,v</math>)<nowiki>=</nowiki>'<math>\displaystyle \cdot</math>'
<math>\displaystyle \mathcal{A}_{LP}\leftarrow</math>''JoinAutomata''('<math>\displaystyle b</math>'<math>\displaystyle ,\mathcal{A}_L,\mathcal{A}_P</math>);
 
'''endif'''
 
'''if''' ''Label''(<math>\displaystyle T,v</math>)<nowiki>=</nowiki>'<math>\displaystyle *</math>'
<math>\displaystyle \mathcal{A}_{LP}\leftarrow</math>''JoinAutomata''('<math>\displaystyle c</math>'<math>\displaystyle ,\mathcal{A}_L,\mathcal{A}_P</math>);
 
'''endif'''
 
'''return''' <math>\displaystyle \mathcal{A}_{LP}</math>;
 
'''endif'''
 
'''end procedure'''
 
}}
 
{{przyklad|||
Zastosujemy algorytm ''Wr2Automat'' do konstrukcji automatu dla
Zastosujemy algorytm ''Wr2Automat'' do konstrukcji automatu dla
wyrażenia regularnego <math>\displaystyle w=(a^*b)(ab+c)</math>.
wyrażenia regularnego <math>w=(a^*b)(ab+c)</math>.
}}
}}
<center>
[[File:ja-lekcja08-w-anim1.svg|350x350px|thumb|center|Animacja 1]]
</center>


'''ANIMACJA - opis w pliku ja-lekcja8-w-anim1.pdf'''<br>
Automat <math>\mathcal{A}_{10}</math> jest zwrócony przez algorytm jako automat
 
akceptujący język opisywany wyrażeniem <math>r=(a^*b)(ab+c)</math>. Automat ten przedstawiony jest na rysunku 5.
Automat <math>\displaystyle \mathcal{A}_{10}</math> jest zwrócony przez algorytm jako automat
<center>
akceptujący język opisywany wyrażeniem <math>\displaystyle r=(a^*b)(ab+c)</math>. Automat ten
[[File:ja-lekcja08-w-rys6.svg|500x350px|thumb|center|Rysunek 5]]
przedstawiony jest na rysunku [[##ja-lekcja8-w-rys6|Uzupelnic ja-lekcja8-w-rys6|]]
</center>
 
'''RYSUNEK ja-lekcja8-w-rys6'''


Ramkami zaznaczono i opisano automaty budowane w trakcie działania
Ramkami zaznaczono i opisano automaty budowane w trakcie działania
Linia 413: Linia 299:
wcześniej algorytmów ''UsuńPustePrzejścia'' oraz  
wcześniej algorytmów ''UsuńPustePrzejścia'' oraz  
''Determinizuj''.
''Determinizuj''.
{{algorytm|Procedure PostOrder||
  1  '''procedure''' ''PostOrder'' (<math>T</math>: drzewo, <math>v</math>: wierzchołek)
  2  '''if''' ''IsLeaf''(<math>T,v</math>) '''then'''
  3    '''if''' <math>v</math><nowiki>=</nowiki>'''NULL''' '''then'''
  4      <math>\mathcal{A}_v \leftarrow</math> ''CreateAutomata''('<math>a</math>');
  5    '''else'''
  6      '''if''' ''Label''(<math>T,v</math>)<nowiki>=</nowiki>'1' '''then'''
  7        <math>\mathcal{A}_v \leftarrow</math> ''CreateAutomata''('<math>b</math>');
  8      '''else'''
  9        <math>\mathcal{A}_v \leftarrow</math> ''CreateAutomata''('<math>c</math>'); <math>\triangleright</math> ''Label''<math>(T,v)\in A</math>
  10      '''end if'''
  11    '''end if'''
  12    '''return''' <math>\mathcal{A}_v</math>;
  13  '''else'''
  14    <math>\mathcal{A}_L \leftarrow</math> ''PostOrder''(<math>T</math>,  LeftChild<math>(T,v)</math>);
  15    <math>\mathcal{A}_P \leftarrow</math> ''PostOrder''(<math>T</math>, RightChild<math>(T,v)</math>);
  16    '''if''' ''Label''(<math>T,v</math>)<nowiki>=</nowiki>'<math>+</math>' '''then'''
  17      <math>\mathcal{A}_{LP}\leftarrow</math>''JoinAutomata''('<math>a</math>'<math>,\mathcal{A}_L,\mathcal{A}_P</math>);
  18    '''end if'''
  19    '''if''' ''Label''(<math>T,v</math>)<nowiki>=</nowiki>'<math>\cdot</math>' '''then'''
  20      <math>\mathcal{A}_{LP}\leftarrow</math>''JoinAutomata''('<math>b</math>'<math>,\mathcal{A}_L,\mathcal{A}_P</math>);
  21    '''end if'''
  22    '''if''' ''Label''(<math>T,v</math>)<nowiki>=</nowiki>'<math>*</math>' '''then'''
  23      <math>\mathcal{A}_{LP}\leftarrow</math>''JoinAutomata''('<math>c</math>'<math>,\mathcal{A}_L,\mathcal{A}_P</math>);
  24    '''end if'''
  25    '''return''' <math>\mathcal{A}_{LP}</math>;
  26  '''end if'''
  27  '''end procedure'''
}}


Procedura tworzenia drzewa dla wyrażenia regularnego działa w czasie
Procedura tworzenia drzewa dla wyrażenia regularnego działa w czasie
Linia 427: Linia 344:
przedstawimy w ćwiczeniach do tego wykładu.
przedstawimy w ćwiczeniach do tego wykładu.


Niech dany  będzie automat <math>\displaystyle \mathcal{A}=(S, A, f, s_1, T)</math>. Zbudujemy
Niech dany  będzie automat <math>\mathcal{A}=(S, A, f, s_1, T)</math>. Zbudujemy
wyrażenie regularne opisujące język akceptowany przez <math>\displaystyle \mathcal{A}</math>.
wyrażenie regularne opisujące język akceptowany przez <math>\mathcal{A}</math>.


Konstrukcja polega na obliczeniu zbiorów <math>\displaystyle R_{ij}^k</math> (definicja
Konstrukcja polega na obliczeniu zbiorów <math>R_{ij}^k</math> (definicja
poniżej), gdzie <math>\displaystyle i,j=1,...,|S|</math>, co jest równoważne konstrukcji
poniżej), gdzie <math>i,j=1,\ldots,|S|</math>, co jest równoważne konstrukcji
pewnych wyrażeń regularnych <math>\displaystyle r_{ij}^k</math>. Szukany język będzie
pewnych wyrażeń regularnych <math>r_{ij}^k</math>. Szukany język będzie
odpowiadał sumie pewnych zbiorów <math>\displaystyle R_{ij}^k</math>, a zatem opisywany
odpowiadał sumie pewnych zbiorów <math>R_{ij}^k</math>, a zatem opisywany
będzie przez wyrażenie regularne postaci <math>\displaystyle r_{ij_1}^k + ... +
będzie przez wyrażenie regularne postaci <math>r_{ij_1}^k + ... +
r_{ij_t}^k</math> dla pewnych <math>\displaystyle j_l</math>, <math>\displaystyle i</math> oraz <math>\displaystyle k</math>.
r_{ij_t}^k</math> dla pewnych <math>j_l</math>, <math>i</math> oraz <math>k</math>.


Załóżmy, że zbiór stanów automatu jest postaci <math>\displaystyle S=\{s_1, s_2, ..., s_n\}</math>.
Załóżmy, że zbiór stanów automatu jest postaci <math>S=\{s_1, s_2, ..., s_n\}</math>.
Wprowadźmy porządek na
Wprowadźmy porządek na
zbiorze <math>\displaystyle S</math>, przyjmując: <center><math>\displaystyle s_i \prec s_j \Leftrightarrow i<j.</math></center>
zbiorze <math>S</math>, przyjmując: <center><math>s_i \prec s_j \Leftrightarrow i<j</math>.</center>


Zbiory <math>\displaystyle R_{ij}^k</math> definiujemy w następujący sposób:
Zbiory <math>R_{ij}^k</math> definiujemy w następujący sposób:


<center><math>\displaystyle \aligned R_{ij}^0 &= \left\{ \begin{array} {ll} \{a \in A:\ f(s_i,a)=s_j\}
<math>\mbox{(1)}</math>
<center><math>\begin{align} R_{ij}^0 &= \left\{ \begin{array} {ll} \{a \in A:\ f(s_i,a)=s_j\}
& \mbox{ dla } i \not = j,\ 1 \leqslant i,j \leqslant n \\ \{a \in A\ f(s_i,
& \mbox{ dla } i \not = j,\ 1 \leqslant i,j \leqslant n \\ \{a \in A\ f(s_i,
a)=s_j\} \cup \{ 1 \} & \mbox{ dla } i=j,\ 1 \leqslant i,j \leqslant n
a)=s_j\} \cup \{ 1 \} & \mbox{ dla } i=j,\ 1 \leqslant i,j \leqslant n
\end{array}  
\end{array}  
\right.  \\
\right.  \\
\nonumber R_{ij}^k &= R_{ik}^{k-1}(R_{kk}^{k-1})^*R_{kj}^{k-1}
R_{ij}^k &= R_{ik}^{k-1}(R_{kk}^{k-1})^*R_{kj}^{k-1}
\cup R_{ij}^{k-1},\ 1 \leqslant i,j,k \leqslant n.
\cup R_{ij}^{k-1},\ 1 \leqslant i,j,k \leqslant n.
\endaligned</math></center>
\end{align}</math></center>


Intuicyjnie, zbiór <math>\displaystyle R_{ij}^k</math> to ogół wszystkich słów <math>\displaystyle w</math> takich, że
Intuicyjnie, zbiór <math>R_{ij}^k</math> to ogół wszystkich słów <math>w</math> takich, że
<math>\displaystyle f(s_i, w)=s_j</math>, a ponadto jeśli <math>\displaystyle w=a_1a_2...a_m</math>, to <math>\displaystyle \forall 1
<math>f(s_i, w)=s_j</math>, a ponadto jeśli <math>w=a_1a_2...a_m</math>, to <math>\forall 1
\leqslant j \leqslant m-1 f(s_i, a_1a_2...a_j) = s_l \wedge l \leqslant k</math>.
\leqslant j \leqslant m-1 f(s_i, a_1a_2...a_j) = s_l \wedge l \leqslant k</math>.


Zamiast obliczać zbiory <math>\displaystyle R_{ij}^k</math> wygodniej będzie od razu
Zamiast obliczać zbiory <math>R_{ij}^k</math> wygodniej będzie od razu
zapisywać odpowiadające im wyrażenia regularne, które oznaczać
zapisywać odpowiadające im wyrażenia regularne, które oznaczać
będziemy poprzez <math>\displaystyle r_{ij}^k</math>.
będziemy poprzez <math>r_{ij}^k</math>.
Przez analogię mamy wzór rekurencyjny:
Przez analogię mamy wzór rekurencyjny:


<center><math>\displaystyle r_{ij}^k=r_{ik}^{k-1}(r_{kk}^{k-1})^*r_{kj}^{k-1} + r_{ij}^{k-1},\ 1
<math>\mbox{(2)}</math>
\leqslant i,j,k \leqslant n.
<center><math>r_{ij}^k=r_{ik}^{k-1}(r_{kk}^{k-1})^*r_{kj}^{k-1} + r_{ij}^{k-1},\ 1
</math></center>
\leqslant i,j,k \leqslant n</math></center>


Pozostaje wyjaśnić jak wyglądają wyrażenia  <math>\displaystyle r_{ij}^0</math>. Jeśli
Pozostaje wyjaśnić jak wyglądają wyrażenia  <math>r_{ij}^0</math>. Jeśli
<math>\displaystyle R_{ij}^k=\{a_1,a_2,\dots,a_s\}</math> to
<math>R_{ij}^k=\{a_1,a_2,\dots,a_s\}</math> to


<center><math>\displaystyle r_{ij}^0=a_1+a_2+\dots+a_s
{{kotwica|prz.3|}}<math>\mbox{(3)}</math>
<center><math>r_{ij}^0=a_1+a_2+\dots+a_s
</math></center>
</math></center>


{{twierdzenie|||
{{kotwica|prz.1|}}{{twierdzenie|1.1||


Niech <math>\displaystyle \mathcal{A}</math> oraz <math>\displaystyle R_{ij}^k</math> będą zdefiniowane jak powyżej i
Niech <math>\mathcal{A}</math> oraz <math>R_{ij}^k</math> będą zdefiniowane jak powyżej i
niech zbiór stanów końcowych dla <math>\displaystyle \mathcal{A}</math> ma postać
niech zbiór stanów końcowych dla <math>\mathcal{A}</math> ma postać
<math>\displaystyle T=\{s_{j_1}, s_{j_2}, ..., s_{j_t}\}</math>. Wtedy
<math>T=\{s_{j_1}, s_{j_2}, ..., s_{j_t}\}</math>. Wtedy
<center><math>\displaystyle L(\mathcal{A})=r_{1j_1}^n + r_{1j_2}^n + ... + r_{1j_t}^n.</math></center>
<center><math>L(\mathcal{A})=r_{1j_1}^n + r_{1j_2}^n + ... + r_{1j_t}^n</math>.</center>


}}
}}
Linia 483: Linia 402:
''Automat2WR1'').
''Automat2WR1'').


{{algorytm|||
{{algorytm|''Automat2WR1'' -- buduje wyrażenie regularne  
 
opisujące język akceptowany przez automat skończony.||
{''Automat2WR1'' -- buduje wyrażenie regularne  
opisujące język akceptowany przez automat skończony.}
 
[1]
Wejście: <math>\displaystyle \mathcal{A}=(S=\{s_1, s_2, ..., s_n\}, A, f, s_1,
T)</math>.
 
Wyjście: <math>\displaystyle w</math> -- wyrażenie regularne opisujące język
<math>\displaystyle L=L(\mathcal{A})</math>.
 
'''for''' <math>\displaystyle i\leftarrow 1 \textbf{ to } n</math>
 
'''for''' <math>\displaystyle j\leftarrow 1 \textbf{ to } n</math>
 
'''oblicz''' <math>\displaystyle r_{ij}^0\displaystyle \triangleright</math> stosujemy
wzór ([[##compute_rij0|Uzupelnic compute_rij0|]]);
 
'''endfor'''
 
'''endfor'''
 
'''for''' <math>\displaystyle k \leftarrow 1 \textbf{ to } n</math>
 
'''for''' <math>\displaystyle i\leftarrow 1 \textbf{ to } n</math>
 
'''for''' <math>\displaystyle j\leftarrow 1 \textbf{ to } n\displaystyle r_{ij}^k \leftarrow r_{ik}^{k-1}(r_{kk}^{k-1})^*r_{kj}^{k-1}
+ r_{ij}^{k-1}</math>; <math>\displaystyle \triangleright</math> dokonujemy
katenacji słów
 
'''endfor'''
 
'''endfor'''
 
'''endfor'''
 
<math>\displaystyle r \leftarrow </math> ""; <math>\displaystyle \triangleright</math> podstaw pod <math>\displaystyle r</math>
słowo puste
 
'''for''' <math>\displaystyle i\leftarrow 1 \textrm{ to } n</math>
 
'''if''' <math>\displaystyle \ s_i \in T</math>
 
'''if''' r<nowiki>=</nowiki>""
<math>\displaystyle r\leftarrow r_{1i}^{n}</math>; <math>\displaystyle \triangleright</math>
stosujemy Twierdzenie&nbsp;[[##thm:FormOfL|Uzupelnic thm:FormOfL|]]
'''else'''
 
<math>\displaystyle r \leftarrow r + r_{1i}^{n}</math>;
'''endif'''
 
'''endif'''
 
'''endfor'''
 
'''return''' <math>\displaystyle r</math>;


  1  Wejście: <math>\mathcal{A}=(S=\{s_1, s_2, ..., s_n\}, A, f, s_1, T)</math>.
  2  Wyjście: <math>w</math> - wyrażenie regularne opisujące język <math>L=L(\mathcal{A})</math>.
  3  '''for''' <math>i\leftarrow 1</math> '''to''' <math>n</math> '''do'''
  4    '''for''' <math>j\leftarrow 1</math> '''to''' <math>n</math> '''do'''
  5      '''oblicz''' <math>r_{ij}^0</math>                            <math>\triangleright</math> stosujemy wzór (3);
  6    '''end for'''
  7  '''end for'''
  8  '''for''' <math>k \leftarrow 1</math> '''to''' <math>n</math> '''do'''
  9    '''for''' <math>i\leftarrow 1</math> '''to''' <math>n</math> '''do'''
  10      '''for''' <math>j\leftarrow 1</math> '''to''' <math>n</math> '''do'''
  11        <math>r_{ij}^k \leftarrow r_{ik}^{k-1}(r_{kk}^{k-1})^*r_{kj}^{k-1} + r_{ij}^{k-1}</math>; <math>\triangleright</math> dokonujemy katenacji słów
  12      '''end for'''
  13    '''end for'''
  14  '''end for'''
  15  <math>r \leftarrow</math> "";                          <math>\triangleright</math> podstaw pod <math>r</math> słowo puste
  16  '''for''' <math>i\leftarrow 1</math> '''to'''  <math>n</math>
  17    '''if''' <math>\ s_i \in T</math>
  18      '''if''' r<nowiki>=</nowiki>""
  19        <math>r\leftarrow r_{1i}^{n}</math>;                    <math>\triangleright</math> stosujemy Twierdzenie 1.1
  20      '''else'''
  21        <math>r \leftarrow r + r_{1i}^{n}</math>;
  22      '''end if'''
  23    '''end if'''
  24  '''end for'''
  25  '''return''' <math>r</math>;
}}
}}


Podczas obliczania wyrażeń <math>\displaystyle r_{ij}^k</math> należy je w miarę możliwości
Podczas obliczania wyrażeń <math>r_{ij}^k</math> należy je w miarę możliwości
upraszczać, gdyż, szczególnie przy dużej liczbie stanów,  
upraszczać, gdyż, szczególnie przy dużej liczbie stanów,  
nieskracane, mogą rozrastać się do bardzo dużych rozmiarów.
nieskracane, mogą rozrastać się do bardzo dużych rozmiarów.


{{przyklad|||
{{przyklad|1.5||


Znajdziemy wyrażenie regularne opisujące język akceptowany przez
Znajdziemy wyrażenie regularne opisujące język akceptowany przez
automat z rysunku [[##ja-lekcja8-w-rys7|Uzupelnic ja-lekcja8-w-rys7|]].
automat z rysunku 6.
}}
}}
<center>
<center>
<div class="thumb"><div style="width:250px;">
[[File:ja-lekcja08-w-rys7.svg|250x250px|thumb|center|Rysunek 6]]
<flash>file=ja-lekcja08-w-rys7.swf|width=250|height=250</flash>
<div.thumbcaption>ja-lekcja08-w-rys7</div>
</div></div>
</center>
</center>


Mamy <math>\displaystyle |S|=3</math>, <math>\displaystyle i,j \in \{1, 2, 3\}</math>, <math>\displaystyle k \in \{0, 1, 2, 3\}</math>,
Mamy <math>|S|=3</math>, <math>i,j \in \{1, 2, 3\}</math>, <math>k \in \{0, 1, 2, 3\}</math>,
<math>\displaystyle T=\{s_3\}</math>. Szukamy zatem wyrażenia regularnego <math>\displaystyle r = r_{13}^3</math>.
<math>T=\{s_3\}</math>. Szukamy zatem wyrażenia regularnego <math>r = r_{13}^3</math>.


Najpierw musimy obliczyć <math>\displaystyle r_{ij}^0</math> dla wszystich <math>\displaystyle i,j \in
Najpierw musimy obliczyć <math>r_{ij}^0</math> dla wszystich <math>i,j \in
\{1,2,3\}</math>. Mamy na przykład <math>\displaystyle r_{31}^0=a+b</math>, gdyż z definicji
\{1,2,3\}</math>. Mamy na przykład <math>r_{31}^0=a+b</math>, gdyż z definicji
zachodzi: <math>\displaystyle R_{31}^0 = \{a \in A:\ f(s_3, a)=s_1\}=\{a, b\}</math>.
zachodzi: <math>R_{31}^0 = \{a \in A:\ f(s_3, a)=s_1\}=\{a, b\}</math>.


Gdy mamy wyliczone wszystkie <math>\displaystyle r_{ij}^0</math>, przystępujemy do obliczeń
Gdy mamy wyliczone wszystkie <math>r_{ij}^0</math>, przystępujemy do obliczeń
dla <math>\displaystyle k=1</math>.
dla <math>k=1</math>.


Na przykład:
Na przykład:
<center><math>\displaystyle r_{31}^1=r_{31}^0(r_{11}^0)^*r_{11}^0+r_{31}^0=(a+b)(a+1)^*(a+1)+(a+b),</math></center>
<center><math>r_{31}^1=r_{31}^0(r_{11}^0)^*r_{11}^0+r_{31}^0=(a+b)(a+1)^*(a+1)+(a+b)</math>,</center>


co po zredukowaniu daje
co po zredukowaniu daje
<center><math>\displaystyle r_{31}^1=(a+b)(a+1)^++(a+b)=(a+b)a^*+(a+b)=(a+b)a^*=a^++ba^*.</math></center>
<center><math>r_{31}^1=(a+b)(a+1)^++(a+b)=(a+b)a^*+(a+b)=(a+b)a^*=a^++ba^*</math>.</center>
 
Obliczone wyrażenia <math>\displaystyle r_{ij}^k</math> dla <math>\displaystyle k=0,1,2</math> oraz dla wszystkich
<math>\displaystyle i,j</math> przedstawione są w tabeli [[##tab_rijk|Uzupelnic tab_rijk|]].


[!hf]
Obliczone wyrażenia <math>r_{ij}^k</math> dla <math>k=0,1,2</math> oraz dla wszystkich
<math>i,j</math> przedstawione są w tabeli [[#tab.1|1]].


{| border=1
{| border=1 align=center
|+ <span style="font-variant:small-caps">Uzupelnij tytul</span>
|-  
|-  
| ||  <math>\displaystyle k=0</math>  ||  <math>\displaystyle k=1</math>  ||  <math>\displaystyle k=2</math>  
| ||  <math>k=0</math>  ||  <math>k=1</math>  ||  <math>k=2</math>  
|-
|-
| <math>\displaystyle r_{11}^k</math>  ||  <math>\displaystyle a+1</math>  ||  <math>\displaystyle a^*</math>  ||  <math>\displaystyle a^*b(a^+b)^*a^++a^*</math>  
| <math>r_{11}^k</math>  ||  <math>a+1</math>  ||  <math>a^*</math>  ||  <math>a^*b(a^+b)^*a^++a^*</math>  
|-
|-
| <math>\displaystyle r_{12}^k</math>  ||  <math>\displaystyle b</math>  ||  <math>\displaystyle a^*b</math>  ||  <math>\displaystyle a^*b(a^+b)^*+1</math>  
| <math>r_{12}^k</math>  ||  <math>b</math>  ||  <math>a^*b</math>  ||  <math>a^*b(a^+b)^*</math>  
|-
|-
| <math>\displaystyle r_{13}^k</math>  ||   ||   ||  <math>\displaystyle a^*b(a^+b)^*b</math>  
| <math>r_{13}^k</math>  || <math>\phi</math>  || <math>\phi</math>  ||  <math>a^*b(a^+b)^*b</math>  
|-
|-
| <math>\displaystyle r_{21}^k</math>  ||  <math>\displaystyle a</math>  ||  <math>\displaystyle a^+</math>  ||  <math>\displaystyle (a^+b)^*a^+</math>  
| <math>r_{21}^k</math>  ||  <math>a</math>  ||  <math>a^+</math>  ||  <math>(a^+b)^*a^+</math>  
|-
|-
| <math>\displaystyle r_{22}^k</math>  ||  <math>\displaystyle 1</math>  ||  <math>\displaystyle a^+b+1</math>  ||  <math>\displaystyle (a^+b)^*</math>  
| <math>r_{22}^k</math>  ||  <math>1</math>  ||  <math>a^+b+1</math>  ||  <math>(a^+b)^*</math>  
|-
|-
| <math>\displaystyle r_{23}^k</math>  ||  <math>\displaystyle b</math>  ||  <math>\displaystyle b</math>  ||  <math>\displaystyle (a^+b)^*b</math>  
| <math>r_{23}^k</math>  ||  <math>b</math>  ||  <math>b</math>  ||  <math>(a^+b)^*b</math>  
|-
|-
| <math>\displaystyle r_{31}^k</math>  ||  <math>\displaystyle a+b</math>  ||  <math>\displaystyle a^++ba^*</math>  ||  <math>\displaystyle (1+(a^+b+ba^*b)(a^+b+1))a^++ba^*</math>
| <math>r_{31}^k</math>  ||  <math>a+b</math>  ||  <math>a^++ba^*</math>  ||  <math>(1+(a^+b+ba^*b)(a^+b)^*)a^++ba^*</math>


|-
|-
| <math>\displaystyle r_{32}^k</math>  ||   ||  <math>\displaystyle a^+b+ba^*b</math>  ||  <math>\displaystyle (a^+b+ba^*b)(a^*b)^*</math>  
| <math>r_{32}^k</math>  || <math>\phi</math>  ||  <math>a^+b+ba^*b</math>  ||  <math>(a^+b+ba^*b)(a^+b)^*</math>  
|-
|-
| <math>\displaystyle r_{33}^k</math>  ||  <math>\displaystyle 1</math>  ||  <math>\displaystyle 1</math> ||  <math>\displaystyle (a^+b+ba^*b)(a^*b)^*b+1</math>
| <math>r_{33}^k</math>  ||  <math>1</math>  ||  <math>1</math> ||  <math>(a^+b+ba^*b)(a^+b)^*b+1</math>


|}
|}


{Obliczone wartości <math>\displaystyle r_{ij}^k</math> dla automatu z rys.
{{kotwica|tab.1|}}<center><tt>Tabela 1. Obliczone wartości <math>r_{ij}^k</math> dla automatu z rys. 6</tt></center>
[[##ja-lekcja8-w-rys7|Uzupelnic ja-lekcja8-w-rys7|]]}


Ponieważ <math>\displaystyle T=\{s_3\}</math>, szukanym wyrażeniem regularnym będzie
Ponieważ <math>T=\{s_3\}</math>, szukanym wyrażeniem regularnym będzie
<math>\displaystyle r=r_{13}^3</math>. Obliczamy zatem:
<math>r=r_{13}^3</math>. Obliczamy zatem:


<center><math>\displaystyle \aligned \nonumber r_{13}^3 &= r_{13}^2(r_{33}^2)^*r_{33}^2+r_{13}^2 \\
<center><math>\begin{align}  r_{13}^3 &= r_{13}^2(r_{33}^2)^*r_{33}^2+r_{13}^2 \\
\nonumber &=
&=
a^*b(a+b)^*b(a^+b+ba^*b)(a^*b)^*b+1)^*+a^*b(a^+b)^*b \\
a^*b(a^+b)^*b((a^+b+ba^*b)(a^+b)^*b+1)^++a^*b(a^+b)^*b \\
\nonumber &= a^*b(a^+b)^*b((a^+b+ba^*b)(a^*b)^*b)^*.
&= a^*b(a^+b)^*b((a^+b+ba^*b)(a^+b)^*b)^*.
\endaligned</math></center>
\end{align}</math></center>


==2. Problemy rozstrzygalne algorytmicznie==
==Problemy rozstrzygalne algorytmicznie==


Kończąc część wykładu prezentującą języki regularne, wskażemy  
Kończąc część wykładu prezentującą języki regularne, wskażemy  
Linia 632: Linia 514:
skończenie stanowego, czy też gramatyki regularnej.
skończenie stanowego, czy też gramatyki regularnej.


{{twierdzenie|||
{{twierdzenie|2.1||
W klasie języków regularnych  <math>\displaystyle \mathcal{REG}(A^{*}) </math>  następujące
W klasie języków regularnych  <math>\mathcal{REG}(A^{*})</math>  następujące
problemy są rozstrzygalne:
problemy są rozstrzygalne:
#  problem niepustości języka,  <math>\displaystyle L\neq \emptyset, </math>  
#  problem niepustości języka,  <math>L\neq \emptyset</math>  
#  problem nieskończoności języka,  <math>\displaystyle card\, L=\aleph _{0}</math>  
#  problem nieskończoności języka,  <math>card\, L=\aleph _{0}</math>  
#  problem równości języków,  <math>\displaystyle L_{1}=L_{2}</math>  
#  problem równości języków,  <math>L_{1}=L_{2}</math>  
#  problem należenia słowa do języka,  <math>\displaystyle w\in L</math>  
#  problem należenia słowa do języka,  <math>w\in L</math>  


}}
}}
Linia 644: Linia 526:
{{dowod|||
{{dowod|||


[[##niepusty|Uzupelnic niepusty|]].
1.  
Aby uzasadnić ten fakt zauważmy, że wystarczy sprawdzić niepustość
Aby uzasadnić ten fakt zauważmy, że wystarczy sprawdzić niepustość
skończonego podzbioru języka
skończonego podzbioru języka
<math>\displaystyle L</math>  co wynika z równoważności:
<math>L</math>  co wynika z równoważności:


<center><math>\displaystyle L\neq \oslash \: \Leftrightarrow \: \exists w\in L\, :\, \mid w\mid <N,</math></center>
<center><math>L\neq \oslash \ : \Leftrightarrow \ : \exists w\in L\, :\, \mid w\mid <N</math>,</center>


gdzie  <math>\displaystyle N </math>  stała z  lematu o pompowaniu.
gdzie  <math>N</math>  stała z  lematu o pompowaniu.
Implikacji  <math>\displaystyle \Leftarrow   </math>  jest oczywista. Natomiast fakt, że
Implikacji  <math>\Leftarrow</math>  jest oczywista. Natomiast fakt, że
do niepustego języka należy słowo o długości ograniczonej
do niepustego języka należy słowo o długości ograniczonej
przez  <math>\displaystyle N </math> , wynika z lematu o pompowaniu. Jeśli mianowicie
przez  <math>N</math> , wynika z lematu o pompowaniu. Jeśli mianowicie
<math>\displaystyle w\in L </math>  i  <math>\displaystyle \mid w\mid \geqslant N </math> , to
<math>w\in L</math>  i  <math>\mid w\mid \geqslant N</math> , to
rozkładamy słowo  <math>\displaystyle w </math>    następująco:
rozkładamy słowo  <math>w</math>    następująco:


<center><math>\displaystyle w=v_{1}uv_{2},\; \; u\neq 1,\; \; \forall i=0,1,2\ldots \: v_{1}u^{i}v_{2}\in L. </math></center>
<center><math>w=v_{1}uv_{2},\; \; u\neq 1,\; \; \forall i=0,1,2\ldots \ : v_{1}u^{i}v_{2}\in L</math></center>


Przyjmując teraz wartość  <math>\displaystyle i=0 </math> , uzyskujemy:
Przyjmując teraz wartość  <math>i=0</math> , uzyskujemy:


<center><math>\displaystyle v_{1}v_{2}\in L,\; \mid v_{1}v_{2}\mid <\mid w\mid </math></center>
<center><math>v_{1}v_{2}\in L,\; \mid v_{1}v_{2}\mid <\mid w\mid</math></center>


Po skończonej ilości powtórzeń  powyższego rozkładu uzyskamy
Po skończonej ilości powtórzeń  powyższego rozkładu uzyskamy
słowo należące do języka, o długości ograniczonej
słowo należące do języka, o długości ograniczonej
przez  <math>\displaystyle N </math> .
przez  <math>N</math> .


[[##nieskonczony|Uzupelnic nieskonczony|]].
2.  
Wystarczy udowodnić nastepującą równoważność:
Wystarczy udowodnić nastepującą równoważność:
<center><math>\displaystyle L \mbox{ nieskończony } \;\; \Longleftrightarrow \;\;\exists w \in L \;\; : \;\; N \leqslant \mid w \mid < 2N.</math></center>
<center><math>L \mbox{ nieskończony } \;\; \Longleftrightarrow \;\;\exists w \in L \;\; : \;\; N \leqslant \mid w \mid < 2N</math>.</center>


Jeśli  <math>\displaystyle L </math>  jest językiem nieskończonym, to znajdziemy w <math>\displaystyle L</math>   
Jeśli  <math>L</math>  jest językiem nieskończonym, to znajdziemy w <math>L</math>   
słowo  <math>\displaystyle w </math>  dowolnie długie. Niech  <math>\displaystyle \mid w\mid \geqslant N  
słowo  <math>w</math>  dowolnie długie. Niech  <math>\mid w\mid \geqslant N  
</math> ''.'' Jeśli słowo  <math>\displaystyle w </math>  nie spełnia ograniczenia  <math>\displaystyle \mid  
</math> ''.'' Jeśli słowo  <math>w</math>  nie spełnia ograniczenia  <math>\mid  
w\mid <2N </math> , to podobnie jak poprzednio korzystamy z lematu o  
w\mid <2N</math> , to podobnie jak poprzednio korzystamy z lematu o  
pompowaniu i po skończonej ilości kroków otrzymamy słowo krótsze  
pompowaniu i po skończonej ilości kroków otrzymamy słowo krótsze  
od  <math>\displaystyle 2N </math> . Istotne jest, że wykorzystując lemat o pompowaniu,  
od  <math>2N</math> . Istotne jest, że wykorzystując lemat o pompowaniu,  
możemy założyć, że usuwane słowo  <math>\displaystyle u </math>  ma długość ograniczoną  
możemy założyć, że usuwane słowo  <math>u</math>  ma długość ograniczoną  
przez  <math>\displaystyle N </math> . Zatem oznacza to, że ze słowa dłuższego od  <math>\displaystyle 2N </math>   
przez  <math>N</math> . Zatem oznacza to, że ze słowa dłuższego od  <math>2N</math>   
nie
nie
dostaniemy słowa krótszego od  <math>\displaystyle N </math> . <br>
dostaniemy słowa krótszego od  <math>N</math> . <br>


Jeśli teraz do języka  <math>\displaystyle L </math>  należy słowo  <math>\displaystyle w </math>  o długości
Jeśli teraz do języka  <math>L</math>  należy słowo  <math>w</math>  o długości
większej lub równej  <math>\displaystyle N </math> , to znów z lematu o&nbsp;pompowaniu wnioskujemy, że
większej lub równej  <math>N</math> , to znów z lematu o&nbsp;pompowaniu wnioskujemy, że


<center><math>\displaystyle w=v_{1}uv_{2},\: u\neq 1\;\;\; i\;\;\; v_{1}u^{*}v_{2}\subset L</math></center>
<center><math>w=v_{1}uv_{2},\ : u\neq 1\;\;\; i\;\;\; v_{1}u^{*}v_{2}\subset L</math></center>


Istnieje więc nieskończony podzbiór języka  <math>\displaystyle L </math> , a więc i sam język
Istnieje więc nieskończony podzbiór języka  <math>L</math> , a więc i sam język
<math>\displaystyle L </math>  jest  nieskończony.
<math>L</math>  jest  nieskończony.


[[##rownosc|Uzupelnic rownosc|]].
3.  
Rozważmy  <math>\displaystyle L=(L_{1}\cap \overline{L_{2}})\cup (\overline{L_{1}}\cap L_{2}) </math> .
Rozważmy  <math>L=(L_{1}\cap \overline{L_{2}})\cup (\overline{L_{1}}\cap L_{2})</math> .
Język  <math>\displaystyle L </math>  jest regularny, co wynika z domkniętości
Język  <math>L</math>  jest regularny, co wynika z domkniętości
klasy  <math>\displaystyle \mathcal{L}_{3} </math>  na operaje boolowskie. Równoważność
klasy  <math>\mathcal{L}_{3}</math>  na operaje boolowskie. Równoważność


<center><math>\displaystyle L\neq \emptyset \Longleftrightarrow L_{1}\neq L_{2}. </math></center>
<center><math>L\neq \emptyset \Longleftrightarrow L_{1}\neq L_{2}</math></center>


sprowadza problem równości języków do problemu
sprowadza problem równości języków do problemu
niepustości omówionego powyżej.
niepustości omówionego powyżej.


[[##nalezenie|Uzupelnic nalezenie|]].
4.  
Konstruujemy automat  <math>\displaystyle \mathcal{A}  \displaystyle =(S,f,s_0,T)</math> rozpoznający język
Konstruujemy automat  <math>\mathcal{A}  =(S,f,s_0,T)</math> rozpoznający język
<math>\displaystyle L </math>  i sprawdzamy, czy  <math>\displaystyle f(s_{0},w)\in T.  \displaystyle \diamondsuit</math>   }}
<math>L</math>  i sprawdzamy, czy  <math>f(s_{0},w)\in T</math>.
 
}}


Na podstawie dowodu powyższego twierdzenia nietrudno jest określić algorytmy
Na podstawie dowodu powyższego twierdzenia nietrudno jest określić algorytmy
Linia 712: Linia 596:
deterministyczny.
deterministyczny.


{{algorytm|||
{{algorytm|''NależenieDoJęzyka'' -- sprawdza, czy dane słowo
 
należy do języka <math>L</math> akceptowanego przez zadany automat
{''NależenieDoJęzyka'' -- sprawdza, czy dane słowo
<math>\mathcal{A}</math>||
należy do języka <math>\displaystyle L</math> akceptowanego przez zadany automat
<math>\displaystyle \mathcal{A}</math>}.
 
[1]
Wejście: <math>\displaystyle \mathcal{A}=(S, A, f, s_0, T)</math> -- automat
akceptujący język <math>\displaystyle L</math> oraz <math>\displaystyle w=w_1w_2\ldots w_n \in A^*</math> -- słowo.
 
Wyjście: Odpowiedź '''true''' (tak) lub '''false'''
(nie).
 
<math>\displaystyle k \leftarrow |w|</math>;
 
<math>\displaystyle s \leftarrow s_0</math>;
 
'''for''' <math>\displaystyle i\leftarrow 1 \textbf{ to } k\displaystyle s \leftarrow f(s,w_i)</math>;
 
'''endfor'''
 
'''if'''  <math>\displaystyle s \in T</math>
 
'''return true''';
 
'''else'''
 
'''return false''';
 
'''endif'''


  1  Wejście: <math>\mathcal{A}=(S, A, f, s_0, T)</math> - automat akceptujący język <math>L</math> oraz <math>w=w_1w_2\ldots w_n \in A^*</math> - słowo.
  2  Wyjście: Odpowiedź '''true''' (tak) lub '''false''' (nie).
  3  <math>k \leftarrow |w|</math>;
  4  <math>s \leftarrow s_0</math>;
  5  '''for''' <math>i\leftarrow 1</math> '''to''' <math>k</math> '''do'''
  6    <math>s \leftarrow f(s,w_i)</math>;
  7  '''end for'''
  8  '''if'''  <math>s \in T</math> '''then'''
  9    '''return true''';
  10  '''else'''
  11    '''return false''';
  12  '''end if'''
}}
}}


Algorytm działa w czasie <math>\displaystyle O(|w|)</math> i posiada złożoność pamięciową
Algorytm działa w czasie <math>O(|w|)</math> i posiada złożoność pamięciową
<math>\displaystyle O(|A| \cdot |S|)</math>, co spowodowane jest koniecznością przechowywania funkcji
<math>O(|A| \cdot |S|)</math>, co spowodowane jest koniecznością przechowywania funkcji
przejść automatu  <math>\displaystyle \mathcal{A}</math>.
przejść automatu  <math>\mathcal{A}</math>.


Jeśli język zadany jest nie automatem, a gramatyką regularną, to
Jeśli język zadany jest nie automatem, a gramatyką regularną, to

Aktualna wersja na dzień 21:58, 15 wrz 2023

W tym wykładzie przedstawimy algorytmy konstrukcji gramatyki regularnej, automatu skończenie stanowego oraz wyrażeń regularnych opisujących ten sam język L. Omówimy także problemy rozstrzygalne algorytmicznie w klasie języków regularnych.

Dalsze algorytmy języków regularnych

Dowód twierdzenia z ostatniego wykładu, w którym udowodniliśmy równoważność rozpoznawania języka L i generowania tego języka przez gramatykę regularną daje podstawę do określenia dwóch algorytmów. Algorytmu konstruującego automat skończony w oparciu o daną gramatykę regularną i oczywiście akceptujący język generowany przez tę gramatykę oraz algorytmu budowy gramatyki regularnej dla zadanego automatu. Bez utraty ogólności przyjmujemy, że automat jest deterministyczny.

Idea działania algorytmu Automat2GReg jest następująca: każdy symbol nieterminalny v tworzonej gramatyki odpowiada pewnemu stanowi sv automatu. Jeśli w automacie pod wpływem litery a następuje przejście ze stanu sv do stanu sw, to do zbioru praw gramatyki dodawane jest prawo svasw. Ponadto, jeśli stan sw jest stanem końcowym, to dodajemy także prawo sw1, aby w danym miejscu wywód słowa mógł zostać zakończony.

Algorytm Automat2GReg - buduje gramatykę regularną dla zadanego automatu skończonego.



  1  Wejście: 𝒜=(S,A,f,s0,T) - automat niedeterministyczny.
  2  Wyjście: G=(VN,VT,P,v0) - gramatyka regularna taka, że L(G)=L(𝒜).
  3  VN{v0,v1,,v|S|1};
  4  VTA;
  5  v0s0;
  6  P;
  7  for each  siS do
  8    for each  aA do
  9      if f(si,a)NULL then
 10        sjf(si,a);                  funkcja f jest określona na (si,a)
 11        PP{siasj};
 12        if sjT then
 13          PP{sj1};
 14        end if
 15      end if
 16    end for
 17  end for
 18  return G;

Oznaczmy przez E(𝒜) ilość krawędzi w grafie n-stanowego automatu niedeterministycznego 𝒜. Złożoność czasowa liczona względem E(𝒜) jest liniowa i równa O(E(𝒜)). Również złożoność pamięciowa jest liniowa i wynosi O(|S|+E(𝒜)).

Przykład 1.1

Niech dany będzie automat 𝒜 pokazany na rysunku 1. Zbudujemy gramatykę, która będzie

generowała język akceptowany przez 𝒜.
Rysunek 1

Ponieważ f(s0,a)=s0, a ponadto s0 jest stanem końcowym, do P dodajemy produkcje v0av0 oraz v01. Dodajemy także produkcję v0bv1, gdyż mamy f(s0,b)=s1.

Fakt, że f(s1,b)=s1 oraz f(s1,a)=s2 sprawia, że do P dodajemy: v1 bv1, v1av2.

Ponieważ f(s2,a)=s1 do P dodajemy: v2av1 oraz v21, gdyż s2T.

Symbolem początkowym nowej gramatyki jest symbol v0. Ostatecznie gramatyka ma postać:

v0av0 | bv1 | 1v1bv1 | av2v2av1 | 1

Zwróćmy uwagę na wygodny zapis produkcji gramatyki, jaki został użyty powyżej. Produkcje o tej samej lewej stronie (wspólnym symbolu nieterminalnym) zapisywane są razem, a prawe strony tych produkcji oddzielane są pionowymi kreskami.

Przedstawiony poniżej algorytm GReg2Automat konstruuje automat skończenie stanowy, akceptujący język generowany przez zadaną gramatykę.

Zauważmy, że gramatyka podana na wejściu algorytmu nie może zawierać produkcji postaci vx, gdzie vVN, x(VNVT). Jeśli gramatyka zawiera takie produkcje, to możemy się ich łatwo pozbyć, zgodnie z Twierdzeniem 2.2 z Wykładu 7 (patrz twierdzenie 2.2 wykład 7), uzyskując oczywiście gramatykę równoważną.

Idea działania algorytmu jest podobna do poprzedniej - każdy tworzony stan automatu odpowiadać będzie pewnemu symbolowi nieterminalnemu gramatyki wejściowej. Zależnie od postaci produkcji gramatyki, niektóre stany będą stanami końcowymi.

Zapis f w linii 7. symbolicznie oznacza, że funkcja przejść nie jest jeszcze określona.

W pętli 8.-15. pozbywamy się produkcji, w których występuje więcej niż jeden terminal; każdą taką produkcję "rozbijamy" na sekwencję produkcji postaci vaw, gdzie v,wVN, aVT.

Algorytm GReg2Automat - buduje automat dla zadanej gramatyki regularnej.



  1  Wejście: G=(VN,VT,P,v0) - gramatyka regularna.
  2  Wyjście: 𝒜=(S,A,f,v0,T) - automat taki, że L(𝒜)=L(G).
  3  SVN;
  4  AVT;
  5  T;                                    nie ma jeszcze stanów końcowych
  6  s0v0;
  7  f;                  funkcja f nie jest określona dla żadnego argumentu
  8  for each  (via1a2...anvj)P do
  9    if n>1 then
 10      VNVN{va1,...,van1};      rozbijamy produkcję na kilka prostszych
 11      PP{via1a2...anvj};            w tym celu usuwamy produkcję z P
 12      PP{via1va1,van1anvj};     w zamian dodając ciąg krótszych
 13      PP{va1a2va2,,van2an1van1};
 14    end if
 15  end for
 16     wszystkie produkcje są postaci uav lub u1, gdzie u,vVN, aVT
 17  for each (siasj)P do
 18    f(si,a)sj;
 19  end for
 20  for each  (vi1)P do
 21    TT{vi};
 22  end for
 23  return 𝒜=(S,A,f,s0,T);

Przykład 1.2

Jako wejście algorytmu rozważmy gramatykę z przykładu 1.1 (patrz przykład 1.1.) Używając algorytmu Greg2Automat, zbudujemy dla niej automat akceptujący język przez nią generowany.

Mamy S={s0,s1,s2}. W liniach 17. -- 19. określana jest funkcja przejścia: f(s0,a)=s0, f(s0,b)=s1, f(s1,b)=s1, f(s1,a)=s2 oraz f(s2,a)=s1.

Pętla w liniach 20. - 22. przebiega po dwóch produkcjach: v01 oraz v21, dodaje zatem do zbioru T stanów końcowych stany s0 oraz s2. Szukany automat to automat z poprzedniego przykładu; przedstawiony jest na rysunku 1.

Automat powstały w wyniku działania algorytmu GReg2Automat nie musi być automatem deterministycznym (wystarczy, że w zbiorze produkcji znajdą się dwie produkcje postaci viavj oraz viavk dla pewnego aA), jednak po jego determinizacji i minimalizacji otrzymujemy minimalny automat deterministyczny akceptujący język, który jest generowany przez gramatyke podaną na wejście algorytmu.

Złożoność czasowa jak i pamięciowa algorytmu wynosi O(p), gdzie p jest liczbą produkcji występujących w zbiorze praw P gramatyki.

Przedstawimy teraz algorytmy związane z wyrażeniami regularnymi. Pierwszy z nich prowadzi do konstrukcji automatu skończenie stanowego, rozpoznającego język opisany wyrażeniem regularnym. Drugi, mając na wejściu automat, konstruuje wyrażenie regularne opisujące język rozpoznawany przez ten automat.

Rozpoczynamy od algorytmu prowadzącego do konstrukcji automatu na podstawie wyrażenia regularnego.

Niech aA,r,s𝒲. Najpierw pokażemy, że językom odpowiadającym wyrażeniom regularnym , 1, a, r+s, rs oraz r* można przyporządkować automaty akceptujące te języki, a następnie podamy algorytm konstruowania automatu rozpoznającego dowolne wyrażenie regularne.

Na rysunku 2 przedstawione są trzy automaty. Automat a) rozpoznaje język pusty, automat b) - język {1}, a automat c) - język {a}, dla aA.

Rysunek 2

Niech dane będą automaty: M1, akceptujący język opisywany wyrażeniem r oraz M2, akceptujący język opisywany wyrażeniem s. Na rysunku 3 przedstawiono konstrukcje automatów akceptujących wyrażenia regularne r+s (automat a)), (rs) (automat b)) oraz r* (automat c)).

Rysunek 3

W automacie a) stan q0 jest stanem początkowym, stan f0 - stanem końcowym, stany q1,q2 oraz f1,f2 oznaczają odpowiednio stany początkowe automatów M1 i M2 oraz stany końcowe automatów M1 i M2.

W automacie b) stan q0 jest jednocześnie jego stanem początkowym oraz stanem początkowym automatu M1, stan f1 jest stanem końcowym automatu b) i jednocześnie stanem końcowym automatu M2. Stan f0 jest stanem końcowym w M1, a q1 - początkowym w M2.

W automacie c) stan q0 jest jego stanem początkowym a f0 końcowym. Stany q1 oraz f1 to odpowiednio początkowy i końcowy stan automatu M1.

Wyrażenia regularne można przedstawiać w postaci drzewa, w którym liśćmi są litery, słowo puste 1 lub zbiór pusty , a węzły symbolizują operacje na wyrażeniach regularnych, czyli sumę, konkatenację lub iterację, czyli gwiazdkę Kleene'ego.

Przykład 1.3

Rozważmy wyrażenie regularne r=(a*b)(ab+c). Drzewo odpowiadające r przedstawione jest na rysunku 4. Korzeniem jest wierzchołek z małą wchodzącą strzałką.

Rysunek 4

Powyższe konstrukcje będą stosowane podczas iteracyjnej budowy automatu. Algorytm do tego celu będzie wykorzystywał drzewo odpowiadające wyrażeniu regularnemu w następujący sposób: drzewo będzie przeszukiwane metodą post-order (zaczynając od korzenia), tzn. najpierw rekurencyjnie przeszukiwane są poddrzewa danego węzła x, a na końcu sam węzeł x. Dzięki temu, wchodząc do węzła x drzewa etykietowanego daną operacją na wyrażeniu regularnym oba poddrzewa P i L wierzchołka x będą już reprezentowane przez automaty 𝒜P oraz 𝒜L. Teraz wystarczy zastosować jedną z konstrukcji z rysunku 2 lub 3. Procedurę powtarzamy do momentu, aż przechodzenie drzewa zakończy się w korzeniu. Szukanym przez nas automatem będzie automat "odpowiadający" korzeniowi drzewa.

Poniżej przedstawiony jest algorytm konstrukcji automatu w oparciu o wyrażenie regularne. Jego istotną część składową stanowi procedura PostOrder, której pseudo-kod jest przedstawiony poniżej. Wykorzystamy także dwie procedury, mianowicie CreateAutomata(type) oraz JoinAutomata(type,1,2). Zmienna type może przyjmować wartości 'a', 'b' lub 'c'. Funkcja zwraca automat CreateAutomata(type) przedstawiony (zależnie od zmiennej type) na rysunku 2. Procedura JoinAutomata(type,1,2) natomiast konstruuje na podstawie automatów 1, 2 automat z rysunku 2, przy czym dla przypadku type=c automat 2 jest bez znaczenia. Ostatnią wykorzystaną procedurą będzie BuildTree(r), tworząca drzewo (binarne) dla wyrażenia regularnego. Zakładamy, że studentowi doskonale jest znana Odwrotna Notacja Polska i budowa takiego drzewa nie będzie dla niego stanowiła problemu. Dla ustalenia uwagi zakładamy, że symbol * prowadzi zawsze do lewego dziecka.

Poniżej przedstawiamy oznaczenia standardowych funkcji operujących na drzewach. Funkcja Root(T) zwraca korzeń drzewa T, funkcje LeftChild(T,v) oraz RightChild(T,v) zwracają lewe i prawe dziecko wierzchołka v (ew. NULL, gdy brak lewego lub prawego dziecka), natomiast funkcja Label(T,v) zwraca etykietę wierzchołka v drzewa T. Funkcja IsLeaf(T,v) zwraca wartość true, gdy v jest liściem w drzewie T oraz false w przypadku przeciwnym.

Algorytm Wr2Automat -- buduje automat rozpoznający język opisywany wyrażeniem regularnym



  1  Wejście: r -- wyrażenie regularne.
  2  Wyjście: 𝒜=(S,A,f,s0,T) -- automat rozpoznający język opisywany wyrażeniem r.
  3  TBuildTree(r);
  4  v0Root(T);
  5  𝒜 PostOrder(T,v0);
  6  return 𝒜;

Przykład 1.4

Zastosujemy algorytm Wr2Automat do konstrukcji automatu dla wyrażenia regularnego w=(a*b)(ab+c).

Plik:Ja-lekcja08-w-anim1.svg
Animacja 1

Automat 𝒜10 jest zwrócony przez algorytm jako automat akceptujący język opisywany wyrażeniem r=(a*b)(ab+c). Automat ten przedstawiony jest na rysunku 5.

Rysunek 5

Ramkami zaznaczono i opisano automaty budowane w trakcie działania procedury PostOrder.

Rezultat działania algorytmu Wr2Automat może nie być zadawalający, gdyż wynikiem działania algorytmu nie jest automat deterministyczny, lecz automat z pustymi przejściami. Automat ten można więc poddać procesowi usunięcia przejść pustych oraz determinizacji, co można przeprowadzić przy pomocy omówionych wcześniej algorytmów UsuńPustePrzejścia oraz Determinizuj.

Algorytm Procedure PostOrder



  1  procedure PostOrder (T: drzewo, v: wierzchołek)
  2  if IsLeaf(T,v) then
  3    if v=NULL then
  4      𝒜v CreateAutomata('a');
  5    else
  6      if Label(T,v)='1' then
  7        𝒜v CreateAutomata('b');
  8      else
  9        𝒜v CreateAutomata('c');  Label(T,v)A
 10      end if
 11    end if
 12    return 𝒜v;
 13  else
 14    𝒜L PostOrder(T,  LeftChild(T,v));
 15    𝒜P PostOrder(T, RightChild(T,v));
 16    if Label(T,v)='+' then 
 17      𝒜LPJoinAutomata('a',𝒜L,𝒜P);
 18    end if
 19    if Label(T,v)='' then
 20      𝒜LPJoinAutomata('b',𝒜L,𝒜P);
 21    end if
 22    if Label(T,v)='*' then
 23      𝒜LPJoinAutomata('c',𝒜L,𝒜P);
 24    end if
 25    return 𝒜LP;
 26  end if
 27  end procedure

Procedura tworzenia drzewa dla wyrażenia regularnego działa w czasie liniowym ze względu na długość napisu reprezentującego wyrażenie regularne -- napis ten można najpierw przekształcić do równoważnego mu, zapisanego w Odwrotnej Notacji Polskiej, a następnie, przechodząc od lewej strony do prawej, konstruować po kolei fragmenty drzewa.

Przechodzimy teraz do algorytmów konstruujących wyrażenie regularne na podstawie zadanego automatu. Pierwszą metodę, można powiedzieć klasyczną i omawianą w większości podręczników, prezentujemy poniżej. Drugą, nieco prostszą i wygodniejszą w zastosowaniu, przedstawimy w ćwiczeniach do tego wykładu.

Niech dany będzie automat 𝒜=(S,A,f,s1,T). Zbudujemy wyrażenie regularne opisujące język akceptowany przez 𝒜.

Konstrukcja polega na obliczeniu zbiorów Rijk (definicja poniżej), gdzie i,j=1,,|S|, co jest równoważne konstrukcji pewnych wyrażeń regularnych rijk. Szukany język będzie odpowiadał sumie pewnych zbiorów Rijk, a zatem opisywany będzie przez wyrażenie regularne postaci rij1k+...+rijtk dla pewnych jl, i oraz k.

Załóżmy, że zbiór stanów automatu jest postaci S={s1,s2,...,sn}. Wprowadźmy porządek na

zbiorze

S

, przyjmując:

sisji<j.

Zbiory Rijk definiujemy w następujący sposób:

(1)

Rij0={{aA: f(si,a)=sj} dla i=j, 1i,jn{aA f(si,a)=sj}{1} dla i=j, 1i,jnRijk=Rikk1(Rkkk1)*Rkjk1Rijk1, 1i,j,kn.

Intuicyjnie, zbiór Rijk to ogół wszystkich słów w takich, że f(si,w)=sj, a ponadto jeśli w=a1a2...am, to 1jm1f(si,a1a2...aj)=sllk.

Zamiast obliczać zbiory Rijk wygodniej będzie od razu zapisywać odpowiadające im wyrażenia regularne, które oznaczać będziemy poprzez rijk. Przez analogię mamy wzór rekurencyjny:

(2)

rijk=rikk1(rkkk1)*rkjk1+rijk1, 1i,j,kn

Pozostaje wyjaśnić jak wyglądają wyrażenia rij0. Jeśli Rijk={a1,a2,,as} to

(3)

rij0=a1+a2++as

Twierdzenie 1.1

Niech 𝒜 oraz Rijk będą zdefiniowane jak powyżej i niech zbiór stanów końcowych dla 𝒜 ma postać T={sj1,sj2,...,sjt}. Wtedy

L(𝒜)=r1j1n+r1j2n+...+r1jtn.

Powyższą metodę ujmiemy formalnie w ramy algorytmu (algorytm Automat2WR1).

Algorytm Automat2WR1 -- buduje wyrażenie regularne opisujące język akceptowany przez automat skończony.



  1  Wejście: 𝒜=(S={s1,s2,...,sn},A,f,s1,T).
  2  Wyjście: w - wyrażenie regularne opisujące język L=L(𝒜).
  3  for i1 to n do
  4    for j1 to n do
  5      oblicz rij0                             stosujemy wzór (3);
  6    end for
  7  end for
  8  for k1 to n do
  9    for i1 to n do
 10      for j1 to n do
 11        rijkrikk1(rkkk1)*rkjk1+rijk1;  dokonujemy katenacji słów
 12      end for
 13    end for
 14  end for
 15  r "";                            podstaw pod r słowo puste
 16  for i1 to  n 
 17    if  siT 
 18      if r="" 
 19        rr1in;                      stosujemy Twierdzenie 1.1
 20      else
 21        rr+r1in;
 22      end if
 23    end if
 24  end for
 25  return r;

Podczas obliczania wyrażeń rijk należy je w miarę możliwości upraszczać, gdyż, szczególnie przy dużej liczbie stanów, nieskracane, mogą rozrastać się do bardzo dużych rozmiarów.

Przykład 1.5

Znajdziemy wyrażenie regularne opisujące język akceptowany przez automat z rysunku 6.

Rysunek 6

Mamy |S|=3, i,j{1,2,3}, k{0,1,2,3}, T={s3}. Szukamy zatem wyrażenia regularnego r=r133.

Najpierw musimy obliczyć rij0 dla wszystich i,j{1,2,3}. Mamy na przykład r310=a+b, gdyż z definicji zachodzi: R310={aA: f(s3,a)=s1}={a,b}.

Gdy mamy wyliczone wszystkie rij0, przystępujemy do obliczeń dla k=1.

Na przykład:

r311=r310(r110)*r110+r310=(a+b)(a+1)*(a+1)+(a+b),

co po zredukowaniu daje

r311=(a+b)(a+1)++(a+b)=(a+b)a*+(a+b)=(a+b)a*=a++ba*.

Obliczone wyrażenia rijk dla k=0,1,2 oraz dla wszystkich i,j przedstawione są w tabeli 1.

k=0 k=1 k=2
r11k a+1 a* a*b(a+b)*a++a*
r12k b a*b a*b(a+b)*
r13k ϕ ϕ a*b(a+b)*b
r21k a a+ (a+b)*a+
r22k 1 a+b+1 (a+b)*
r23k b b (a+b)*b
r31k a+b a++ba* (1+(a+b+ba*b)(a+b)*)a++ba*
r32k ϕ a+b+ba*b (a+b+ba*b)(a+b)*
r33k 1 1 (a+b+ba*b)(a+b)*b+1

Tabela 1. Obliczone wartości rijk dla automatu z rys. 6

Ponieważ T={s3}, szukanym wyrażeniem regularnym będzie r=r133. Obliczamy zatem:

r133=r132(r332)*r332+r132=a*b(a+b)*b((a+b+ba*b)(a+b)*b+1)++a*b(a+b)*b=a*b(a+b)*b((a+b+ba*b)(a+b)*b)*.

Problemy rozstrzygalne algorytmicznie

Kończąc część wykładu prezentującą języki regularne, wskażemy problemy rozstrzygalne algorytmicznie w zakresie tej rodziny języków formalnych. Ponieważ pojęcia rozstrzygalności i nierozstrzygalności możemy uznać za znane (były wprowadzone na innych wykładach) nie będziemy tutaj ich definiować ani kreślić tła teorii rozstrzygalności.

W obrębie rodziny języków regularnych wszystkie podstawowe problemy są algorytmicznie rozstrzygalne. Uzasadnienia są proste. Część z nich opiera się na lemacie o pompowaniu, a część wynika bezpośrednio z algorytmicznej struktury automatu skończenie stanowego, czy też gramatyki regularnej.

Twierdzenie 2.1

W klasie języków regularnych 𝒢(A*) następujące problemy są rozstrzygalne:

  1. problem niepustości języka, L
  2. problem nieskończoności języka, cardL=0
  3. problem równości języków, L1=L2
  4. problem należenia słowa do języka, wL

Dowód

1. Aby uzasadnić ten fakt zauważmy, że wystarczy sprawdzić niepustość skończonego podzbioru języka L co wynika z równoważności:

L : :wL:w<N,

gdzie N stała z lematu o pompowaniu. Implikacji jest oczywista. Natomiast fakt, że do niepustego języka należy słowo o długości ograniczonej przez N , wynika z lematu o pompowaniu. Jeśli mianowicie wL i wN , to rozkładamy słowo w następująco:

w=v1uv2,u1,i=0,1,2 :v1uiv2L

Przyjmując teraz wartość i=0 , uzyskujemy:

v1v2L,v1v2<w

Po skończonej ilości powtórzeń powyższego rozkładu uzyskamy słowo należące do języka, o długości ograniczonej przez N .

2. Wystarczy udowodnić nastepującą równoważność:

L nieskończony wL:Nw<2N.

Jeśli L jest językiem nieskończonym, to znajdziemy w L słowo w dowolnie długie. Niech wN . Jeśli słowo w nie spełnia ograniczenia w<2N , to podobnie jak poprzednio korzystamy z lematu o pompowaniu i po skończonej ilości kroków otrzymamy słowo krótsze od 2N . Istotne jest, że wykorzystując lemat o pompowaniu, możemy założyć, że usuwane słowo u ma długość ograniczoną przez N . Zatem oznacza to, że ze słowa dłuższego od 2N nie dostaniemy słowa krótszego od N .

Jeśli teraz do języka L należy słowo w o długości większej lub równej N , to znów z lematu o pompowaniu wnioskujemy, że

w=v1uv2, :u1iv1u*v2L

Istnieje więc nieskończony podzbiór języka L , a więc i sam język L jest nieskończony.

3. Rozważmy L=(L1L2)(L1L2) . Język L jest regularny, co wynika z domkniętości klasy 3 na operaje boolowskie. Równoważność

LL1L2

sprowadza problem równości języków do problemu niepustości omówionego powyżej.

4. Konstruujemy automat 𝒜=(S,f,s0,T) rozpoznający język L i sprawdzamy, czy f(s0,w)T.

Na podstawie dowodu powyższego twierdzenia nietrudno jest określić algorytmy rozstrzygające przedstawione problemy. Poniżej prezentujemy algorytm rozstrzygający problem należenia słowa do języka regularnego zadanego automatem. Bez straty ogólności możemy założyć, że automat jest deterministyczny.

Algorytm NależenieDoJęzyka -- sprawdza, czy dane słowo należy do języka L akceptowanego przez zadany automat 𝒜



  1  Wejście: 𝒜=(S,A,f,s0,T) - automat akceptujący język L oraz w=w1w2wnA* - słowo.
  2  Wyjście: Odpowiedź true (tak) lub false (nie).
  3  k|w|;
  4  ss0;
  5  for i1 to k do
  6    sf(s,wi);
  7  end for
  8  if  sT then
  9    return true;
 10  else
 11    return false;
 12  end if

Algorytm działa w czasie O(|w|) i posiada złożoność pamięciową O(|A||S|), co spowodowane jest koniecznością przechowywania funkcji przejść automatu 𝒜.

Jeśli język zadany jest nie automatem, a gramatyką regularną, to gramatykę można przekształcić na automat poznanym na początku wykładu algorytmem GReg2Automat, następnie zdeterminizować ten automat i podać go jako wejście dla algorytmu NależenieDoJęzyka.

Jeśli język zadany jest wyrażeniem regularnym, to mając wyrażenie regularne, można zbudować odpowiadający mu automat przy pomocy algorytmu WR2Automat. A zatem, na przykład, z powyższego twierdzenia wynika, iż problem równoważności wyrażeń regularnych jest rozstrzygalny.