Equivalenza tra PDA e Grammatiche Context Free (CFG)

Traduzione non autorizzata delle pagine del capitolo 2.2 di Sipser. In blu, i miei commenti.

In questa sezione mostriamo che le grammatiche context-free e gli automi pushdown sono equivalenti nelle loro potenzialità. Entrambi sono in grado di descrivere la classe dei linguaggi context-free. Mostriamo come convertire ogni grammatica context-free in un automa pushdown che riconosce lo stesso linguaggio e viceversa. Ricordiamo che abbiamo definito un linguaggio context-free come un linguaggio che può essere descritto con una grammatica context-free, il nostro obiettivo nel teorema seguente.

2.20 Teorema

Un linguaggio è context-free se e solo se un automa pushdown lo riconosce.

Come sempre, quando c'è un "se e solo se" in un teoriema, abbiamo due direzioni da provare. In questo teorema entrambe le direzioni sono interessanti (=incasinate). Prima, facciamo la direzione "se", più facile.

2.21 Lemma

Se un linguaggio è context-free, allora un automa pushdown lo riconosce.

Idea

Sia A un CFL. Dalla definizione, sappiamo che A ha una CFG, G, che lo genera. Mostriamo come convertire G in un PDA equivalente, che chiamiamo P.

Il PDA P che ora descriviamo accetta i suoi input w, se G genera questo input, determinando se c'è una derivazione per w. Ricordiamo che una derivazione è semplicemente la sequenza di sostituzioni fatte allo stesso modo in cui una grammatica genera una stringa. Ogni passo della derivazione produce una stringa intermedia di variabili e terminali. Disegnamo P per determinare se qualche serie di sostituzioni, usando le regole di G, possono condurre dalla variabile di partenza a w.

Una delle difficoltà nel testare se c'è una derivazione per w è immaginare quali sostituzioni fare. Il nondeterminismo del PDA permette di indovinare la sequenza di sostituzioni corrette. A ogni passaggio della derivazione, una delle regole per una variabile particolare è selezionata nondeterministicamente e usata per sostituire questa variabile.

Il PDA P inizia scrivendo la variabile di partenza nel suo stack. Passa attraverso una serie di stringhe intermedie, facendo una sostituzione dopo l'altra. Eventualmente può arrivare a una stringa che contiene solo simboli terminali, che significa che ha usato la grammatica per derivare la stringa (bingo!). Allora P accetta se la stringa è identica a quella che ha ricevuto in input.

Implementare questa strategia su un PDA richiede un'idea addizionale. Abbiamo bisogno di vedere come il PDA memorizza la stringa intermedia per andare da una a un'altra. Usare semplicemente lo stack per memorizzare ogni stringa intermedia è una tentazione. Però questo non funzione bene, perché il PDA ha bisogno di trovare la variabile nella stringa intermedia e fare sostituzioni. Il PDA può accedere solo al simbolo in cima allo stack e che può essere un simbolo terminale dentro una variabile. Il modo per aggirare il poblema è tenere solo una parte della stringa intermedia nello stack: i simboli con cui inizia la prima variabile nella stringa intermedia. Ogni simbolo terminale che appare prima della prima variabile è messa in corrispondenza immediatamente con i simboli nella stringa di input. La figura seguente illustra il PDA P.

Figura 2.22: P rappresenta la stringa intermedia 01A1A0

Di seguito, una descrizione informale di P.

  1. Metti il simbolo marcatore $ e la variabile di partenza nello stack
  2. Ripeti i seguenti passi all'infinito
    1. se la cima dello stack è una variabile simbolo A scegliere nondeterministicamente una delle regole per A e sostituire A con la stringa sul lato destro della regola
    2. se la cima dello stack è il simbolo terminale a, leggere il simbolo successivo dall'input e confrontarlo con a. Se è lo stesso, ripetere. Se non è lo stesso, rigettare questo ramo del nondeterminismo
    3. se la cima dello stack è il simbolo terminale $, entrare nello stato accettato. Facendo così, si accetta l'input se è stato letto interamente.

Qui manca un esempio, perché non si capisce molto.

Immaginiamo un PDA fatto così:

S → aAb

A → # | aAa

