🤖 BI-AAG – Automaty a gramatiky

Státnicové poznámky · Bakalářské státní zkoušky · FIT ČVUT

Regulární jazyky Bezkontextové jazyky Chomského hierarchie Jan Holub · 2025/2026
Okruh 1 · BI-SPOL.21-1

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

Definice – Abeceda
Abeceda Σ je konečná neprázdná množina symbolů. Příklady: {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: xR obrátí pořadí symbolů; (abcd)R = dcba
  • Délka: |x| ≥ 0; |ε| = 0
Definice – Formální jazyk
Formální jazyk L nad Σ je libovolná podmnožina Σ*. 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*.

Proč to tak je?
Formální jazyky jsou matematickým základem pro popis syntaxe programovacích jazyků. Každý zdrojový kód je řetězec nad abecedou (tokeny/znaky), a syntakticky správné programy tvoří jazyk. Automaty a gramatiky pak umožňují tyto jazyky efektivně rozpoznávat.

🏛️ Chomského hierarchie

Gramatika G = (N, Σ, P, S) kde N jsou neterminály, Σ terminály (N∩Σ=∅), P pravidla, S počáteční symbol.

TypNázevTvar pravidelAutomaton
3RegulárníA → aB nebo A → aKonečný automat
2BezkontextováA → α, A∈N, α∈(N∪Σ)*Zásobníkový automat
1KontextováγAδ → γαδ, |α|≥1Lineárně omezený automat
0NeomezenáObecná pravidlaTuringův stroj

Hierarchie jazyků: Regulární ⊂ Bezkontextové ⊂ Kontextové ⊂ Rekurzivně spočetné.

Klíčová myšlenka
Každý regulární jazyk je i bezkontextový, každý bezkontextový je i kontextový atd. Containment je striktní – např. {0ⁿ1ⁿ : n≥1} je bezkontextový, ale není regulární.

⚙️ Deterministický konečný automat (DKA)

Definice
DKA M = (Q, Σ, δ, q0, F) kde Q = konečná množina stavů, Σ = vstupní abeceda, δ: Q×Σ→Q přechodová funkce, q0∈Q počáteční stav, F⊆Q množina koncových stavů.

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}

Úplně určený DKA
DKA je úplně určený, pokud δ(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)

Definice
NKA M = (Q, Σ, δ, q0, F) – stejná struktura jako DKA, ale δ: 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ů.

Odstranění ε-přechodů
Algoritmus: nový 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)

Algoritmus – Převod NKA na DKA
Idea: Každý stav nového DKA odpovídá množině stavů původního NKA. Stavy DKA jsou tedy podmnožiny Q.

Postup:

  1. Počáteční stav DKA = {q0}
  2. Pro každý stav DKA (= množinu stavů NKA) q' a každý symbol a: δ'(q', a) = ∪_{p∈q'} δ(p, a)
  3. Nové stavy přidáváme dokud existují neprozkoumané
  4. Koncové stavy DKA = stavy obsahující alespoň jeden koncový stav NKA
Exponenciální nárůst
DKA může mít až 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ě).

Praktický dopad
Před minimalizací odstraníme nedosažitelné a zbytečné stavy. Automat bez takových stavů je čistší a menší – z každého stavu vede cesta do koncového stavu.

📐 Minimalizace deterministického konečného automatu

Definice – Minimální DKA
DKA je minimální, pokud neexistuje ekvivalentní DKA s menším počtem stavů.

Hopcraftův algoritmus (Myhill-Nerodova věta v praxi):

  1. Inicializace: Rozděl stavy do dvou tříd: koncové QII = F a nekoncové QI = Q\F
  2. 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
  3. Opakuj dokud se třídy nemění
  4. Výsledek: Každá třída = jeden stav minimálního DKA
Proč to funguje?
Dva stavy jsou rozlišitelné, pokud existuje řetězec w takový, že z jednoho stavu se po přečtení w dostaneme do koncového stavu a z druhého ne. Minimalizace slučuje nerozlišitelné stavy. Myhill-Nerodova věta dokazuje, že takto vzniklý automat je skutečně nejmenší možný.

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.

