Regulární jazyky
DKA, NKA, determinizace, minimalizace, operace s automaty, regulární gramatiky, regulární výrazy, regulární rovnice.
📚 Základní pojmy – abeceda, řetězec, jazyk
{0,1}, {A,C,G,T}, {while, do, begin, ...}.
Řetězec nad Σ je konečná posloupnost symbolů. Prázdný řetězec = ε.
Σ*– množina všech řetězců nad Σ (včetně ε)Σ+– množina všech neprázdných řetězců;Σ* = Σ+ ∪ {ε}- Zřetězení: asociativní, není komutativní; ε je neutrální prvek
- Reverze:
xRobrátí pořadí symbolů;(abcd)R = dcba - Délka:
|x| ≥ 0;|ε| = 0
Σ*. Operace: sjednocení, průnik, rozdíl, doplněk, součin (zřetězení jazyků), iterace (Kleene star L*).
Kleeneho iterace: L* = L0 ∪ L1 ∪ L2 ∪ ... kde L0 = {ε}. Vždy platí ε ∈ L*.
🏛️ Chomského hierarchie
Gramatika G = (N, Σ, P, S) kde N jsou neterminály, Σ terminály (N∩Σ=∅), P pravidla, S počáteční symbol.
| Typ | Název | Tvar pravidel | Automaton |
|---|---|---|---|
| 3 | Regulární | A → aB nebo A → a | Konečný automat |
| 2 | Bezkontextová | A → α, A∈N, α∈(N∪Σ)* | Zásobníkový automat |
| 1 | Kontextová | γAδ → γαδ, |α|≥1 | Lineárně omezený automat |
| 0 | Neomezená | Obecná pravidla | Turingův stroj |
Hierarchie jazyků: Regulární ⊂ Bezkontextové ⊂ Kontextové ⊂ Rekurzivně spočetné.
{0ⁿ1ⁿ : n≥1} je bezkontextový, ale není regulární.
⚙️ Deterministický konečný automat (DKA)
Jak funguje? DKA čte vstupní řetězec symbol po symbolu. Pro každý symbol a v každém stavu je přesně jeden přechod (deterministický). Řetězec je přijat, pokud po přečtení celého vstupu skončí v koncovém stavu.
Konfigurace: dvojice (q, w) ∈ Q × Σ* kde q je aktuální stav, w je dosud nepřečtený vstup.
Přechod: (q, aw') ⊢ (p, w') pokud δ(q, a) = p
Přijímaný jazyk: L(M) = {w : (q0, w) ⊢* (q, ε), q∈F}
δ(q, a) je definována pro každou dvojici (q, a). Neúplný DKA doplníme přidáním nulového stavu q∅, do kterého vedou všechny chybějící přechody. Z q∅ vedou přechody pouze zpět do q∅ a q∅ není koncový.
🔀 Nedeterministický konečný automat (NKA)
δ: Q×Σ → 2^Q (přechodová funkce vrací množinu stavů, nikoliv jediný stav).
Klíčový rozdíl DKA vs NKA:
- DKA: z každého stavu vede pro každý symbol přesně 1 přechod
- NKA: z každého stavu může vést pro jeden symbol 0, 1 nebo více přechodů
- Řetězec přijímán NKA = existuje alespoň jedna akceptující posloupnost přechodů
ε-přechody: NKA může provést přechod bez čtení vstupního symbolu. Funkce ε-Closure(q) vrací všechny stavy dosažitelné z q pomocí ε-přechodů.
δ'(q, a) = ∪_{p ∈ ε-Closure(q)} δ(p, a). Koncové stavy F' = {q : ε-Closure(q) ∩ F ≠ ∅}. Výsledný NKA bez ε-přechodů přijímá stejný jazyk.
Věta: Ke každému NKA existuje ekvivalentní DKA (přijímají stejný jazyk). NKA jsou tedy stejně silné jako DKA – oba rozpoznávají právě regulární jazyky.
🔄 Determinizace NKA → DKA (podmnožinová konstrukce)
Postup:
- Počáteční stav DKA =
{q0} - Pro každý stav DKA (= množinu stavů NKA) q' a každý symbol a:
δ'(q', a) = ∪_{p∈q'} δ(p, a) - Nové stavy přidáváme dokud existují neprozkoumané
- Koncové stavy DKA = stavy obsahující alespoň jeden koncový stav NKA
2^|Q| stavů (exponenciálně více než NKA). V praxi většinou vznikne mnohem méně stavů, protože ne všechny podmnožiny jsou dosažitelné.
Proč to funguje? NKA v každém okamžiku může být v několika stavech najednou. DKA to simuluje tak, že si pamatuje celou množinu možných aktuálních stavů. Tato množina je vždy jednoznačně určena přečteným prefixem vstupního slova.
🗺️ Dosažitelné a užitečné stavy
Dosažitelný stav: stav q je dosažitelný, pokud existuje w ∈ Σ* takové, že (q0, w) ⊢* (q, ε). Nedosažitelné stavy lze bezpečně odstranit (BFS/DFS od q0).
Užitečný stav: stav q je užitečný, pokud existuje w ∈ Σ* takové, že (q, w) ⊢* (f, ε) pro nějaký f∈F. Zbytečné stavy (ze kterých nelze dojít do koncového stavu) lze také odstranit (BFS/DFS od F zpětně).
📐 Minimalizace deterministického konečného automatu
Hopcraftův algoritmus (Myhill-Nerodova věta v praxi):
- Inicializace: Rozděl stavy do dvou tříd: koncové
QII = Fa nekoncovéQI = Q\F - Iterace: Pokud existují dva stavy ve stejné třídě, ale pro nějaký symbol a přecházejí do různých tříd, rozděl jejich třídu
- Opakuj dokud se třídy nemění
- Výsledek: Každá třída = jeden stav minimálního DKA
Na konci algoritmu musí platit: δm(Qi, a) = Qj ⟺ ∀q ∈ Qi: δ(q, a) ∈ Qj
🔧 Operace s konečnými automaty
Regulární jazyky jsou uzavřené na sjednocení, průnik, doplněk, zřetězení a iteraci.
| Operace | Konstrukční technika | Výsledek |
|---|---|---|
| Sjednocení | Nový počáteční stav + ε-přechody do obou automatů, nebo paralelní běh (Q1×Q2, F1×Q2 ∪ Q1×F2) | NKA |
| Průnik | Paralelní běh automatů: Q1×Q2, δ((q1,q2),a) = (δ1(q1,a), δ2(q2,a)), F = F1×F2 | NKA/DKA |
| Doplněk | Prohodíme koncové a nekoncové stavy v úplně určeném DKA | DKA |
| Součin | ε-přechod z F1 do q02; nebo bez ε-přechodů s přidáním q02 ke přechodům vedoucím do F1 | NKA |
| Iterace | Nový počáteční (a zároveň koncový) stav q0' s ε-přechodem do q0; ε-přechody z F zpět do q0 | NKA |
📝 Regulární gramatiky
A → aB nebo A → a, kde A,B∈N, a∈Σ. Povoleno i S → ε pokud S není na pravé straně žádného pravidla.
Převod regulární gramatiky na NKA:
- Stavy NKA = neterminály N ∪ {A} (A je nový akceptující stav)
A → aBv G ⟹δ(A, a) ∋ Bv NKAA → av G ⟹δ(A, a) ∋ A_finv NKA (přechod do akceptujícího stavu)- Počáteční stav NKA = S
Převod NKA na regulární gramatiku:
- Neterminály = stavy NKA
- Pro každý přechod
δ(B, a) ∋ C: pravidloB → aC - Pokud C∈F: také pravidlo
B → a - Pokud q0∈F: speciálně ošetřit (přidat S'→ε nebo S'→...)
🔤 Regulární výrazy
- Základní:
∅,ε,a(pro a∈Σ) jsou regulární výrazy - Složené: jsou-li x, y regulární výrazy, pak
(x+y),(x.y),(x)*jsou také regulární výrazy
Jazyk regulárního výrazu:
L(∅) = ∅,L(ε) = {ε},L(a) = {a}L(x+y) = L(x) ∪ L(y)L(x.y) = L(x).L(y)L(x*) = (L(x))*
Algebraické vlastnosti:
- P4:
x+x = x(idempotence sjednocení) - P7:
∅.x = x.∅ = ∅(∅ je nulový prvek pro zřetězení) - V1:
∅* = ε - V3:
(x*)* = x* - V4:
(x+y)* = (x*y*)* - V9:
(xy)*x = x(yx)*
🔢 Regulární rovnice a převody mezi formalismy
x = xα + β má řešení x = βα*.Pravá regulární rovnice
x = αx + β má řešení x = α*β.
KA → regulární výraz (metoda regulárních rovnic – odchozí přechody):
- Pro každý stav q zapišeme rovnici:
Xq = Σ_a Xp (pro každý přechod δ(q,a)=p) - Ke koncovým stavům přidáme
+ ε - Soustavu pravých regulárních rovnic vyřešíme (substitucí + Ardenovo lemma)
- Výsledek = Xq0
Regulární výraz → NKA (Glushkova metoda – metoda sousedů):
- Očísluj všechny výskyty symbolů ve výrazu:
a1b2*a3 + a4c5 - Zjisti Z (počáteční symboly), K (koncové symboly), P (dvojice sousedů)
- Stavy = {q0} ∪ {ai}; přechody z q0 pro symboly z Z; přechody ai→bj pro každou dvojici aibj ∈ P
- Koncové stavy = K ∪ {q0 pokud ε ∈ L(V)}
RG → NKA, NKA → RG, RV → NKA (Glushkov), NKA → RV (regulární rovnice), RG → RV (regulární rovnice), RV → RG (postupná konstrukce). Všechny tyto převody jsou efektivně algoritmické.
💪 Pumping lemma (PL) pro regulární jazyky
- |y| ≥ 1 (y není prázdné)
- |xy| ≤ p
- ∀k ≥ 0: xy^k z ∈ L (slovo lze „pumpovat")
Intuice: Pokud slovo je dost dlouhé, automat musí navštívit jeden stav dvakrát ⟹ existuje smyčka ⟹ lze ji libovolně opakovat.
Použití PL k důkazu, že L není regulární:
- Předpokládejme, že L je regulární ⟹ existuje p
- Zvolíme konkrétní slovo w ∈ L s |w| ≥ p (typicky
w = 0^p 1^p) - Ukážeme, že pro každé rozdělení w=xyz splňující |y|≥1 a |xy|≤p existuje k, pro které xy^k z ∉ L
- To je spor s PL ⟹ L není regulární
Příklad: L = {0ⁿ1ⁿ : n≥1} není regulární. Vezmeme w = 0ᵖ1ᵖ. Podmínka |xy|≤p zaručuje, že y = 0^j pro j≥1. Pak xy⁰z = 0^(p-j)1^p ∉ L, spor.
🧮 Myhill-Nerodova věta
u ~_L v ⟺ ∀w∈Σ*: uw∈L ⟺ vw∈L. Dva řetězce jsou ekvivalentní, pokud je nelze odlišit žádnou příponou z hlediska příslušnosti do jazyka L.
- L je přijímán deterministickým konečným automatem
- L je sjednocením tříd pravé kongruence konečného indexu na Σ*
- Relace ~_L má konečný index (konečně mnoho tříd ekvivalence)
Praktické použití:
- Důkaz neregularity: ukážeme, že ~_L má nekonečně mnoho tříd. Např. pro
{0ⁿ1ⁿ}: žádné dvě různé mocniny0^ia0^j(i≠j) nejsou ekvivalentní, protože 0^i 1^i ∈ L, ale 0^j 1^i ∉ L. - Konstrukce minimálního DKA: Stavy = třídy ~_L. Počet stavů minimálního DKA = index ~_L (počet tříd).
📌 Shrnutí okruhu 1
- DKA: δ: Q×Σ→Q (deterministická), NKA: δ: Q×Σ→2^Q (nedeterministická); DKA a NKA jsou ekvivalentně silné
- Determinizace NKA→DKA: podmnožinová konstrukce; DKA může mít až 2^|Q| stavů
- Minimalizace DKA: Hopcraftův algoritmus; slučujeme nerozlišitelné stavy; výsledek je unikátní (až na isomorfismus)
- Regulární jazyky jsou uzavřené na ∪, ∩, doplněk, ·, *
- RG ≡ NKA ≡ DKA ≡ RV – všechny popisují přesně regulární jazyky (Kleeneova věta)
❓ Kontrolní otázky k okruhu 1
Bezkontextové jazyky
Bezkontextové gramatiky, zásobníkové automaty, varianta ZA, modely syntaktické analýzy LL (shora dolů) a LR (zdola nahoru).
🌳 Bezkontextové gramatiky (BG)
A → α kde A∈N, α∈(N∪Σ)*. Na levé straně je vždy jeden neterminál, na pravé straně libovolný řetězec.
Klíčový rozdíl od regulárních: Pravá strana pravidla může obsahovat více neterminálů a ve složitém pořadí. To umožňuje popis rekurzivních struktur jako jsou závorky, vnořené bloky kódu apod.
Derivace: α ⇒ β (přímá derivace) pokud v α nahradíme jeden neterminál dle pravidla. α ⇒* β (libovolný počet kroků), α ⇒+ β (alespoň jeden krok).
Uzavřenost BJ: Třída bezkontextových jazyků je uzavřena na sjednocení, součin a iteraci. Není uzavřena na průnik a doplněk!
S → S1 | S2, zachováme P1 a P2.
Praktické využití: BG popisují syntaxi programovacích jazyků (EBNF, BNF). Výrazy jako E → E+T | T zachycují precedenci operátorů.
⚖️ Jednoznačnost a nejednoznačnost gramatik
Klasický příklad – dangling else:
S → if b then S else S | if b then S | a- Věta
if b then if b then a else amá dva derivační stromy - Oprava: separovat S1 (může mít else) a S2 (nemůže mít else)
Proč záleží na jednoznačnosti? Při syntaktické analýze chceme mít jednoznačný derivační strom – určuje sémantiku programu (pořadí operací, vazby proměnných). Nejednoznačná gramatika neumožňuje deterministický parser.
🔧 Transformace bezkontextových gramatik
Zbytečné symboly:
- Negenerující neterminál: nelze z něj odvodit žádné terminální slovo. Algoritmus: BFS – neterminálem přidáváme do Nt ty, z jejichž pravé strany lze odvodit terminální slova. Odstraníme neterminály mimo Nt.
- Nedostupný symbol: nevyskytuje se v žádné větné formě. Algoritmus: BFS od S – přidáváme symboly dostupné z dosud přidaných.
- Zbytečný symbol = negenerující nebo nedostupný. Algoritmus: 1) odstraň negenerující, 2) odstraň nedostupné.
ε-pravidla: Algoritmus k jejich odstranění: 1) Najdi Nε – množinu neterminálů derivujících ε (BFS). 2) Pro každé pravidlo A → X1X2...Xn vytvoř všechny varianty, kde Xi∈Nε může být vynecháno. 3) Zachováme pravidlo S'→ε jen pokud ε∈L(G).
Jednoduchá pravidla A → B (A,B∈N): Algoritmus: Pro každé A najdi NA = {B : A⇒+B} (tranzitivní uzávěr). Pak pravidla A→α (α∉N) přidej i pro každý B∈NA.
📐 Chomského normální tvar (CNF)
A → BC(A,B,C∈N)A → a(a∈Σ)S → ε(jen pokud ε∈L(G) a S není na pravé straně)
Každý BJ lze generovat gramatikou v CNF. Postup převodu z vlastní gramatiky bez jednoduchých pravidel:
- Pravidla
A → BCaA → anecháme - Pravidla
A → X1X2...Xkpro k>2: zavádíme pomocné neterminály Y a rozkládáme:A → X1' Y_X2..Xk, Y → X2' Y_X3..Xk, ... - Terminály v delších pravidlech: místo a přidáme neterminál a' s pravidlem a'→a
📊 Algoritmus Cocke-Younger-Kasami (CYK)
P[j,i] = množina neterminálů, ze kterých lze odvodit podřetězec délky i začínající na pozici j.
Postup:
- Inicializace: P[i,1] = {A : A→xi ∈ P} pro každý symbol xi
- Iterace (délka i od 2 do n, pozice j od 1 do n-i+1): P[j,i] ∪= {A : A→BC, B∈P[j,k], C∈P[j+k,i-k]} pro každé k∈{1,...,i-1}
- Výsledek: x ∈ L(G) ⟺ S ∈ P[1,n]
📚 Zásobníkový automat (ZA)
δ: Q × (Σ∪{ε}) × G* → konečné podmnožiny (Q × G*)
Konfigurace ZA: trojice (q, w, α) kde q∈Q je stav, w∈Σ* je zbytek vstupu, α∈G* je obsah zásobníku (vrchol vlevo).
Přechod: (q, aw, αβ) ⊢ (p, w, γβ) pokud (p, γ) ∈ δ(q, a, α). ZA přečte symbol a, ze zásobníku odebere α a vloží γ.
Dvě varianty přijetí:
- Přechodem do koncového stavu:
L(R) = {w : (q0, w, Z0) ⊢* (q, ε, γ), q∈F} - S prázdným zásobníkem:
Lε(R) = {w : (q0, w, Z0) ⊢* (q, ε, ε)}
Věta: Jazyk L je bezkontextový ⟺ L je přijímán zásobníkovým automatem.
🎯 Deterministický zásobníkový automat (DZA)
|δ(q, a, γ)| ≤ 1pro každé q, a∈Σ∪{ε}, γ∈G*- Pokud δ(q, a, α) ≠ ∅ a δ(q, a, β) ≠ ∅, pak α není prefixem β ani β prefixem α
- Pokud δ(q, a, α) ≠ ∅ a δ(q, ε, β) ≠ ∅, pak α není prefixem β ani β prefixem α
Klíčové omezení: DZA jsou slabší než NZA – přijímají vlastní podtřídu bezkontextových jazyků (deterministické bezkontextové jazyky). Například palindromy nepatří do deterministických BJ.
⬇️ Syntaktická analýza shora dolů (Top-Down, LL)
Konstrukace ZA pro SA shora dolů:
R = ({q}, Σ, N∪Σ, δ, q, S, ∅) kde:
δ(q, ε, A) = {(q, α) : A→α ∈ P}pro každý A∈N (expanze)δ(q, a, a) = {(q, ε)}pro každý a∈Σ (srovnání)
Zásobník obsahuje dosud nevyřešenou část větné formy. Vrchol zásobníku je vlevo.
Problémy nedeterministické SA shora dolů: Pokud má neterminál více pravidel se stejným prvním symbolem na pravé straně, automat neví, které pravidlo použít ⟹ musí hádat (nedeterminismus).
LL(k) gramatiky (Deterministic Top-Down): ZA rozhoduje, které pravidlo použít, na základě aktuálního neterminálu na zásobníku a k dalších symbolů vstupu. LL analýza je obsahem předmětu BI-PJP.
⬆️ Syntaktická analýza zdola nahoru (Bottom-Up, LR)
Konstrukace ZA pro SA zdola nahoru:
R = ({q, r}, Σ, N∪Σ∪{#}, δ, q, #, {r}) kde:
δ(q, a, ε) = {(q, a)}pro každý a∈Σ (přesun / shift)δ(q, ε, α) = {(q, A)}pro každé pravidlo A→α (redukce / reduce)δ(q, ε, #S) = {(r, ε)}(přijetí)
Vrchol zásobníku je vpravo. ZA přesouvá symboly ze vstupu na zásobník (shift), a když vrchol zásobníku odpovídá pravé straně pravidla, redukuje na levou stranu (reduce).
LR(k) gramatiky (Deterministic Bottom-Up): Rozhodnutí shift/reduce na základě zásobníku a k symbolů vstupu. Mnohem silnější třída než LL(k). LR analýza je obsahem předmětu NI-SYP.
🔗 Vztah BG a ZA – varianty zásobníkových automatů
| Model | Přijímaná třída | Poznámka |
|---|---|---|
| NZA (nedeterministický) | Všechny BJ | Ekvivalentní s BG |
| DZA (deterministický) | Deterministické BJ (DCFL) | Striktně slabší než NZA |
| ZA s prázdným zásobníkem | Všechny BJ | Ekvivalentní s NZA (přechodem do konc. stavu) |
- Je-li dána BG G, lze sestrojit ZA R takový, že
Lε(R) = L(G)(model SA shora dolů) - Je-li dána BG G, lze sestrojit ZA R takový, že
L(R) = L(G)(model SA zdola nahoru) - Naopak: ke každému ZA existuje ekvivalentní BG
Překlad výše na modely SA:
- SA shora dolů = ZA s expanzí neterminálů na zásobníku + srovnáváním symbolů (levá derivace)
- SA zdola nahoru = ZA s přesuny na zásobník a redukcemi (pravá derivace)
📋 Vlastnosti bezkontextových jazyků
Uzavřenost BJ:
| Operace | Uzavřenost BJ? | Poznámka |
|---|---|---|
| Sjednocení ∪ | ✅ ANO | Nový počáteční symbol S→S1|S2 |
| Součin (zřetězení) | ✅ ANO | S→S1 S2 |
| Iterace (Kleene *) | ✅ ANO | S'→SS'|ε |
| Průnik ∩ | ❌ NE | Protipříklad: {aⁿbⁿcⁿ} |
| Doplněk | ❌ NE | Z uzavřenosti ∪ a neuzavřenosti ∩ |
| Průnik s reg. jazykem | ✅ ANO | Simulace ZA + DKA paralelně |
L1 = {aⁿbⁿcᵐ : n,m≥1} a L2 = {aᵐbⁿcⁿ : n,m≥1} jsou oba BJ, ale L1 ∩ L2 = {aⁿbⁿcⁿ : n≥1} není BJ (lze dokázat PL pro BJ).
Pumping lemma pro BJ: Nechť L je BJ. Pak ∃p≥1 takové, že každé w∈L s |w|≥p lze zapsat jako w = uvxyz kde |vy|≥1, |vxy|≤p, a ∀k≥0: uv^k xy^k z ∈ L. (Obě části v a y se pumpují souběžně.)
📌 Shrnutí okruhu 2
- BG: každé pravidlo A→α, A∈N; popisuje syntaxi programovacích jazyků; derivační strom reprezentuje strukturu
- Nejednoznačnost BG: ≥2 derivační stromy pro 1 větu; inherentně nejednoznačné jazyky existují
- Transformace: odstranění zbytečných symbolů, ε-pravidel, jednoduchých pravidel → vlastní BG; CNF pro CYK
- ZA: rozpoznává přesně BJ (NZA ≡ BG); DZA je slabší (DCFL ⊊ CFL)
- SA shora dolů (LL): expanze neterminálů, levá derivace; SA zdola nahoru (LR): shift-reduce, pravá derivace