La stringa aa#ab è riconosciuta? Servono alcuni passaggi: aaAab (A = #), aAb (A = aAa), S (S = aAb), per cui va mantenuta la stringa intermedia.

  1. metto nello stack il simbolo di dollaro $ seguito da aAb, cioè la variabile di partenza, esclusi i terminali a destra. Il mio stack contiene $aA, e siccome è una LIFO, estraggo nell'ordine Aa$
  2. la cima dello stack è una variabile, quindi nondeterministicamente sostituisco A con # e aAa in due diversi branch
    1. # ⇒ nello stack ho #a$, ma quando confronto a#b con la mia stringa, ottengo una differenza, poiché il secondo carattere non è # ma a
    2. aAa => nello stack ho aAaa$, quindi la mia stringa completa è aaAab, ma quando vado a ricontrollarla sostituisco nondeterministicamente A con # e con aAa
      1. # ⇒ ottengo aa#ab, che è la mia stringa
      2. aAa ⇒ scarto questa opzione perché otterrei aaaAaab, ma il terzo carattere è # e non a.

Non ho capito ancora in che modo funziona lo scarico/carico dello stack.

Prova

Ora diamo un dettaglio formale della costruzione dell'automa pushdown P = (Q, Σ, Γ, δ, q1, F) . Per fare una costruzione più chiara, usiamo l'abbreviazione per la funzione di transizione. QUesta notazione fornisce un modo per scrivere una stringa intera nello stack in un passaggio della macchina. Possiamo simulare questa azione introducendo degli stati addizionali per scrivere la stringa un simbolo alla volta, come implementato nella seguente costruzione formale.

Siano q e r stati del PDA e sia a in Σε e s sia in Γε. Vogliamo che il PDA vada da q a r e legge a ed estrae s. Inoltre vogliamo che allo stesso tempo si spinga la stringa intera u = u1...ul nello stack. Possiamo implementare questa azione introducendo dei nuovi stati q1, ..., ql-1 e impostando la funzione di transizione in questo modo:

δ(q, a, s) per contenere (q1, ul),
δ(q1, ε, ε) = {(q2, ul-1)},
δ(q2, ε, ε) = {(q3, ul-2)},
δ(q3, ε, ε) = {(q4, ul-2)},
...
δ(ql-1, ε, ε) = {(r, u1)}.

Usiamo la notazione (r, u) ∈ δ(q, a ,s) per dire che quando q è lo stato dell'automa, a è il prossimo simbolo di input, s è il simbolo in cima allo stack, il PDA può leggere la a e prelevare la s, allora inserisce la stringa u nello stack e va allo stato r. La figura seguente mostra questa implementazione:

Figura 2.23: implementazione dell'abbreviazione (r, xyz) ∈ δ(q, a, s)

Gli stati di P sono Q = {qstart, qloop, qaccept} ∪ E, dove E è l'insieme di stati di cui abbiamo bisogno per implementare la scorciatoria appena descritta. Lo stato di inizio è qstart. L'unico stato accettato è qaccept.

La funzione di transizione è definita di seguito. Cominciamo inizializzando lo stack perché contenga i simboli $ e S, implementando il passo 1 nella descrizione informale: δ(qstart, ε, ε) = {(qloop, S$)}. Poi facciamo le transizioni per il ciclo principale del passo 2.

Prima ci occupiamo del caso (a), se la cima dello stack contiene una variabile. Sia δ(qloop, ε, A) = {(qloop, w) | dove A → w è una regola in R}.

Secondo, gestiamo il caso (b), se la cima dello stack contiene un terminale. Si ha che δ(qloop, a, a) = {(qloop, ε)}.

Infine, gestiamo il caso (c), se il marcatore di stack vuoto $ è in cima allo stack. Si ha che δ(qloop, ε, $) = {(qaccept, ε)}.

Il diagramma di stato è mostrato nella figura 2.24.

Figura 2.24: il diagramma di stato di P

Questo completa la prova del lemma 2.21.

La seconda parte la farò se e quando lo riterrò utile

Suggerimenti per il pumping lemma

Ieri siamo andati a parlare con il prof Calzavara e abbiamo raccolto un'importante indicazione sull'applicazione del pumping lemma.

  1. Non fare l'errore di applicare il PL a un esempio, "dimenticandosi" di includere la lunghezza p nella dimostrazione.

Per esempio, la soluzione di 1.49/b non può essere di prendere una stringa 1111010101... perché presuppone che p≤4, ma non prova niente nel caso in cui p>4.