OperaceKonstrukční technikaVýsledek
SjednoceníNový počáteční stav + ε-přechody do obou automatů, nebo paralelní běh (Q1×Q2, F1×Q2 ∪ Q1×F2)NKA
PrůnikParalelní běh automatů: Q1×Q2, δ((q1,q2),a) = (δ1(q1,a), δ2(q2,a)), F = F1×F2NKA/DKA
DoplněkProhodíme koncové a nekoncové stavy v úplně určeném DKADKA
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 F1NKA
IteraceNový počáteční (a zároveň koncový) stav q0' s ε-přechodem do q0; ε-přechody z F zpět do q0NKA
Doplněk – pozor!
Algoritmus doplněk funguje pouze na úplně určeném DKA. Na NKA nestačí jen prohodit stavy – nejprve NKA determinizujeme, doplníme na úplně určený DKA, pak teprve prohodíme koncové/nekoncové stavy.

📝 Regulární gramatiky

Definice – Regulární gramatika
Regulární (pravá lineární) gramatika: každé pravidlo má tvar 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 → aB v G ⟹ δ(A, a) ∋ B v NKA
  • A → a v G ⟹ δ(A, a) ∋ A_fin v 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: pravidlo B → aC
  • Pokud C∈F: také pravidlo B → a
  • Pokud q0∈F: speciálně ošetřit (přidat S'→ε nebo S'→...)
Vzájemná ekvivalence
Regulární gramatiky, konečné automaty a regulární výrazy popisují přesně tutéž třídu jazyků – regulární jazyky. Lze mezi nimi libovolně převádět.

🔤 Regulární výrazy

Definice
Regulární výraz V nad Σ je definován induktivně:
  • 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)*
Kleeneova věta
Jazyk je regulární právě tehdy, když je přijímán konečným automatem, právě tehdy, když ho lze popsat regulárním výrazem.

🔢 Regulární rovnice a převody mezi formalismy

Ardenovo lemma
Levá regulární rovnice 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):

  1. Pro každý stav q zapišeme rovnici: Xq = Σ_a Xp (pro každý přechod δ(q,a)=p)
  2. Ke koncovým stavům přidáme + ε
  3. Soustavu pravých regulárních rovnic vyřešíme (substitucí + Ardenovo lemma)
  4. Výsledek = Xq0

Regulární výraz → NKA (Glushkova metoda – metoda sousedů):

  1. Očísluj všechny výskyty symbolů ve výrazu: a1b2*a3 + a4c5
  2. Zjisti Z (počáteční symboly), K (koncové symboly), P (dvojice sousedů)
  3. Stavy = {q0} ∪ {ai}; přechody z q0 pro symboly z Z; přechody ai→bj pro každou dvojici aibj ∈ P
  4. Koncové stavy = K ∪ {q0 pokud ε ∈ L(V)}
Přehled všech převodů
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

Pumping lemma (formálně)
Nechť L je regulární jazyk. Pak existuje konstanta p ≥ 1 taková, že pro každé slovo w ∈ L s |w| ≥ p existuje rozdělení w = xyz splňující:
  • |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í:

  1. Předpokládejme, že L je regulární ⟹ existuje p
  2. Zvolíme konkrétní slovo w ∈ L s |w| ≥ p (typicky w = 0^p 1^p)
  3. 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
  4. To je spor s PL ⟹ L není regulární
Pozor – PL je jen nutná podmínka!
Splnění PL nezaručuje, že jazyk JE regulární – existují neregulárnní jazyky, které PL splňují. PL umíme použít jen k důkazu, že jazyk 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

Prefixová ekvivalence
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.
Myhill-Nerodova věta
Následující tvrzení jsou ekvivalentní:
  1. L je přijímán deterministickým konečným automatem
  2. L je sjednocením tříd pravé kongruence konečného indexu na Σ*
  3. 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é mocniny 0^i a 0^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).
MNV vs Pumping lemma
MNV je silnější nástroj – je nutná i postačující podmínka regularity. PL je pouze nutná podmínka. Pro zkoušku je klíčové umět obě použít k důkazu neregularity.

📌 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

1. Jaký je rozdíl mezi DKA a NKA?
DKA má δ: Q×Σ→Q – z každého stavu pro každý symbol přesně jeden přechod. NKA má δ: Q×Σ→2^Q – z každého stavu může vést více přechodů (nebo žádný). NKA přijímá řetězec, pokud existuje alespoň jedna akceptující cesta. Oba modely jsou ekvivalentně silné – rozpoznávají přesně regulární jazyky.
2. Popište algoritmus determinizace NKA → DKA.
Podmnožinová konstrukce: stavy DKA jsou podmnožiny stavů NKA. Počáteční stav DKA = {q0}. Pro každý stav q'⊆Q a symbol a: δ'(q', a) = ∪_{p∈q'} δ(p, a). Přidáváme nové stavy dokud existují neprozkoumané. Koncové stavy DKA = podmnožiny obsahující alespoň jeden stav z F. Výsledný DKA může mít až 2^n stavů.
3. Jak probíhá minimalizace DKA?
Hopcraftův algoritmus: 1) Rozděl Q na koncové F a nekoncové Q\F. 2) Opakuj: pokud existují dva stavy ve stejné třídě, ale přecházejí pro nějaký symbol do různých tříd, rozděl třídu. 3) Opakuj dokud se třídy nemění. 4) Každá třída je jeden stav minimálního DKA. Minimální DKA je jedinečný (až na isomorfismus).
4. Co říká Pumping lemma a jak ho použijeme k důkazu neregularity?
PL: Pro každý regulární jazyk L ∃p≥1 takové, že každé w∈L s |w|≥p lze zapsat jako w=xyz s |y|≥1, |xy|≤p, a xy^k z ∈ L pro všechna k≥0. Důkaz neregularity: sporem – předpokládáme existenci p, zvolíme vhodné w, ukážeme, že pro každé rozdělení xyz (splňující podmínky) ∃k tak, aby xy^k z ∉ L.
5. Jak funguje Ardenovo lemma a k čemu slouží?
Ardenovo lemma: Rovnice x = αx + β má řešení x = α*β. Používá se při výpočtu regulárního výrazu z konečného automatu metodou regulárních rovnic – každý stav dostane proměnnou a soustavu rovnic vyřešíme substitucí a Ardenovým lematem. Výsledek pro počáteční stav je hledaný regulární výraz.
6. Co je Myhill-Nerodova věta a jak se liší od Pumping lemmatu?
MNV říká: L je regulární ⟺ prefixová ekvivalence ~_L má konečný index. MNV je nutná a postačující podmínka regularity. PL je jen nutná podmínka. MNV je silnější – pomocí ní lze dokázat neregularitu v případech, kdy PL nestačí (existují jazyky splňující PL, které nejsou regulární). Počet tříd ~_L = počet stavů minimálního DKA.
Okruh 2 · BI-SPOL.21-2

Bezkontextové jazyky

Bezkontextové gramatiky, zásobníkové automaty, varianta ZA, modely syntaktické analýzy LL (shora dolů) a LR (zdola nahoru).

🌳 Bezkontextové gramatiky (BG)

Definice
Bezkontextová gramatika G = (N, Σ, P, S) – každé pravidlo v P má tvar 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).

Derivační strom
Derivační strom graficky vyjadřuje strukturu derivace. Kořen = S, vnitřní uzly = neterminály, listy = terminály (nebo ε). Čtením listů zleva doprava dostaneme derivovanou větu. Derivačnímu stromu odpovídá jednoznačně levá i pravá derivace.

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!

Algoritmus pro sjednocení
Mějme G1 = (N1,Σ,P1,S1) a G2 = (N2,Σ,P2,S2), N1∩N2=∅. Gramatika pro L1∪L2: přidáme nový S, pravidla 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

Definice – Nejednoznačná BG
BG G je nejednoznačná (víceznačná), pokud existuje věta w∈L(G) s alespoň dvěma různými derivačními stromy. Jinak je jednoznačná.

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 a má dva derivační stromy
  • Oprava: separovat S1 (může mít else) a S2 (nemůže mít else)
Inherentně nejednoznačné jazyky
Existují jazyky, pro které neexistuje žádná jednoznačná gramatika. Pro takové jazyky nelze nejednoznačnost odstranit přeformulováním gramatiky. Navíc nelze algoritmicky rozhodnout, zda daná BG je nejednoznačná (redukcí z Postova korespondenčního problému).

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.

Vlastní gramatika
BG je vlastní, pokud je bez cyklů (A ⇒+ A), bez ε-pravidel a bez zbytečných symbolů. Každý neprázdný bezkontextový jazyk lze generovat vlastní gramatikou.

📐 Chomského normální tvar (CNF)