Un'idea di fondo più "solida" sarebbe dire che abbiamo una stringa 1p01p che sicuramente è più lunga di p, e la cui parte sinistra sia xy, mentre z è la parte a destra dello zero. Facendo il pumping up si arriva ad avere un numero di 1 a sinistra maggiore del numero di 1 a destra, che contraddice la definizione del linguaggio.

  1. Il costrutto contrario di "per tutti esiste uno" è "per qualcuno non esiste nessuno".

Non è sufficiente trovare un caso in cui qualcosa non funziona, ma bisogna trovare un caso in cui niente funziona.

  1. In ogni caso, quando si parla di pumping lemma più che dividere la stringa in tre parti è meglio valutare se dividerla in una "parte sinistra" pompabile e una parte destra.

Più che cercare di definire quale sia la suddivisione in xyz, è meglio definire le caratteristiche che dovrebbe avere questa suddivisione.

  1. Il pumping lemma nei linguaggi context-free è più complicato, perché non c'è più una "parte sinistra" pompabile, ma c'è una suddivisione in cinque della stringa per cui la zona pompabile diventa quella centrale.

exercise 1.29

Use the pumping lemma to show that the following languages are not regular.
a. A1 = {0n1n2n| n ≥ 0}
b. A2 = {www | w ∈ {a, b}}
c. A3 = {a2n| n ≥ 0} (Here, a2n means a string of 2n a’s.)

a. Theorem

A1 = {0n1n2n| n ≥ 0} is not regular.

a. Proof

Applying the pumping lemma to a string that matches with A1 we can have two scenarios:

  1. y is composed by only 0s or 1s or 2s. For example, 012 with y = 1. When we change the number of ys, we obtain a different number of 1s than others: 02, 0112, 01112, ..., and this doesn't match with A1 anymore
  2. y is composed by a pair or more of numbers. For example, 001122, with y = 0011. Changing the number of ys, the order of the numbers are not kept: 22, 0011001122, 00110011001122...

so A1 is not regular.

b. Theorem

A2 = {www | w ∈ {a, b}} is not regular.

b. Proof

We show that choosing a pattern w, composed by as and bs, the language A2, that recognizes the pattern w repeated three times, is not regular.

If the y part of the pumping lemma is composed by a substring of w, repeat the y more times means don't match A2 anymore, because the string can be composed by other than ws. I.E. w = aab, a valid string is aabaabaab, y = the central pair aa: repeating y, we obtain: aabbaab (0 times), aabaabaab (matches), aabaaaabaab (1 time). The first and the third examples are not composed by ws.

If the y part of the pumping lemma is exactly w, repeat the y a number of times different by 1 mod 3 (1, 4, 7, 10...) doesn't match the rule of three times w. I.E. w = aab, y = aab ⇒ aabaab (0 times, doesn't match); aabaabaab (1 time, matches), aabaabaabaab (2 times, doesn't match)...

So, A2 is not regular.

c. Theorem

A3 = {a2n| n ≥ 0} (Here, a2n means a string of 2n a’s.)

c. Proof

A3 recognizes a, aa, aaaa, aaaaaaaa, ...

Choose x = a, y = aa, z = a seems a good idea, because we're sure that every string recognized by A3 is writable with an xyiz, but there are two problems: a single "a" is not writable with this pattern, and there are strings that are writable but don't be recognized by A3, for example, i = 2 ⇒ a aa aa a (the spaces have been added to make the string more readable).

Choosing a simpler definition of x, y, z, as x = ε, y = a, z = ε, we are affected by the same problem.

exercise 1.49

a. Theorem

Let B = {1ky|y∈{0,1}* and y contains at least k 1s, for k ≥ 1}. Show that B is a regular language.

a. Proof

B equals to "Let B is a string that begins with 1 and, after, contains at least another 1".

This DFA doesn't "count" the number of 1s

b. Theorem

Let B = {1ky|y∈{0,1}* and y contains at most k 1s, for k ≥ 1}. Show that B is not a regular language.

b. Proof

As shown in 1.77 Sipser's example, we can apply an absurd proof of the pumping lemma to this language

Let we assume that B is regular. Let we choose one 1 as pumping's y and the second part (i.e. from the first 0 found) as pumping's z, and it seems to work:

1111y0100z

or better

1y11y21y31yn0100z

Pumping lemma doesn't allows a length of 0 for the y part, but allows that y is repeated zero times.

Using as example the previous string, with y repeated zero times, we obtain the string 0100, that doesn't match. Thus we obtain a contradiction.

Layer fisico

In base al numero di armoniche che possiamo trasmettere, un segnale può essere più o meno dettagliato, e inviare più o meno bit. In realtà quello che viene trasmesso, infatti, non è un segnale vero/falso, come ci si potrebbe immaginare, ma un segnale che "varia" un sinusoide in modo che abbia un segnale alto in corrispondenza degli uno e basso in corrispondenza degli zero.

Nyquist provò che per un segnale con larghezza di banda B, il segnale può essere ricostruito con esattamente 2B campioni.

Se il segnale ha V stati (es. 0/1 sono due stati), allora la larghezza di banda massima è data da:

bandwidth = 2B log2 V bits/sec

In pratica, però, un segnale non è mai esente da rumore. Il rumore è misurato in SNR (Signal/Noise Ratio).

La formula di Shannon, che tiene conto anche del rumore, è

bandwidth = B log2 (1+S/N)

La formula di Tannenbaum sulla larghezza di banda di una Station Wagon è:

Never underestimate the bandwidth of a station wagon full of tapes hurtling down the highway

Reti di calcolatori - il programma

Copincollo il programma di Reti di Calcolatori tenuto da Simonetta Balsamo nell'AA 2018-2019

Introduzione alle reti di calcolatori.

Principi, caratteristiche chiave, vantaggi e svantaggi. Scelte di progetto e problematiche connesse. Classificazione: topologie, tipi di rete. LAN, MAN, WAN. Protocolli e servizi. Prestazioni. Modello ISO/OSI. Protocolli TCP/IP. Internetworking. Problematiche comuni: tipi di connessione, routing, controllo del flusso e della congestione

Livello fisico e livello data-link.

Mezzi trasmissivi. Controllo dell’errore. Gestione del flusso. Protocolli a finestra scorrevole. Stop and wait. Protocolli go-back-n e ripetizione selettiva.

Livello MAC e livello rete.

Reti LAN. Ethernet, token ring. Reti ATM. Algoritmi di routing statici e dinamici. Controllo della congestione e del flusso. Protocollo IP.

Livello trasporto e livello applicazioni.

Protocolli, buffering, controllo del flusso e congestione. Multiplexing. Protocolli TCP e UDP. Esempi di applicazione.

Comunicazione e naming.

Comunicazione fra processi in sistemi distribuiti e reti di calcolatori. Risoluzione dei nomi e name service. Sicurezza delle reti di calcolatori. Casi di studio.

 

Pumping lemma

Il pumping lemma è una cavolata, espressa in una maniera complicata perché formale.

Ogni stringa sposta l'automa da uno stato all'altro, ma se ci sono più lettere che stati è ovvio che perché la stringa sia riconosciuta bisogna che ripeta più volte il passaggio attraverso alcuni stati.

Gli angolofoni lo chiamano principio dei buchi della piccionaia, noi principio dei cassetti, ma il senso non cambia.

In pratica quindi:

  1. for each i ≥ 0, xyiz ∈ A significa che A contiene xz, xyz, xyyz, xyyyz, xyyyyz...
  2. |y| > 0 significa, banalmente, che non posso scegliere ε come y, cioè devo avere un y che contiene qualcosa. Al limite, lo ripeto zero volte...
  3. |xy| ≤ p significa che o p si ferma a x oppure è lungo almeno xy

Da questi tre punti si evince anche che:

  1. x e z possono essere ε
  2. se p > 0, allora o x o y devono essere diversi da ε

La struttura a livelli ISO/OSI

 

Nome Tipo di informazione scambiata Descrizione breve brevissima Alcuni protocolli della suite TCP/IP
Application APDU Usa i dati all'interno di un'applicazione. HTTP, SMTP, DNS, RTP
Presentation PPDU Trasforma i dati trasmessi in "oggetti" logici.
Session SPDU Questo livello permette di gestire le sessioni tra utenti.
Transport TPDU Questo livello "compone il puzzle" dei pacchetti, garantendo che non manchi niente. TCP, UDP
Network packet Questo livello determina come un'informazione viene instradata da una sorgente a una destinazione. IP, ICMP
Data Link frame Questo livello garantisce che i pacchetti non contengano errori di trasmissione. DSL, SONET, 802.11, Ethernet
Physical bit In questo livello si scambiano bit: ciò che questo livello fa è garantire che un bit che vale 1 per il sender valga 1 anche per il receiver e viceversa. Inoltre determina quando una connessione inizia e finisce e quale sarà il bitrate.