Definice – CNF
BG je v Chomského normálním tvaru, pokud každé pravidlo má tvar:
  • 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:

  1. Pravidla A → BC a A → a necháme
  2. Pravidla A → X1X2...Xk pro k>2: zavádíme pomocné neterminály Y a rozkládáme: A → X1' Y_X2..Xk, Y → X2' Y_X3..Xk, ...
  3. Terminály v delších pravidlech: místo a přidáme neterminál a' s pravidlem a'→a
Proč CNF?
CNF je kanonická forma využívaná v algoritmech pro syntaktickou analýzu – zejména CYK. V CNF má každý derivační strom věty délky n přesně 2n-1 vnitřních uzlů.

📊 Algoritmus Cocke-Younger-Kasami (CYK)

CYK – dynamické programování
Vstup: BG G v CNF, řetězec x = x1x2...xn. Výstup: x ∈ L(G)?
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:

  1. Inicializace: P[i,1] = {A : A→xi ∈ P} pro každý symbol xi
  2. 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}
  3. Výsledek: x ∈ L(G) ⟺ S ∈ P[1,n]
Složitost
CYK algoritmus běží v čase O(n³ · |G|) kde n = délka slova, |G| = velikost gramatiky. Je to nejúčinnější obecný algoritmus pro rozpoznávání BJ (lepší speciální algoritmy existují pro podtřídy jako LL nebo LR gramatiky).

📚 Zásobníkový automat (ZA)

Definice
ZA R = (Q, Σ, G, δ, q0, Z0, F) kde Q = stavy, Σ = vstupní abeceda, G = zásobníková abeceda, δ = přechodová funkce, q0 = počáteční stav, Z0∈G = počáteční symbol zásobníku, F = koncové stavy.
δ: 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, ε, ε)}
Ekvivalence obou přijetí
Věta: Pro každý ZA Pf (přijímající koncovým stavem) existuje ekvivalentní ZA Pε (přijímající prázdným zásobníkem) a naopak. Obě varianty jsou ekvivalentní.

Věta: Jazyk L je bezkontextový ⟺ L je přijímán zásobníkovým automatem.

🎯 Deterministický zásobníkový automat (DZA)

Definice – Deterministický ZA
ZA R je deterministický, pokud:
  1. |δ(q, a, γ)| ≤ 1 pro každé q, a∈Σ∪{ε}, γ∈G*
  2. Pokud δ(q, a, α) ≠ ∅ a δ(q, a, β) ≠ ∅, pak α není prefixem β ani β prefixem α
  3. 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.

Rozdíl oproti konečným automatům
U KA jsou DKA a NKA ekvivalentně silné. U zásobníkových automatů DZA ⊊ NZA – deterministické ZA jsou slabší. Existují bezkontextové jazyky, které nelze rozpoznat deterministickým ZA.

⬇️ Syntaktická analýza shora dolů (Top-Down, LL)

Definice
SA shora dolů = hledání levého rozkladu věty. ZA konstruuje derivační strom od kořene dolů, vždy expanduje neterminál nejvíce vlevo.

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.

Levý rozklad
Při SA shora dolů ZA vytváří levou derivaci a levý rozklad = posloupnost čísel použitých pravidel v levé derivaci. Příklad: pro gramatiku výrazů a větu a+a*a je levý rozklad 1,2,4,6,3,4,6,6.

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)

Definice
SA zdola nahoru = hledání pravého rozkladu věty (obrácená posloupnost pravidel pravé derivace). ZA konstruuje derivační strom od listů nahoru.

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).

Pravý rozklad
Pravý rozklad = obrácená posloupnost čísel pravidel použitých v pravé derivaci. Příklad: pro a+a*a je pravý rozklad 6,4,2,6,4,6,3,1. Tento pořadí odpovídá pořadí redukcí v ZA zdola nahoru.

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ů

ModelPřijímaná třídaPoznámka
NZA (nedeterministický)Všechny BJEkvivalentní s BG
DZA (deterministický)Deterministické BJ (DCFL)Striktně slabší než NZA
ZA s prázdným zásobníkemVšechny BJEkvivalentní s NZA (přechodem do konc. stavu)
Hlavní věta o BG a ZA
  • 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:

OperaceUzavřenost BJ?Poznámka
Sjednocení ∪✅ ANONový počáteční symbol S→S1|S2
Součin (zřetězení)✅ ANOS→S1 S2
Iterace (Kleene *)✅ ANOS'→SS'|ε
Průnik ∩❌ NEProtipříklad: {aⁿbⁿcⁿ}
Doplněk❌ NEZ uzavřenosti ∪ a neuzavřenosti ∩
Průnik s reg. jazykem✅ ANOSimulace ZA + DKA paralelně
Důležité omezení
BJ nejsou uzavřeny na průnik – průnik dvou BJ nemusí být BJ. Například 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

❓ Kontrolní otázky k okruhu 2

1. Co je bezkontextová gramatika a jaký je vztah mezi BG a zásobníkovým automatem?
BG G = (N, Σ, P, S) kde každé pravidlo má tvar A→α, A∈N. BG a NZA jsou ekvivalentní: ke každé BG existuje NZA přijímající stejný jazyk a naopak. Konkrétně: konstruujeme ZA s jedním stavem, kde expanze neterminálu ze zásobníku odpovídá aplikaci pravidla gramatiky.
2. Jaký je rozdíl mezi SA shora dolů (LL) a SA zdola nahoru (LR)?
LL (shora dolů): konstruuje levý rozklad, vždy expanduje neterminál nejvíce vlevo; ZA má na zásobníku dosud nevyřešenou větnou formu, vrchol vlevo. LR (zdola nahoru): konstruuje pravý rozklad (obráceně); ZA přesouvá symboly ze vstupu na zásobník (shift) a redukuje pravé strany pravidel na levé (reduce); vrchol zásobníku je vpravo. LR parsery jsou silnější (zpracovávají více gramatik) než LL parsery.
3. Popište Chomského normální tvar a k čemu se používá.
CNF: každé pravidlo má tvar A→BC nebo A→a (případně S→ε). Každý BJ lze generovat gramatikou v CNF. Převod: rozkládáme dlouhá pravidla pomocnými neterminály, terminály v dlouhých pravidlech nahrazujeme novými neterminály. CNF se používá v CYK algoritmu pro rozpoznávání (složitost O(n³)).
4. Na jakých operacích jsou BJ uzavřeny a na jakých ne?
BJ jsou uzavřeny na: sjednocení (S→S1|S2), součin (S→S1S2), iteraci (S'→SS'|ε), průnik s regulárním jazykem (paralelní simulace ZA+DKA). BJ nejsou uzavřeny na průnik dvou BJ ani na doplněk. Protipříklad pro průnik: L1∩L2 = {aⁿbⁿcⁿ} kde L1={aⁿbⁿcᵐ} a L2={aᵐbⁿcⁿ} jsou BJ, ale jejich průnik není.
5. Co je deterministický zásobníkový automat a jak se liší od nedeterministického?
DZA: z každé konfigurace existuje nejvýše jedna možná akce; bez konfliktu mezi přechodem na symbol a ε-přechodem; žádné dva použitelné přechody nesmí mít jeden prefix druhého na zásobníku. DZA jsou slabší – přijímají jen deterministické BJ (DCFL), což je vlastní podmnožina BJ. Na rozdíl od KA (kde DKA≡NKA) zde DZA ⊊ NZA.
6. Vysvětlete CYK algoritmus.
CYK pracuje s BG v CNF. Tabulka P[j,i] = množina neterminálů derivujících podřetězec délky i od pozice j. Inicializace: P[i,1] z pravidel A→xi. Rekurze: P[j,i] = {A : A→BC, B∈P[j,k], C∈P[j+k,i-k] pro nějaké k}. Přijato: S∈P[1,n]. Složitost O(n³). Je to obecný algoritmus – funguje pro libovolnou BG v CNF, bez omezení na LL/LR.
7. Co je nejednoznačná gramatika a proč je to problém?
BG je nejednoznačná, pokud existuje věta s alespoň dvěma různými derivačními stromy. Problém: derivační strom určuje sémantiku (pořadí operací atd.). Nejednoznačná gramatika vede k nejednoznačné sémantice. Příklad: dangling else – „if b then if b then a else a" lze přiřadit else k oběma then. Řešení: přeformulovat gramatiku (oddělit věty s else a bez). Existují inherentně nejednoznačné jazyky, pro které nelze sestrojit jednoznačnou gramatiku.