Sei sulla pagina 1di 481

Dino Mandrioli, Paola Spoletini

Informatica teorica

Seconda edizione

CilWStudi
EDIZIONI
Indice

IX Prefazione all’edizione inglese


XV Prefazione all’edizione italiana
XVII Prefazione alla II edizione
XXI Notazioni e simboli

1 PARTE 1 : Prerequisiti matematici

3 Capitolo primo - Glossario matematico


3 1.1 Insiemi, relazioni, funzioni
7 1.2 Strutture algebriche di base
10 1.3 Permutazioni e combinazioni
10 1.4 Grafi ed alberi
13 1.5 Alfabeti, stringhe e linguaggi

17 Capitolo secondo - Elementi essenziali di logica matematica


17 2.1 Introduzione alle teorie formali
19 2.2 II calcolo proposizionale (CP )
2.2.1 La sintassi del Calcolo Proposizionale, p. 22 - 2.2.2 La semantica
del Calcolo Proposizionale, p. 23 - 2.2.3 Un sistema di assiomi e regole
di inferenza per il CP, p. 32
35 2.3 Calcolo dei predicati e teorie del primo ordine
2.3.1 Elementi di base della logica del prim’ordine, p. 36 - 2.3.2 Sintassi
della FOL, p. 38 - 2.3.3 Semantica della FOL, p. 45 - 2.3.4 Cenni all’as-
siomatizzazione delle teorie del prim’ordine, p. 53
60 2.4 Studio di un caso: una teoria formale per l’aritmetica e le sue fun­
zioni
2.4.1 Gli assiomi di Peano, p. 61-2.4.2 Funzioni ricorsive, p. 65-2.4.3*
Alcune proprietà fondamentali dell’assiomatizzazione dell’aritmetica,
p. 68
VI Indice

75 PARTE 2: Modelli per T informatica

77 Capitolo terzo - L’uso dei modelli in campo scientifico e ingegneristico


78 3.1 Esempi introduttivi
80 3.2 I modelli per l’informatica

87 Capitolo quarto - Automi


87 4.1 Automi a stati finiti
4.1.1 Automi a stati finiti come riconoscitori di linguaggi, p. 90 -
4.1.2 Gli automi a stati finiti come traduttori di linguaggi, p. 94 -
4.1.3 Proprietà degli automi a stati finiti, p. 100
113 4.2 Gli automi a pila
4.2.1 Automi a pila come riconoscitori di linguaggi, p. 117 - 4.2.2 Gli
automi a pila come traduttori di linguaggi, p. 122 - 4.2.3 Proprietà degli
automi a pila, p. 126
130 4.3 Le macchine di Turing
4.3.1 Le macchine di Turing come accettori di linguaggi, p. 141 -4.3.2 Le
macchine di Turing come traduttori di linguaggi, p. 145 - 4.3.3 Le mac­
chine di Turing come valutatori di funzioni, p. 149 - 4.3.4 Proprietà delle
macchine di Turing, p. 154
164 4.4 Automi non deterministici
4.4.1 Gli automi non deterministici a stati finiti, p. 165 - 4.4.2 Automi a
pila non deterministici e macchine di Turing non deterministiche, p. 170
176 4.5 Un modello evolutivo intrinsecamente non deterministico: le reti di
Petti
4.5.1 Le reti di Petti come riconoscitori di linguaggi, p. 184 - 4.5.2 Le
reti di Petti per descrivere sistemi, p. 192 - 4.5.3 Le reti di Petti temporiz-
zate, p. 197
201 4.6 Alcune annotazioni sul confronto fra modelli non deterministici e
quelli stocastici

206 Appendice quarta - A L’equivalenza fra le MT e le funzioni ricorsive


parziali

211 Capitolo quinto - Grammatiche


217 5.1 La classificazione delle grammatiche
220 5.2 Grammatiche e automi

239 Capitolo sesto - Definizioni denotazionali dei linguaggi


239 6.1 Le espressioni regolari
243 6.2 Equazioni negli spazi dei linguaggi
249 6.3 Confronto tra modelli denotazionali e modelli operazionali
Indice VII

253 Capitolo settimo - La logica matematica in informatica


254 7.1 L’uso della logica per definire linguaggi formali
258 7.2 L’uso della logica per esprimere proprietà dei programmi
263 7.3 L’uso della logica per specificare proprietà di sistemi
7.3.1 Un approccio sistematico per la rappresentazione di requisiti tem­
porali, p. 267 - 7.3.2 Caso di studio, p. 270

278 Appendice settima - A Dimostrazioni formali di correttezza


281 7.A. 1 Dimostrazione delle proprietà dei programmi
287 7.A.2 Dimostrazione di proprietà di sistemi

295 PARTE 3 : Proprietà del calcolo automatico

295 Capitolo ottavo - La risoluzione automatica dei problemi


296 8.1 Formalizzazione del concetto di problema
299 8.2 Macchine di Turing, linguaggi di programmazione e tesi di Church
(prima parte)
301 8.3 Algoritmi e tesi di Church (seconda parte)
307 8.4 Enumerazione delle macchine di Turing e macchine di Turing uni­
versali
8.4.1 Enumerazione delle macchine di Turing, p. 308 - 8.4.2 Macchine di
Turing universali, p. 313
317 8.5 Problemi (algoritmicamente) irrisolvibili
8.5.1 II problema della terminazione del calcolo, p. 318 - 8.5.2 II proble­
ma di riconoscere se una funzione è totale, p. 322
326 8.6 Problemi risolvibili, problemi risolti e problemi di cui non si cono­
sce la soluzione
330 8.7 Ulteriori concetti fondamentali di teoria della computabilità
8.7.1 Insiemi ricorsivi e ricorsivamente enumerabili, p. 331 - 8.7.2 I teo­
remi di Kleene e di Rice, p. 338
342 8.8 Riflessioni sulla irrisolvibilità dei problemi
348 8.9 Problemi risolvibili e irrisolvibili relativi ai linguaggi
352 8.10* La riducibilità dei problemi e i gradi di irrisolvibilità

363 Capitolo nono - La complessità del calcolo


365 9.1 Introduzione alla complessità computazionale
9.1.1 Come definire con precisione la complessità, p. 369 - 9.1.2 Com­
portamento asintotico, p. 370
374 9.2 Analisi di complessità per mezzo di automi
9.2.1 La complessità della risoluzione dei problemi mediante le macchine
Vili Indice

di Turing, p. 375 - 9.2.2 La complessità della risoluzione di problemi me­


diante altri automi, p. 384
9.2.2.1 La complessità delle computazioni degli automi a stati finiti, p. 384 -
9.2.2.2 La complessità delle computazioni degli automi a pila, p. 385 - 9.2.2.3 La
complessità delle MT a nastro singolo, p. 388
9.2.3 Alcune proprietà generali della complessità delle MT multinastro,
p. 396
406 9.3 Analisi della complessità di programmi per calcolatori reali
9.3.1 La macchina RAM, p. 407 - 9.3.2 Linguaggi di programmazione
di alto livello, p. 424
433 9.4 La valutazione della complessità all’atto pratico
9.4.1 Un esempio di ordinamento, p. 433 - 9.4.2 Un algoritmo sui grafi,
p. 437
441 9.5 Riassunto e confronto dei risultati ottenuti
454 9.6 Concetti avanzati relativi alla complessità: gerarchie, riducibilità e
completezza
462 9.7 Alcuni classici problemi NP-completi*
474 9.8 Limiti inferiori di complessità
479 9.9 Consigli e conclusioni

485 Bibliografia
497 Indice analitico
Capitolo 1

GLOSSARIO MATEMATICO

Questo capitolo ha lo scopo di fornire una breve panoramica delle definizioni e


della terminologia matematica di base. Poiché si suppone che il lettore abbia già
dimestichezza con la maggior parte delle nozioni qui esposte, esse verranno
corredate solo da brevi spiegazioni e da qualche esempio.

1.1 Insiemi, relazioni, funzioni


Un insieme è una collezione di oggetti. Ogni oggetto x in un insieme X è chiamato
elemento di X; l’appartenenza di x a X si indica con la notazione x e X, mentre la
relazione di non appartenenza si indica con x <£ X.
La scrittura X £ Y sta ad indicare che X è contenuto in Y, ovvero che ne è un
sottoinsieme', tutti gli elementi di X, quindi, sono anche elementi di Y. Y 2 X è una
notazione equivalente alla X 2 Y. X= Y indica che X è identico a Y, ovvero che
tutti gli elementi di X sono anche elementi di Y e viceversa, ovvero che X 2 Y e
Y 2 X. Gli operatori f, £, e, 2 sono rispettivamente l’opposto di =, 2, 2. X <= Y
indica un’inclusione propria, ossia il verificarsi della relazione X c Y ma non della
X= Y.Xz> Y si definisce analogamente.
Xu Y (X unione Y) indica l’insieme formato dagli elementi che appartengono
almeno ad uno fra X ed Y. Xn Y (X intersezione Y) indica l’insieme degli elementi
che appartengono sia ad X che ad Y. Vale la proprietà (Tu )j'uZ=f u (ZuZ),
quindi l’unione è associativa', inoltre X'J Y = Y o X, cioè l’unione è commutativa.
Le stesse proprietà valgono per l’intersezione. L’unione e l’intersezione sono
inoltre mutuamente distributive, in quanto valgono le uguaglianze Tu ( 7'0 Z)
= (XpY) v(f(YZ),eXv(YnZ) = (Iu Y) rXX'-.j Z).
Il simbolo 0 indica Vinsieme vuoto, ossia l’insieme senza elementi; per ogni
insieme X valgono le uguaglianze Tn 0 = 0 e Xu 0=X. X — Y (differenza di
insiemi) è l’insieme di tutti gli elementi di X che non appartengono a Y. X e Y
vengono detti disgiunti se e solo se Xr> Y= 0.
@(X) indica Vinsiemepotenza di X, ossia l’insieme di tutti i sottoinsiemi di X.
Siano b\, b2, ■ ■■,bkk oggetti: la scrittura (b\, b2, ..., bt) denota la k-upla ordinata
formata da b\, b2, ...,bk disposti esattamente nell’ordine indicato; si ha dunque che
b2, ..., bk) = (cb c2, ..., Ck) se e solo se bi = cb b2 = c2, ...,bk = ck. Una 2-upla è
detta anche coppia.
Il prodotto cartesiano Xi X X2 X ... X Xk indica l’insieme di tutte le k-uple
x2, ..., in cui xi eli,x2 e X2, ...,xke Xk. X1 indica il prodotto cartesiano di
X con se stesso (n — 1) volte. X1 può essere definito induttivamente anche in questo
modo: X1 = X, V111 = X1 X X.
4 Informatica teorica

Una relazione n-aria, o di arità n, R sugli insiemi _¥b ..., Xn è un sotto insieme di
Xi X ... XX„. La notazione 7?(xb ...,x„) è usata talvolta al posto della
(xb ...,Xi) e R. In questo caso si dice che R vale per la w-upla (xb ...,xf. Una
relazione 1-aria è detta anche proprietà. Sia P una proprietà definita su un dato
insieme X: la notazione {x | P(x)} indica l’insieme formato da tutti gli elementi
x e X tali che la proprietà P valga per x.
Una relazione 2-aria è detta anche relazione binaria. Se R è una relazione
binaria, la notazione infissa xRy può essere usata al posto della notazione prefìssa
R(x,y).
Data una relazione binaria R sugli insiemi X e Y, X è chiamato dominio di R e
Y è chiamato codominio di R. La relazione inversa R~' di R è l’insieme di tutte le
coppie ordinate (y, z) tali che (z, y) e R. Si consideri una relazione R £ X X X, per
la quale, cioè, X costituisca sia il dominio che il codominio; in questo caso si dice
che R è una relazione su X. R si dice riflessiva se xRx. R si dice simmetrica se xRy
implica yRx, mentre si dice transitiva se xRy e yRz implicano xRz. R si dice
irriflessiva se xRx non è mai valida, qualunque sia x, mentre si dice antisimmetrica
se xRy implica la negazione di yRx.
La chiusura transitiva e riflessiva R* di R, relazione binaria su X, è definita da
xR*y se e solo se esistono x0, xb ..., x„, con n > 0, tali che x0 = x, x„ =y e, per ogni
i, i = \,...,n, risulti X; flx.. Quindi, per ogni R vale la xR*x qualunque sia x. La
chiusura transitiva R+ e la A-esima potenza Rk di R vengono definite in modo
analogo a R*, con l’unica differenza che valgono rispettivamente le condizioni
n > 1 e n = k.
Una relazione binaria che sia riflessiva, simmetrica e transitiva è detta
relazione di equivalenza. Data una relazione di equivalenza R su un insieme X, e
preso un qualunque y, con v e L si definisce come l’insieme di tutte le z in X
tali che valga yRz. è detta classe di R-equivalenza di y. È facile verificare che
= lzfl se e s°l° se la relazione yRz è vera, e che, se [y]À [y]R, allora
[v]à n tzìx = 0, cioè che differenti classi di ^-equivalenza non hanno elementi in
comune. Quindi X è completamente suddiviso nelle sue classi di .^-equivalenza.
L’indice di R, indicato come indice(R), è il numero delle sue classi di equivalenza,
siano esse finite o infinite. Il simbolo R verrà omesso dalla notazione [x]ft quando il
contesto lo consentirà.
Gli ordinamenti riflessivi parziali su un insieme X sono relazioni riflessive,
antisimmetriche e transitive su X, mentre gli ordinamenti irriflessivi parziali sono
relazioni irriflessive, antisimmetriche e transitive sul. L’espressione “ordinamento
parziale” è usata sia per gli ordinamenti parziali riflessivi che per quelli irriflessivi.
Per ogni ordinamento parziale R, due elementi x, y e X, tali da non soddisfare le
relazioni xRy e yRx, vengono detti non comparabili o non confrontabili. Un
ordinamento (riflessivo) totale è un ordinamento (riflessivo) parziale che soddisfa,
per ogni x e y, o la relazione x = y, oppure una delle due relazioni: xRy, yRx.
Glossario matematico 5

Sia R un ordinamento parziale su X, e sia B un sottoinsieme di X. Un elemento


x e X è detto maggiorante di B se e solo se, per ogni y e B, vale yRx. Un
maggiorante x di B è detto minimo maggiorante, o estremo superiore, di B se e solo
se, per ogni maggiorante z di B, vale xRz. Analogamente, x è detto minorante di B
se e solo se, per ogni y e B, vale la xRy, mentre è detto massimo minorante, o
estremo inferiore, se e solo se, per ogni minorante di B, vale zRx. Un elemento y,
appartenente a B, è detto elemento minimo di B rispetto alla relazione R se e solo se
vale la relazione yRz per ogni elemento z di B diverso da y. Un buon ordinamento
(o una relazione di buon ordinamento) è un ordinamento totale R su X, tale per cui
ogni sottoinsieme non vuoto di X abbia un elemento 7?-minimo. Per esempio, la
relazione < sull’insieme dei numeri interi non negativi è un buon ordinamento; la
relazione < sull’insieme dei numeri razionali non negativi è un ordinamento totale
ma non un buon ordinamento, poiché l’insieme dei numeri di tipo Un non ha
elemento minimo. Una coppia (X, R), in cui R è un buon ordinamento su X, è detto
insieme ben fondato, o ben ordinato. Un insieme ordinato X si dice discreto se
soddisfa la seguente proprietà: per ogni coppia di elementi x, y e X, con x < y,
esiste solo un numero finito di elementi z e X tali che x < z < y. Gli insiemi dei
numeri naturali e dei numeri interi sono ovviamente discreti. L’insieme dei numeri
razionali invece non è discreto, in quanto per ogni coppia di numeri ne esiste
sempre uno intermedio e questa caratteristica porta a non rispettare la proprietà
richiesta dalla definizione di insieme discreto. Invece, un insieme tale per cui presi
due elementi ne esiste sempre uno intermedio prende il nome di insieme denso',
come appena visto i numeri razionali sono un insieme denso.
Una funzione/con dominio X e codominio Y (ossia che va da Xa Y), indicata
con fi Y —> X, è una relazione su X X Y tale che Vx e X, esista al più un y e Y per
cui valga la xfy. Un elemento y con queste caratteristiche viene indicato con/x). In
generale le funzioni considerate in questo testo sono parziali, ossia può verificarsi
la situazione in cui, per qualche x, non esista un j tale che j =/x). In questo caso si
dice che/x) è indefinita, e si scrive fix) = ±, dove il simbolo speciale ± significa
“indefinito”. Il dominio di definizione Df di una funzione/è l’insieme di tutte le x
per le quali fix) è definita (ossia fix) =y per qualche y e Y). L'immagine /di/è il
sottoinsieme di Y formato da tutti gli y tali che fix) = y per qualche x e X. Una
funzione / si dice totale se si verifica che Df = X; se / = Y, si dice allora che / è
suriettiva ofèsuY. Una funzione / è detta “uno-a-uno” se e solo se, per ogni x,
x e X, la disuguaglianza x / x implica fix) ffix'). Una funzione totale, uno-a-uno
daXa f, è detta biiezione, o corrispondenza biunivoca frale E Se/è uno-a-uno,
la sua relazione inversa/ 1 è anch’essa uno-a-uno da Y a X. Se/è una biiezione, lo
è anche / '. Si consideri una funzione fi X—> Y e sia W Y. L’immagine inversa di
Wper/ indicata come fiXW), è l’insieme di tutte le x e Xtali che/x) e W. Si noti
che non viene richiesto che / sia uno-a-uno. Se / è una funzione con dominio
Xì X X2 X ... X Xn si scrive/xb ..., x„) al posto di/(xb ..., x/).
6 Informatica teorica

Per ogni insieme X, la sua funzione identità Idx è definita come Idfx) = x per ogni
x g X. ,
Si considerino due funzioni: f.X^rY e g:L^Z. La loro composizione
g°fiX Z è definita dall’uguaglianza g °fix) = g(flx))1. Per ogni coppia di
funzioni/ g, l’espressione f = g indica che f e g sono identiche (ossia che sono la
medesima funzione), ossia che Df= Dg e fix) = g(x) per ogni x g Df. Si noti che
fix) = ± implica che g(x) = ± e viceversa. Se f è una biiezione tra X e Y, allora
jT1 o f= idx ef ° = Idy. Se/è una biiezione frale K egè unabiiezione tra Ye
Z, allora g° fè una biiezione fra X e Z.
Si definisce operazione «-aria (o operazione con n argomenti) su un insieme X
una funzione con dominio X1 e codominio X. Per convenzione, un’operazione
0-aria è un elemento fissato (o costante) di X.
Due insiemi X e Y si dicono equinumerosi (e si scrive X ~ Y), o aventi la
stessa cardinalità, se e solo se esiste una biiezione fra.Ye Y. Ovviamente si ha che
X™ X; X ™ Yimplica che Y » X, mentre f e f ~ Z implicano che X » Z. Se
X è equinumeroso ad un sottoinsieme di Y, ma Y non è equinumeroso ad alcun
sottoinsieme di X, si dice che la cardinalità di X è più piccola della cardinalità di Y.
Si dice che un insieme X è finito se e solo se è vuoto, o esiste un intero positivo
n tale che Xè equinumeroso all’insieme dei numeri positivi minori o uguali ad n. In
tal caso si dice che U ha cardinalità n. Un insieme che non sia finito è detto infinito.
Un insieme X è detto numerabile se e solo se è equinumeroso all’insieme M
dei numeri naturali, ossia all’insieme degli interi non negativi. Si dice che un
insieme numerabile ha cardinalità Ko- Ogni insieme equinumeroso all’insieme di
tutti i sottoinsiemi di un insieme numerabile, ha cardinalità 2*“ (detta anche
cardinalità del continuo). L’insieme potenza p(M) dell’insieme dei numeri naturali
N, e l’insieme di tutte le funzioni che vanno da N a se stesso, hanno cardinalità
2N° . Un insieme è detto contabile, o al più numerabile, se e solo se è finito o
numerabile. Ovviamente, ogni sottoinsieme di un insieme numerabile è contabile.
La scrittura indica la cardinalità dell’insieme X. Siano A e B due insiemi finiti;
la cardinalità dell’insieme di tutte le funzioni totali aventi come dominio A e come
codominio B è |5||X|.
Una sequenza numerabile è una funzione totale j avente per dominio l’insieme
dei numeri interi positivi (o non negativi); solitamente si scrive sn al posto di sin) e
l’argomento n viene chiamato indice di sn. Una sequenza finita è una funzione il cui
dominio è {1, 2, ...,n}, dove n è un intero positivo. Le sequenze finite e infinite
vengono indicate rispettivamente con {.s'i, s2, ■■■, sn} e {ó-i, .ss, Una
sottosequenza ss di una sequenza numerabile .$• = {.s'i, s2, ...} è una sequenza
{.y.s'i, ss2, ...} tale per cui, per ogni j, con j > 1, si verifichi che ss, = s, per qualche

1 Si assume che, qualora f sia parziale, dove/è indefinita, g(±) = ±.


Glossario matematico 7

z > 1, e, inoltre, le due relazioni ssj = sh ss,, = sf, con j > j , implichino che z > ì'.
Una sottosequenza di s = {sb sz, ...} è solitamente indicata con {s. , , ...}.
È importante notare che 2*“ è una cardinalità maggiore della No. Infatti 2N“ è
chiaramente non minore di Xo (esiste un’ovvia biiezione fra i numeri naturali e un
sottoinsieme di p(N)). Si supponga ora per assurdo che essi abbiano la stessa
cardinalità. Ciò implicherebbe l’esistenza di una biiezione
s: N-> p(N) = {s0, sb ...}. Si consideri adesso l’insieme DEH, costituito
esattamente da quei numeri n che non appartengono all’insieme sn 6 p(IN).
Formalmente ciò si esprime con la scrittura D = {n | n £ s„}. Servendosi della
Figura 1.1, si cerca ora di definire D; infatti si osserva che 0 g D se e solo se
0 & s0, mentre 1 e D se e solo se 1 ob ... D è un sottoinsieme di N, quindi
D g p(N). Deve dunque esistere un indice z per cui D = Sj (la successione s
enumera tutti i sottoinsiemi di N). Si considerino ora i due casi, distinti dalla
validità o meno della relazione z g st. Nel caso affermativo, poiché D = s{, i non
deve appartenere a in base alla definizione di D, il che è un assurdo.
Analogamente, nel caso negativo, z £ s, implica per definizione che z sia in D, cioè
in s,: di nuovo un assurdo. Quindi N e p(N) non sono equinumerosi.
Il ragionamento sopra esposto è detto diagonalizzazione (vedi Figura 1.1) ed è
dovuto a Cantor. Come si avrà modo di verificare in seguito, questa è una delle
tecniche fondamentali di dimostrazione impiegate nella teoria della computazione.

1.2 Strutture algebriche di base


Un semigruppo è una coppia (S,°) dove S è un insieme e ° è \m‘ operazione
associativa su S, ossia una funzione totale S X S—> S tale che Al:
VxVyVzv(o(°(x, y), z) = °(x, °(y, z))).

Figura 1.1 Esempio di diagonalizzazione.


8 Informatica teorica

Generalmente l’operatore ° viene impiegato in notazione infissa, scrivendo quindi


x ° y al posto di °(x, y).
Un monoide è un semigruppo per il quale esiste un elemento u e S che sia
un’ unità destra e sinistra rispetto a ossia, formalmente,
A2: Vx (x ° u = u ° x = x). Un monoide ha un’unica unità m; si supponga infatti che
esistano due unità uj, u2. in questo caso si otterrebbe zq ° m2 = u2 ° u\ = m2, poiché
zq è un’unità, e m2 ° zq = zq ° m2 = zq, poiché anche m2 è un’unità. Si conclude quindi
che necessariamente deve risultare u2 = us.
Un gruppo è un monoide in cui, per ciascun x e S, esiste un elemento inverso,
indicato con x1, tale che A3: Vx (x ° x-1 = x”1 ° x = zz). Per ciascun x, x-1 è unico.
Infatti, si supponga che esistano x, xi-1, x2-1 tali che x ° x^1 = xi-1 ° x = u,
x o x2-1 = x2-1 o x = m: si otterrebbe allora xi-1 = Xi-1 ° u = xfi1 ° (x ° xfi1) = (xi-1 ° x)
° x2-1 = u ° x2-1 = x2\ Si noti che uA = w. infatti z/-1 = u~x ° u = u.
Sia (fi, un semigruppo. Per ogni X £ S, >C indica il sottoinsieme di S
generato da X, ossia l’insieme di tutti gli elementi s & S tali che s = x, r- x2 xy ...
x„ per qualche n> 1, x,el. Si noti che le parentesi non sono necessarie nella
precedente notazione, in virtù della associatività di °. Quindi Xj °x2° x3 può essere
letto in modo equivalente sia come (xj ° x2) ° x3 che come xi0 (x2 ° x3). X" è un
sottosemigruppo di S, ossia è un semigruppo rispetto a °. Infatti, se xb x2 g Xfi
allora ovviamente si avrà anche xi ° x2 e U.
Se (5, °, u) è un monoide, allora X* = X" u {«} è un sottomonoide di S ed è
chiamato sottomonoide generato da X. Se X^ = S, X è chiamato insieme dei
generatori del semigruppo S. Analogamente, X è chiamato insieme dei generatori
del monoide S se X* = S.
Per un dato insieme X e un simbolo di operazione °, l’insieme Xd di tutti gli
elementi s = xj0 x2 ° ... ° x„ con n > 1 è chiamato semigruppo libero generato da X
e da o. Infatti, se Si =xi0 x2 ° ... ° xk, s2=yx° y2° ... ° yr, allora
0 s2 = Xi0 ... 0 xk ° yl ° ... o yr. Se si aggiunge, per convenzione, un elemento
unità u a X" tale che x ° u = u ° x = x per ogni x g X, si ottiene il monoide libero
X* = X" u {«} generato daXe °.
Si consideri un semigruppo (fi, °). Una congruenza su S è una relazione di
equivalenza C su S tale da mantenere l’operazione, ossia tale per cui xCy implichi
chex ° zCy ° zez° xCz ° y, per ogniz appartenete a S.
Se C è una congruenza su un semigruppo S, l’insieme delle classi di
C-equivalenza di S, chiamato S modulo Co S quoziente C e indicato S mod C o
SIC, diventa un semigruppo se si definisce [y] ° [z] = [y ° z] per ogni y, x g S. La
definizione precedente è ben posta, poiché [yj = [y2] se e solo se y\Cy2. In tal caso
si ottiene [yj ° [z] = [y2] ° [z] = [yi ° z] = [y2 0 z], poiché yi ° zCy2 ° z. In questo
modo la definizione non dipende dalla scelta dell’elemento y (e z) all’interno della
classe [y] (e [z]). Inoltre valgono le relazioni ([x] ° [y]) ° [z] = [x ° y] ° [z] =
Glossario matematico 9

[x ° y o z] = [x ° (y ° z)] = [x] ° [y ° z] = [x] ° ([y] ° [z]). Da ciò segue, quindi, che


SIC è un semigruppo rispetto all’operazione ° definita sulle classi di C-equivalenza.
Se S è un monoide, allora anche SIC lo è, e la sua unità è [«]. Infatti, per ogni
x, [x] o [«] = [x o u\ = [x] = [m o x] = [m] o [x]. Se 5 è un gruppo, allora anche SIC lo
è. Infatti, per ogni x, [x]-1 = [x 1 ], poiché [m] = [x ° x ’] = [x] ° [x-1] e [m] = [x 1 ° x]
= [x-1] o [x]. In tali casi, SIC viene chiamato, rispettivamente, monoide quoziente e
gruppo quoziente.
Si considerino, ad esempio, gli insiemi N e 2 dei numeri naturali e dei numeri
interi: (M,+) è un semigruppo e (N, +, 0) è un monoide. Tuttavia, N non è un
gruppo rispetto all’operazione +. Invece 2 è un gruppo rispetto all’operazione +,
all’unità 0 e all’operazione inversa - (per ogni x, x-1 è indicato con -x). Gli
elementi Gel formano un insieme di generatori per N.
Si definisca la relazione =k su N mediante la x =ky se e solo se il resto della
divisione intera di x per k eguaglia il resto della divisione intera di y per k, ossia se
esistono <7b d2, e r tali che x = d\ k + r, y = d2 ■ k + r e 0 < r < k. In tal caso
[x] = [y] = [r]. =k è una congruenza rispetto a +, ossia x =ky implica x + z =k
y + z. Infatti, sia z = d3 k + s, 0 < s <k. Allora x + z = d-., k + r + d3
k + s = (d^ + d3) ■ k + s + r, y + z = d2 k + r + d3 k + s = (d2 + d3) ■ k + r + s. Sia
s + r = d ■ k+ c, 0 <c <k. Allora x + z = (dx + d3 + d) ■ k + c, y + z = (d2 + d3 + d)
■ k + c, ossia [x + z] = [y + z] = [c].
2/=t è chiamata classe dei resti modulo k. Essa è anche un gruppo definendo
—[x] = [—x]. Infatti, per ogni x, [x] + (—[x]) = [x] + [—x] = [x -x] = [0],
Siano X e Y due semigruppi, monoidi o gruppi. Una funzione h: Y è un
omomorfismo (fra semigruppi, monoidi o gruppi, rispettivamente) se e solo se, per
ogni xb x2 e X, h(xÌL ° x2) = /z(xi) ° /z(x2). Se X e Y sono monoidi e u, u indicano le
rispettive unità, allora h(u) = u ' per ogni omomorfismo da X a Y. Infatti, per ogni x,
h(x) = h(x o a) = h(x) o h(u), h(x) = h(u ° x) = h(u) ° h(x). Si verifica, quindi, che
h(u) è un’unità destra e sinistra per Y. Inoltre, se X e Y sono gruppi, allora, per ogni
x, h(x~l) = h(x)~l. Infatti, u ' = h(u) = h(x ° x"1) = h(x) ° /?(x') = h(x~l ° x) =
à(x ’) o h(x), ossia Mx') è l’inverso di h(x).
Se h:X—>Y è una biiezione ed un omomorfismo, allora viene chiamata
isomorfismo fra X e Y. Se esiste un isomorfismo fra due semigruppi, monoidi o
gruppi, rispettivamente, essi vengono definiti isomorfi.
Un insieme parzialmente ordinato è una coppia (S, <), dove S è un insieme e <
è un ordinamento riflessivo parziale su S. Un reticolo è un insieme parzialmente
ordinato in cui, per ogni x, y, con x, y e S, esistono un massimo minorante e un
minimo maggiorante di x e di y in S, indicati rispettivamente da x n y, x u y.
Un’algebra Booleana è una 6-upla (S, n, li, 0, 1, —dove, considerando la
relazione < come un’abbreviazione di x u y = x (infatti, x n y = x se e solo se
x <y), >A, <) è un reticolo, 0 è l’elemento neutro rispetto a u, 1 è l’elemento neutro
10 Informatica teorica

rispetto a n, e S —> S, detta operazione complemento, è tale che, per ogni x,


x li (—ix) = 1, x n (—x) = 0.

1.3 Permutazioni e combinazioni


Una permutazione p su n elementi è una biiezione dall’insieme {1, 2,..., n} su se
stesso. Generalmente una permutazione p viene indicata con la notazione:

' 1,2,..., n >


P =

che significa p(l) = h, •••> Ptp) = in- L’insieme P„ di tutte le permutazioni su n


elementi è chiaramente un gruppo rispetto alla composizione funzionale. Una
permutazione ciclica su n elementi è una permutazione p per la quale esiste un k,
0 < k < n - 1, tale che pii) =n i + k, per 1 < i < n.
La nozione di permutazione su n elementi è immediatamente estendibile ad un
qualunque insieme di n elementi ax...a„, stabilendo una biiezione naturale fra
{ai, ..., a„} e {1, ..., n}.
Il numero di permutazioni differenti su n elementi è dato da n! (n fattoriale),

dove n\ = if n = 0 then 1 else i.


1=1

Si consideri un insieme S. Un r-multiinsieme di S è una collezione non ordinata


di r elementi di S, con possibili ripetizioni. Quindi, se S è {a, b, c, d, e}, un
5-multiinsieme di S può essere, ad esempio, {a, a, b, a, e}. Una r-combinazione, o
combinazione di classe r, di S è un r-multiinsieme di S in cui ciascun elemento di S
si presenta al più una volta. Una 3-combinazione del precedente S è {a, c, d}. Il
numero C(n, r) di r-combinazioni di un insieme di n elementi è quindi il numero di
modi diversi in cui si possono scegliere r elementi da un dato insieme di n

elementi. Si ha inoltre C(n, r) = dove indica il coefficiente binomiale

dell’elemento f nell’espansione polinomiale di (1 + t)n = a0 + a 4 + arf + ... + anf,


n n n\
ossia = ar. Risulta anche:

1.4 Grafi ed alberi


Un grafo G è una coppia ( V, É) dove V è un insieme (generalmente finito) di vertici
(o nodi) ed E è un insieme di lati (o archi), in cui ciascun lato è definito come una
coppia di vertici. Se i lati sono coppie ordinate, G è detto orientato (viene anche
chiamato digrafo). Se invece i lati sono coppie non ordinate (ossia se (vb vf
rappresenta lo stesso oggetto rappresentato da (v2> u)), allora G è detto non
orientato. Nel seguito ci riferiremo sempre a grafi orientati, a meno che non venga
Glossario matematico 11

detto esplicitamente il contrario. In ogni lato orientato (v;, v7), v, viene chiamato
sorgente del lato, mentre v7 viene chiamato pozzo. Un grafo S-etichettato, sia
orientato che non orientato, è un grafo i cui lati vengono associati ad etichette
appartenenti ad un dato insieme S; quindi un lato è una coppia ((v,-, v7), /) quando
(v;, v7) è una coppia di vertici e l e S.
I grafi devono il loro nome alla rappresentazione grafica normalmente usata
per raffigurarli. In base ad essa, il grafo G = ({A, B, C, D, E}, {(A,B),
(B, A),(A, C), (C, E), (E, D), (D, A), (D, B)}) viene indicato come risulta in Figura
1.2. Se G fosse stato un grafo non orientato, avrebbe avuto, invece, la
rappresentazione di Figura 1.3, in cui, come si può notare, le frecce della Figura 1.2
sono state sostituite da lati non orientati e i due lati (A, B/ e (B, A) sono stati fusi in
un unico lato. Talvolta due nodi possono risultare connessi da più archi, nel qual
caso si parla di multigrafì. I multigrafi possono essere definiti formalmente per
mezzo di multiinsiemi dell’insieme degli archi.
Un cammino di lunghezza n, in un grafo, è una sequenza di lati del tipo
{(v0, uX (vi, vf, ..., v„)}. Due vertici v e v' si dicono connessi, o, in modo
equivalente, si dice che v' è raggiungibile da v, se e solo se esiste un cammino tale
che v = vq, v„ = v'. Per convenzione, un vertice v è sempre connesso a se stesso da
un cammino di lunghezza 0. Un ciclo (o cammino ciclico) è un cammino nel quale
v„ = v0- Due nodi di un grafo vengono detti adiacenti se e solo se risultano connessi
da un cammino di lunghezza 1, ossia se costituiscono la sorgente e il pozzo di un
medesimo lato. Un grafo orientato aciclico (DAG) viene definito come un grafo
orientato che non contiene cicli.

Figura 1.2 Esempio di grafo orientato.


12 Informatica teorica

Figura 1.3 Esempio di grafo non orientato.

Un albero è un DAG in cui:


1. Esiste un unico nodo n0, detto radice, privo di archi entranti, per il quale cioè
non è definito nessun arco del tipo (n, n0).
2. Ciascun nodo n f n0 è il pozzo di uno ed un solo lato.
3. Ciascun nodo è raggiungibile dalla radice.

In base alla definizione precedente si constata facilmente che esiste al più un


cammino congiungente due nodi neri qualsiasi. Inoltre (se l’albero è finito),
esistono dei nodi terminali privi di archi uscenti, ossia, per ogni nodo n di questo
tipo, non è definito un lato del tipo (n ,ri). Tali nodi terminali vengono anche
chiamati foglie dell’albero. Se il lato (n, ri) appartiene ad un albero, si dice
convenzionalmente che n è il padre di ri e, reciprocamente, che ri è il figlio di n.
Se un nodo ri è raggiungibile dal nodo n, n viene detto antenato di ri, mentre ri
viene detto discendente di n. Un nodo n, insieme a tutti i suoi discendenti ed ai lati
che li congiungono in un albero T, è detto sottoalbero di T con radice n.
La Figura 1.4 mostra un esempio di albero. Per semplicità, i lati vengono
implicitamente supposti diretti verso il basso. Un albero binario è un albero in cui
ciascun nodo o è una foglia o ha esattamente due figli.

Figura 1.4 Esempio di albero.


Glossario matematico 13

1.5 Alfabeti, stringhe e linguaggi


Un alfabeto (o vocabolario) V è un insieme finito di oggetti elementari chiamati
simboli (o caratteri). Esempi di alfabeti sono gli alfabeti di lettere {a, b, c,...,z},
l’alfabeto delle cifre decimali {0, 1, 9}, l’alfabeto delle cifre binarie {0, 1}, o
l’alfabeto del linguaggio Pascal. Una stringa di alfabeto V è una sequenza finita su
V. Una stringa .v = {sb s2, ..., s„} è solitamente indicata mediante la notazione più
semplice sb s2,..., s„. Esempi di stringhe dell’alfabeto {a ,b, z} sono abzc,
baaa, ceabaazzc, amore. Per convenzione, il simbolo e indica la stringa vuota
(ossia la stringa formata da nessun simbolo). Data una stringa x, la notazione |x|
indica la lunghezza di x (ossia la cardinalità del suo dominio). Ad esempio \aba\ =
3, |a| = 1, |s| = 0, ecc. Se x = s\s2...sn è una stringa, si dice che i simboli sh
i= 1, ...,n occorrono in x nella posizione i. Quindi se x = abaac, il simbolo a
occorre nella posizione 1, 3 e 4, e la terza occorrenza di a in x è nella posizione 4.
La concatenazione x -y di due stringhe x e y è una stringa composta da tutti i
caratteri di x seguiti dai caratteri di y. Quindi se x = gio, y = rgio, x-y è giorgio.
Formalmente, la concatenazione è un’operazione sull’insieme delle stringhe
definita nel seguente modo: sia x = axa2...an, y = bib2...bm con n, m> 1, allora
x • y = C]C2.. .cn+m con c, = a, per 1 < i < n, c, = per n + 1 < i < n + m. Per
convenzione, per ogni x, x • e = e • x = x, il che significa che e è un unità destra e
sinistra rispetto a •. Quando non può insorgere alcun dubbio, si può scrivere xy al
posto di x ■ y. Si noti che l’operazione di concatenazione è associativa. Si conclude
quindi che, per ogni alfabeto P, l’insieme di tutte le stringhe di V è esattamente il
monoide libero P*.
Una stringa x è detta prefisso di y se esiste una stringa z per la quale risulti
y = xz. Se z s, x viene detto prefisso proprio di y. Analogamente, x è un suffisso
(proprio) di y se esiste una z per la quale y = zx (con z e).
Data una qualunque stringa x, l’espressione x' indica la concatenazione di x
con se stessa i — 1 volte, ossia x° = e (per convenzione) e x!+1 = x! • x.
Un linguaggio £ su di un alfabeto V è un sottoinsieme di V*, ossia un insieme
di stringhe su V. Per esempio, sull’alfabeto V = {a, b, c}, si possono definire, fra gli
altri, i seguenti linguaggi:
£i = 0 (il linguaggio vuoto; ossia il sottoinsieme vuoto di P*),
L2 = {e} (il linguaggio che contiene solo la stringa vuota; si noti che £i £2),
£3 = {a, b, c} — V (il linguaggio i cui elementi sono soltanto le stringhe di
lunghezza 1),
£4 = {aa, ba, ab},
L$ = {a, aaa, aaaaa, bc},
L6 = {ab, aab, aaab, aaaab, ...} (il linguaggio infinito le cui stringhe sono
formate da un numero qualsiasi di a seguite da una sola è; £6 può essere
anche definito in maniera più compatta in questo modo: £6 = {anb\ n > 1},
£7 = {{ab)ncm | n > 1, m > 2},
£s = {anbn | n > 1} = {ab, aabb, aaabbb, ...}.
14 Informatica teorica

Poiché i linguaggi sono insiemi di stringhe, le solite operazioni u, n, - vengono


implicitamente definite; in particolare il complemento L di L indica V* - L. Ad
esempio, L-, OiLs = 0, Lpj L2'J Ls= {anbn | n > 0}. Oltre alle operazioni
Booleane u, n, -, si definiscono anche le seguenti operazioni sui linguaggi.
La concatenazione L ■ M (LM quando non possono insorgere equivoci) di due
linguaggi L e Mè definita come L ■ M= {x • y | x e L, y e M}, ossia ogni stringa
di L • M è ottenuta dalla concatenazione di una stringa di L con una stringa di M.
Per esempio L3 • L4 = {aaa, aba, aab, baa, bba, bab, caa, cba, cab}. Per ogni
linguaggio L, Ll è definito in modo analogo a quello usato per le stringhe, ossia
L° = {e} (si noti che 0° = {e}), L'+i = L ■ LÌ.
La chiusura o * (operazione stella di Kleene) è definita su ogni linguaggio L (
nel modo seguente:

L* = |JL'L' = L° uLwL • Lu ...


i-0 '
L+ indicaL* — {s} = L wL • L u ...
Per esempio 0* = {e}, L3* = V*, il linguaggio universale, \

Li={an'bn'a^b^...antbni | k > 1, n, > 1 per 1 < i < k}.

ESERCIZI
1.1 Si enumerino le prime dieci stringhe xb...,Xi0 di L7 in ordine crescente di
lunghezza, cioè in modo tale che 1 < i <j < 10 implichi |x;| < |x,|.
1.2 È vera l’uguaglianza L6* = V*?
1.3 Per ogni linguaggio L, è vero che (L*)* = L*?
1.4 Si dimostri che L7 = L9+ • L10 • Lio+, dove L9 = {ab}+, Lw = {c}. In forma
breve, per evitare parentesi inutili, L7 = (abf • c ■ c+.
1.5 Si dimostri che, per ogni alfabeto V, l’insieme dei linguaggi su V, ossia ।
p(F*), è un algebra Booleana se si pone < = £, u = u, n = n, —। = .
1.6 Si dimostri che la concatenazione è distributiva rispetto all’unione e
all’intersezione, ossia che, per ogni Lb L2,L3,(Li u L2)
• L3 — Li • L3 vj L2 • L3 e (Li o L2) * L3 — Li * L3 o L2 * L3.

RIASSUNTO DEL CAPITOLO


In questo capitolo sono state illustrate nozioni fondamentali, indispensabili per la
comprensione del resto del testo. Il capitolo contiene la terminologia matematica di
base. Sono state richiamate le nozioni di insieme, relazione, finizione, gruppo,
reticolo, stringa e linguaggio. Non si è inteso comunque fornire una trattazione
completa di tale campo.
Glossario matematico 15

Note bibliografiche
I concetti matematici richiamati in questo capitolo, con la eventuale eccezione della
sezione 1.5, sono trattati approfonditamente in ogni testo introduttivo sull’algebra.
Si menziona, fra tutti, il classico macLane Birkhoff (1970). La terminologia di base
della teoria dei linguaggi può essere reperita in ogni testo sui linguaggi formali,
come Hopcroft e Ullman (1969, 1979), Hopcroft, Motwani e Ullman (2006) e
Harrison (1978).
Capitolo 2
ELEMENTI ESSENZIALI DI LOGICA MATEMATICA

Questo capitolo introduce alcuni elementi di base della logica matematica,


indispensabili per la comprensione di alcune parti successive del testo, in particolar
modo del Capitolo 7.

2.1 Introduzione alle teorie formali


Una teoria formale è un modo di effettuare asserzioni impiegando il formalismo
matematico, allo scopo di rendere le deduzioni simili a un processo di derivazione
di una formula da un’altra e di dedurre la verità di certi fatti come formule derivate
dalla verità di altri fatti. Per esempio, il semplicissimo ragionamento “Se Giovanni
ha fame, allora Giovanni prima o poi mangerà qualcosa; Giovanni ha fame;
Giovanni prima o poi mangerà qualcosa.” può essere rappresentato dalla sequenza
di formule, che non necessitano di spiegazione: “Giovanni-ha-fame => Giovanni-
prima-o-poi-mangerà; Giovanni-ha-fame; Giovanni-prima-o-poi-mangerà”, dove il
simbolo => indica implicazione logica, rappresentata nella frase italiana dalla
parola “allora”.
L’uso di una notazione formale per esprimere il ragionamento umano può
avere molti vantaggi rispetto alle descrizioni informali impiegate nel linguaggio
naturale. Per esempio, la frase “la funzione f non è definita per ogni x” è ambigua:
essa può significare sia che f non è mai definita, sia che esiste qualche valore di x
per cui f non è definita. Le descrizioni formali, invece, possono essere rese non
ambigue, coerenti (ossia non contradditorie) e complete; oltre a ciò, esse sono
spesso più chiare e più coincise. Tutte queste affermazioni verranno comunque
verificate nel corso del testo.
Introduciamo ora i seguenti concetti basilari: teoria formale, dimostrazione (o
deduzione) e teorema. Nella sezione successiva (Sezione 2.2) è esposto un primo,
semplice ed importante esempio di teoria formale, e precisamente il calcolo
proposizionale: come suggerisce il termine, esso studia la verità di frasi complesse
costituite dalla composizione di frasi elementari assunte come “atomicamente vere
o false” (in un certo senso, ogni singola frase elementare viene trattata come un
“bit”);. Nella Sezione 2.3 l’attenzione si sofferma sul calcolo dei predicati e sulle
teorie del prim’ordine, che invece “entrano nel merito” della verità di singole
affermazioni elementari. Infine, nella Sezione 2.4, viene esposta brevemente una
teoria del prim’ordine che formalizza i concetti familiari dell’aritmetica e ne
descrive le proprietà essenziali.
Lo scopo di questo capitolo non è illustrare in modo completo la logica
matematica, che costituisce una branca molto ampia ed importante della
18 Informatica teorica

matematica, ma solo dare una visione introduttiva che fornisca al lettore quella
minima conoscenza di base indispensabile per comprendere il resto del libro.

Definizione 2.1
Una teoria formale ,‘JT è una quadrupla (.Sf TTTfsf \ dove

- .SU è un insieme contabile di simboli. Una sequenza finita di simboli è detta


espressione di .SU
- è un sottoinsieme delle espressioni di chiamato insieme delle formule
ben formate (fbf).
- ./' èun sottoinsieme di UUchiamato insieme degli assiomi di UT
- SU è un insieme finito {Ri, ..., R„} di relazioni fra formule ben formate, dette
regole di inferenza. Per ogni Rh se RflV], ..., JVk, W) vale per qualche JVi, ...,
Wk, W allora W è una diretta conseguenza dell’insieme {Wj, ..., JVk} in virtù
di Ri.

Una formula ben formata fV è una conseguenza in 7T di un insieme T di formule
ben formate se e solo se esiste una sequenza 1V„} di formule ben formate
tali che fV= e, per ogni i, "ÌVJ sia un assioma, oppure sia in T, oppure
sia una diretta conseguenza, in virtù di qualche regola di inferenza 7?y, di qualche
sottoinsieme di {1%, ..., Una sequenza siffatta viene chiamata
dimostrazione (o deduzione} di fV in T. I membri di T prendono il nome di ipotesi
(o premesse) della dimostrazione. La notazione T viene impiegata per
indicare il fatto che 3V è una conseguenza di T in JT II pedice viene omesso
quando non vi è rischio di equivoci. Se T è un insieme finito {W], ..., W„}, si scrive
convenzionalmente 1%, ..., 'Wn>-f¥ al posto di {1%, ...,3V„} •-3V. Se f è
l’insieme vuoto, in genere si scrive semplicemente '-fV. In tal caso, fV viene
chiamato teorema di .V7
Intuitivamente, le formule ben formate sono sequenze di simboli dotate di
significato. Gli assiomi sono formule che descrivono i fatti che vengono assunti
veri a priori. Le regole di inferenza sono meccanismi elementari che sostengono le
deduzioni. I teoremi sono formule che descrivono verità che possono essere dedotte
da altre affermazioni, siano esse vere a priori o precedentemente dimostrate.
In tutte le teorie formali considerate in questo testo si possono ricavare
facilmente degli algoritmi per:
1. stabilire se una data espressione sia o meno una fbf,
2. stabilire se una fbf sia o meno un assioma,
3. stabilire se, per una data regola di inferenza (k + l)-aria R, per un dato insieme
di fbf {3V/, ..., 3Vi} e per una data fbf fV, la relazione ..., fV)
sussista o meno.
Elementi essenziali di logica matematica 19

Con ciò non si vuole tuttavia affermare a priori la possibilità di trovare un


algoritmo per stabilire se una fbf 3V sia o meno un teorema di -U come verrà
ampliamente illustrato nei capitoli seguenti.

2.2 II calcolo proposizionale (


Il calcolo proposizionale ( céCP), chiamato anche logica proposizionale, è l’esempio
più semplice di teoria formale della logica matematica e si basa sul concetto di
proposizione, una frase che può assumere solamente il valore vero o il valore falso;
esso permette di ragionare su proposizioni complesse, ottenute componendo
proposizioni più semplici, sfruttando la loro struttura e i possibili valori di verità
che le proposizioni semplici possono assumere. Un esempio di tale calcolo è il
ragionamento presentato precedentemente nella Sezione 2.1. Un ulteriore esempio
è il seguente: “Maria può essere o viva o morta. Maria non è morta. Quindi Maria è
viva”.
In generale, ogni linguaggio consiste di una sintassi e di una semantica. La
sintassi è l’insieme di regole attraverso cui è possible costruire le frasi da cui il
linguaggio è composto: ad esempio, “una frase è composta da un soggetto seguito
da un predicato”, o “un assegnamento (in un linguaggio di programmazione)
consiste di un nome di varibile seguito dal simbolo ‘=’ e da un’espressione”. La
semantica spiega invece il significato delle frasi del linguaggio: ad esempio, “un
assegnamento è valutato calcolando il valore dell’espressione su lato destro del
simbolo ‘=’ e memorizzando il risultato nella variabile sul lato sinistro del simbolo
4_999
Il può essere descritto come una teoria formale in un modo molto
semplice: si inizierà con la definizione deH’insieme dei simboli della teoria e delle
sue formule ben formate, cioè se ne definirà la sintassi; successivamente, dopo aver
introdotto la semantica, verranno definiti gli assiomi e le regole di inferenza.

2.2.1 La sintassi del Calcolo Proposizionale


Gli elementi di base dei linguaggi naturali sono le proposizioni. Una proposizione è
una frase che può essere vera o falsa. Ulteriori esempi, oltre a quelli citati
nell’introduzione a questa sezione, sono le frasi “Tommaso è più alto di Giovanni”
o “Lucia ha i capelli lunghi e Linda ha gli occhi azzurri”.
Una proposizione può essere atomica, quando non può essere scomposta in
parti che sono a loro volta proposizioni, o composta, quando due o più proposizioni
sono legate tra loro attraverso l’uso di connettivi. I tipici connettivi sono “non”,
“e”, “o”, “se... allora” e “se e solo se”. Quindi “Tommaso è più alto di Giovanni”
è una proposizione atomica, mentre “Lucia ha i capelli lunghi e Linda ha gli occhi
azzurri” è una proposizione composta in cui le due proposizioni atomiche “Lucia
ha i capelli lunghi” e “Linda ha gli occhi azzurri” sono legate attraverso l’uso del
connettivo “e”.
20 Informatica teorica

Tabella 2.1 Rappresentazione simbolica dei connettivi

Connettivi Simbolo logico Simboli alternativi


non —। ~ !
? ‘
e A &, &&, X

0 V u +

se...allora
se e solo se

Generalmente, le proposizioni nel vengono rappresentate attraverso l’uso di


lettere proposizionali e i connettivi sono rappresentati come mostrato nella seconda
colonna della Tabella 2.1. La terza colonna indica invece altre possibili
rappresentazioni per i connettivi.

Esempio 2. 1
Si consideri la proposizione composta “Lucia ha i capelli lunghi e Linda ha gli
occhi azzurri”. Se si indica con la lettera proposizionale A la proposizione “Lucia
ha i capelli lunghi” e con la lettera proposizionale B la proposizione “Linda ha gli
occhi azzurri”, l’intera proposizione può essere scritta in come A a B.

Formalmente, l’alfabeto del composto da:
- Lettere proposizionali, usualmente indicate da lettere latine maiuscole: A,
B,...;
- Connettivi: a, v, =>, <=>;
- Simboli ausiliari: ‘(’,
- Lettere proposizionali costanti: vero (T, o anche V o 1) e falso (±, o anche F o
0).

Le parentesi sono utilizzate per indicare l’ordine in cui i connettivi vengono


applicati e il loro uso verrà discusso più approfonditamente in seguito.
La sintassi del linguaggio definisce le sequnze ammissibili di simboli
sull’alfabeto, ossia le formule ben formate (fbf) del che verranno indicate con
lettere corsive maiuscole e con un apposito tipo di carattere, o “font”; esse sono
definite in modo ricorsivo come segue.

Definizione 2.2
- Ogni lettera proposizionale è una fbf;
- Se 24 e B sono fbf, (->24), (24 a B), (24 v B), (24 => B), (24 <=> B) sono fbf;
- Nient’altro è una fbf.
Elementi essenziali di logica matematica 21

Esempio 2.2
((-i (A a B)) o ( A^> (B v C))) è una fbf. Infatti:
- A, B e C sono lettere proposizionali, quindi sono fbf;
- Poiché A e B sono fbf, (A a B) è una fbf;
- Poiché B e C sono fbf, (B v C) è una fbf;
- Poiché (A a B) è una fbf, (—>(A a B)) è una fbf;
- Poiché A e (B v C) sono fbf, (A a> (B v C)) èuna fbf;
- Poiché (-.(A a B)) e (A =^> (B v C)) sono fbf, l’intera formula di partenza è
una fbf.

Analizzando la formula dell’Esempio 2.2, si può osservare che in modo analogo ad


altri esempi di espressioni parentetizzate, come le espressioni aritmetiche
tradizionali, le parentesi vengono usate nel per indicare l’ordine in cui i
connettivi vengono applicati. Estendendo l’analogia, a volte l’ordine di
applicazione dei connettivi logici viene lasciato implicito sfruttando le relazioni di
precedenza fra essi: ad esempio, come è ben noto che l’espressione a * b + c è una
abbreviazione per ((a * b) + c), analogamente A a B v C abbrevia ((A a B) v C).
Più precisamente, quando non vengono utilizzate le parentesi, i connettivi sono
applicati secondo il seguente ordine di precedenza-, —,, a, v, =^>, Se ci sono più
connettivi dello stesso tipo vengono seguite le regole di associazione; a, v:
associano da sinistra a destra e =>, associano da destra a sinistra.

Esempio 2.3
Nella formula A =^> B =^> C, il primo =^> è applicato tra A e (B =^> C), quindi la
formula è un’abbreviazione per (A => (B =^> C)); diversamente, nella formula
A a B a C, il secondo a viene applicato fra (A a B) e C, cioè la formula è
equivalente a (A a B) a C e a ((A a B) a C ).

Esempio 2.4
Si consideri la formula ((—.(A a B)) « (A (Bv C))), presentata nell'Esempio
2.2. Non tutte le parentesi presenti in essa sono necessarie e la formula può essere
riscritta nella forma -.(A a B) o A => B v C. Infatti, v viene applicato prima di
=>, =^> viene applicato prima di <=^> e —. prima di Invece, le parentesi che
racchiudono A a B sono necessarie, poiché, senza esse, il connettivo —, sarebbe
applicato solo a A e non ad A a B.
22 Informatica teorica

Per analizzare la struttura delle fbf è necessario definire i concetti di sottoformula,


insieme di sottoformule e albero sintattico, che risulteranno utili per introdurre la
semantica del

Definizione 2.3
Una sottoformula è una parte di una fbf che è a sua volta una fbf.

Esempio 2.5
Si consideri nuovamente la formula. ->(A a B) <=> A => B, che verrà richiamata
come fi.
- -.(A a B) è una sottoformula di fi, poiché è contenuta in fi ed è una fbf;
- A v C non è una sottoformula di fi, perchè, anche se è una fbf, non è
contenuta in fi;
- o A =>Bv C non è una sottoformula di fi, infatti, anche se è contenuta in fi,
non è una fbf.

Data ima fbf, l’insieme di tutte le sue sottoformule può essere calcolato
ricorsivamente come segue.

Definizione 2.4
Data una formula J4, l’insieme delle sottoformule di J4, Stfin(JzL), è definito come
segue:
- Se J4 è ima lettera proposizionale A, Stfin(JzL)={ A} ;
- Se J4 è della forma —iS, Stfm(J4)={ J4}uStfin(2?);
- Se J4 è della forma 2? a C, B v C, B => C, B <=> C, Stfm(JzL) = {34} u
Stfin(S) o Stfin(Q.

Esempio 2.6
Considerando nuovamente la fbf fi e applicando la Definizione 2.4, si ottiene
Stfm(—i(A a B) o A => B v C) =
={->(A A B)« A=> B v C; u Stmf(->(A a B)) o Stmf(A x>BvC) = {-.(A a B)
« A => B v C, 4A a B), A a>B v C] u Stmf(A a B) u Stmf(A) u Stfin(B v C)
= {-.(A a B) <=> A=> B v C, ->(A a B), A => B v C, A a B, A, B v C} u Stmf(A)
o Stmf(B) o Stfm(C) =
={->(A a B) o A => B v C. -,(A a B), A => B v C, A a B, A, B v C, B, C }
Elementi essenziali di logica matematica 23

Spesso gli alberi rappresentano un utile strumento per rappresentare le strutture di


espressioni parentetizzate. Anche nel caso del essi possono essere applicati
per analizzare la struttura delle fbf. Infatti, attraverso l’uso degli alberi, è possible
vedere come viene costruita una formula e sottolineare le sue sottoformule.
A partire da una fbf è possible costruire un albero, detto albero sintattico o
albero di struttura, della fbf in modo ricorsivo come segue:
- L’ultimo connettivo applicato (quello a livello più esterno) è la radice;
- I figli di un nodo che rappresenta un connettivo sono i sottoalberi che
corrispondono alle sotto formule a cui il connettivo è applicato;
- I connettivi sono i nodi interni, mentre le lettere proposizionali sono le foglie.

Si noti che ogni nodo può essere visto come la radice di un sottoalbero massimale
che rappresenta l’albero sintattico di una sottoformula.

Esempio 2.7

L’albero sintattico della formula JF è riportato in Figura 2.1

2.2.2 La semantica del Calcolo Proposizionale


Come stabilito in precedenza, la semantica di un linguaggio definisce il significato
della frasi sintatticamente ben formate del linguaggio. In 2C&Ì definisce come
decidere se una formula è vera o falsa a seconda del valore di verità delle lettere
proposizionali che la compongono e viene definita attraverso il concetto di
interpretazione e le tavole di verità dei connettivi.

Figura 2.1 Albero sintattico della formula JFdell’Esempio 2.7.


24 Informatica teorica

Definizione 2.5
L’interpretazione delle formule proposizionali è una funzione v>: {fbf.} {0,1}
che soddisfa le seguenti proprietà:
- o(l) = 0;
- o(T)=l;
- uGH) = l-u(JT);
- u(A?a 2B) = min(u(^f), o(.2));
- u(A?v 22} = max(u(l?), u(^);
- ^) = max(l-u(U?),u(^));
- u(A?aa 22} = min(max(l - u(^), u(^), max(u(_^), 1— o(_2))).

Informalmente, l’interpretazione assegna un valore di verità, vero (1) o falso (0), a
ogni lettera proposizionale, il valore vero (1) e falso (0) rispettivamente alle lettere
proposizionali costanti Tele valuta il valore di verità di ->J4, H a B, H v B,
H => B, H o B, sulla base dei valori di verità di H e B.

Esempio 2.8
Si consideri nuovamente la formula ^introdotta nella Sezione 2.2.1. Se A e C
sono entrambe vere (u(A) = u(C) = 1) e B è falso (u(B) = 0), il suo valore di verità
può essere calcolto utilizzando la Definizione 2.5 come segue:
u(-i(A aB)« A =>Bv C) = min(max(l - u(-,(A a B)), u(A =>B v C)),
max(u(-,(A a B)), 1 - u(A => B v C))).
Poiché,
u(-i(A a B)) = 1 - u(A a B) = 1 - min(u(A), u(B)) = 1 - min(l, 0) = 1 - 0
=1
u(A => B v C) = max(l - u(A), u(B v C)) = max(l - 1, max(u(B), u(C)))
= max(0, max(0,l)) = max(0, 1) = 1
diventa
v>(—i(A a B) o A => Bv C) = min(max(l - 1, 1), max(l,l - 1)) = min(l, 1)
=1

Un modo equivalente per calcolare il valore di verità di una fbf è quello di
utilizzare le tavole di verità, che mostrano come propagare il valore di verità delle
sottoformule attraverso i connettivi. La tavola di verità per il connettivo —। è
mostrata nella Tabella 2.2, mentre quelle dei connettivi binari sono rappresentate
nella Tabella 2.3.
Elementi essenziali di logica matematica 25

Tabella 2.2 Tavola di verità del connettivo —>.

J4
0 1
1 0

Tabella 2.3 Tavole di verità dei connettivi binari.

J4 B B 24 v B .A > B 24 o B
0 0 0 0 1 1
0 1 0 1 1 0
1 0 0 1 0 0
1 1 1 1 1 1

Esempio 2.9
Il valore di verità della formula J- è dato dalla Tabella 2.4, in cui ogni riga
rappresenta una combinazione dei valori di verità delle lettere proposizionali A, B
e C e l’ultima colonna, riporata in grassetto, rappresenta il valore di verità
dell’intera formula.

In generale, l’interpretazione di una formula può essere vera o falsa a seconda dei
valori che vengono assegnati alle lettere proposizionali che la compongono.
Tuttavia, alcune formule possono essere valutate indipendentemente dal valore di

Tabella 2.4 Tavola di verità della formula -i(A aB)q BvC.

A B c AaB -_,(AaB) BvC A=> BvC y


0 0 0 0 1 0 1 1
0 0 1 0 1 1 1 1
0 1 0 0 1 1 1 1
0 1 1 0 1 1 1 1
1 0 0 0 1 0 0 0
1 0 1 0 1 1 1 1
1 1 0 1 0 1 1 0
1 1 1 1 0 1 1 0
26 Informatica teorica

verità delle loro lettere proposizionali: ad esempio, A a —A risulta ovviamente


sempre falsa, indipendentemente dal valore di A. In altri casi, invece, possiamo
essere interessati a sapere se esistono o no assegnamenti di valori di verità alle
lettere proposizionali che compongono una formula tali da renderla vera: ad
esempio, A a B può essere resa vera assegnando il valore vero (1) sia ad A che a B.
Più in generale, si possono definire le seguenti proprietà della semantica delle
formule proposizionali.

Definizione 2.6
Una fbf 34 è. soddisfacibile se esiste un’interpretazione u tale che u(34) = 1; in tal
caso u è chiamato modello di 34

Si noti che una formula è soddisfacibile se e solo se la sua tavola di verità contiene
almeno un 1. Il valore delle lettere proposizionali corrispondenti alla riga che
contiene un 1 rappresentano un modello della formula.

Definizione 2.7
Una fbf 34 è insoddisfacibile se nessuna interpretazione u è un modello di 34.

Una formula insoddisfacibile è spesso chiamata contraddizione e la sua tavola di
verità contiene solo 0.

Definizione 2.8
Una formula 34 è una tautologia se ogni interpretazione è un modello di 34 e si
indica con |= 34.

Una formula 34 è una tautologia se e solo se —.34 è insoddisfacibile. Infatti, quando
una formula è una tautologia, la sua tavola di verità contiene solo 1.
Le definizioni appena introdotte possono essere estese a insiemi di formule
come segue.

Definizione 2.9
Sia T un insieme di fbf
- Un modello di F è un’interpretazione che è un modello per tutte le formule in
F;
- Tè soddisfacibile se ha un modello;
- Tè insoddisfacibile se nessuna interpretazione è un modello per T.
Elementi essenziali di logica matematica 27

Esempio 2.10
Si consideri l’insieme di formule T={A, A=>B, B vC};F è soddisfacibile in
quanto ha almeno un modello: u(A) = u(B) = u(C) = 1.

Partendo dalla Definizione 2.8, si può introdurre il concetto di conseguenza
semantica e il teorema di deduzione semantica.

Definizione 2.10
Una fbf 24 è conseguenza semantica di f (o è implicata da E o è soddisfatta da T)
se ogni modello di T è un modello di 24; in tal caso si indica con r |= 24.

Esempio 2.11

Si consideri l’insieme di formule T, introdotto nell’Esempio 2.10, e la fbf 24 = A v


C. I modelli di T sono u(A) = u(B) = u(C) = 1 e u(A) = u(B) = 1, u(C) = 0 e, in
entrambi i casi u(A vC)= 1. Quindi 24 è conseguenza semantica di T.

Si noti che T |= 24 se e solo se f u {—124} è insoddisfacibile.

Enunciato 2.1 (Teorema di Deduzione Semantica)


Sia T un insieme di fbf, 24 è conseguenza semantica di fu {2} se e solo se
B => 24 è conseguenza semantica di T.

Prima di dimostrare il teorema di deduzione semantica, è utile analizzarne il
significato e la sua rilevanza attraverso un esempio. Inoltre è interessante
evidenziare un significativo caso particolare del teorema stesso.

Esempio 2.12

Siano T l’insieme{A, A=>B, BvC}, 24 = Av C e 2? = A=>C. Poiché


Fu{B} = {A, A=>B, BvC, A=>C}, il suo unico modello è
u(A) = u(B) = u(C) = 1, che è anche un modello per A v C. Inoltre, i due modelli
di r (u(A) = u(B) = u(C) = 1 e u(A) = u(B) = 1, u(C) = 0) sono entrambi modelli
di (A => C) => (A v C).

Quando l’insieme di formule T è l’insieme vuoto, il teorema di deduzione
semantica diventa: 24 è conseguenza semantica di B se e solo se B => 24 è una
tautologia. In altre parole la relazione di conseguenza semantica, indicata con il
28 Informatica teorica

simbolo |=, corrisponde perfettamente al (è l’interpretazione del) connettivo logico


se...allora (qui rappresentato dal simbolo =>).

Dimostrazione dell’Enunciato 2.1


La dimostrazione è divisa nelle seguenti due parti:
- Ipotesi: T u {AB} |= J4
Tesi: T |= B => J4
Sia u un modello di T. Se u è anche un modello di B, allora, per ipotesi,
u(Jzl) = l. Quindi u(2J^_A)=l. Invece, se u non è un modello di B
(u(S) = 0), ed ancora u(S =^> J4) = 1;

- Ipotesi: T |= B =f> A
Tesi: Tu {B} |=A
Sia u un modello di T u {B}, allora è un modello di T e u(B) = 1. Per ipotesi
u(B =f> A) = 1 e, poiché u(B) = 1, allora u(A) = 1.

JT e B si dicono logicamente equivalenti se J41= B e B |= JA Si scrive che
J4 = B per indicare che due formule sono logicamente equivalenti. Informalmente,
due formule sono logicamente equivalenti se hanno la stessa tavola di verità
Si noti che J4 = B se e solo se |= J4<=> B. Infatti, l’equivalenza logica è
l’interpretazione del connettivo se e solo se.
Sia J4 una fbf e sia B una sottoformula di JA Se B viene sostituita con una
formula B', tale che B = B', si ottiene una formula JA' tale che JA=JA Questo
significa che se si sostituisce una sottoformula di una formula data con una formula
equivalente (alla sottoformula sostituita), si ottiene una formula equivalente alla
formula originale.

Esempio 2.13
Se si sostituisce A B in C a ( A B) con —A v B, si ottiene
C a (—.A v B) s C a (A =f> B), poiché ~A v B = A =f> B.

Inoltre, se, in una tautologia JA si sostituiscono tutte le occorrenza di una lettera
predicativa A con una fbf B, si ottiene una nuova formula Jl’ che è ancora una
tautologia.
Di seguito viene riportata una lista di alcune equivalenze di base. Il lettore è
invitato a riflettere sul loro significato intuitivo.

- ^JA>JA
- JAaJA^JA;
Elementi essenziali di logica matematica 29

- 34 v 34 =34;
- 34. a B = B a 34. (Commutatività);
- 34. v B= 2? v 34. (Commutatività);
- (34.a S) a J4 a (2 a C) (Associatività);
- (34.v B) v C = 34. v (2? v C) (Associatività);
- 34. a (34. v B) = 34. (Assorbimento);
- 34. v (34. a B) = 34. (Assorbimento);
- 34a(Sv Q = (34a S) v(34a Q;
- 34v(Sa Q = (34v S) a(34v Q;
- —1(34. a B) = —134. v —12? (Legge di De Morgan);
- —1(34. v B) = ~34. a —<B (Legge di De Morgan);
- 34. => 2? =-.34, v 2?;
- 34. => S = ^(34.a-hS);
- 34<x> B = (34.=> B) a (S^34);
- 2vl = S;
- S = TaS.

Utilizzando le equivalenze, è possibile mostrare che ogni fbf può essere riscritta
usando solamente un insieme minimale di connettivi, che sono:

Sfruttando gli insiemi minimali di connettivi e le equivalenze semantiche, è


possibile definire delle forme normali, che introducono dei ristretti schemi sintattici
per scrivere formule, che hanno però completa generalità semantica, cioè
permettono di formalizzare il significato di qualsiasi formula che si possa scrivere
con la completa generalità sintattica del cé?3d In altre parole, per ogni fbf esistono
una o più formule logicamente equivalenti ad essa scritte in una forma normale.
L’utilizzo di tali forme può essere utile per
- ragionare sulle formule in modo meccanico;
- manipolare le formule;
- sviluppare algoritmi per l’elaborazione automatica.
Ci sono principalmente due (simmetriche) forme normali per il chiamate
forma normale congiuntiva e disgiuntiva.
30 Informatica teorica

Definizione 2.11
Una fbf è in forma normale congiuntiva (FNC) quando è la congiunzione di una ò
più fbf, ciascuna delle quali è una disgiunzione di una o più lettere proposizionali o
della loro negazione.

Ad esempio, -A a (A v B v C) e (C v -A) a (B v C) a (A v -,B) sono in FNC.

Definizione 2.12
Una fbf è in forma normale disgiuntiva (FND) quando è la disgiunzione di una o
più fbf, ciascuna delle quale è una congiunzione di una o più lettere proposizionali
o della loro negazione.

Le fbf A v (B a C) v (-A a B a -.C) e (A a -nC) v (B a C) sono in FND.
Si noti che sia la forma normale congiuntiva sia la forma normale disgiuntiva
di una formula data non sono uniche, ma possono essere costruite in modo diverso.
Le forme normali possono essere costruite automaticamente attraverso
un’adeguata analisi delle tavole di verità. Precisamente, è possibile guardare agli 1
nella colonna che rappresenta il valore di verità della formula come alla
combinazione di valori delle lettere proposizionali (i valori che hanno nella riga)
che rendono la formula vera. Quindi, tutti gli 1 in una tabella di verità
corrispondono alle sole combinazioni di valori di verità delle lettere proposizionali
che rendono la formula vera. Sfruttando questa osservazione, è possibile partire
dalla tavola di verità di una fbf e costruire la formula equivalente in FND attraverso
la seguente procedura:
1. Per ogni 1 nella colonna della formula si costruisce una congiunzione che
contiene tutte le lettere proposizionali nella tabella
- in forma positiva, se, nella riga considerata, la colonna della lettera
contiene un 1 ;
- in forma negata, se, nella riga considerata, la colonna della lettera
contiene uno 0;
2. Si crea una formula in FND come disgiunzione delle formule costruite al
punto precedente.
Quando una formula contiene molti uno, è possible ragionare nel seguente modo:
uno 0 nella colonna della formula nella sua tavola di verità corrisponde a una
combinazione di valori delle lettere proposizionali (i valori cha assumono nella riga
corrispondente) che rende la formula falsa e, di conseguenza, tutti gli 0 in una
tabella di verità corrispondono alle sole combinazioni di valori di verità delle
lettere proposizionali che rendono la formula falsa, cioè le combinazioni non
desiderabili. Analogamente al caso della FND, questa osservazione può essere
Elementi essenziali di logica matematica 31

utilizzata per costruire una procedura, simmetrica al caso precedente, che permette
di costruire una formula in FNC a partire dalla sua tavola di verità:
1. Per ogni 0 nella colonna della formula si costruisce una disgiunzione che
contiene tutte le lettere proposizionali nella tabella
- in forma positiva, se, nella riga considerata, la colonna della lettera
contiene uno 0;
- in forma negata, se, nella riga considerata, la colonna della lettera
contiene uno 1 ;
2. Si crea una formula in FNC come congiunzione delle formule costruite al
punto precedente.

Esempio 2.14

Si consideri la tavola di verità per una formula JT definita su tre lettere


proposizionali rappresentata in Tabella 2.5.
La tavola contiene tre 0: riga 2, riga 5 e riga 6. Per ogni riga si costruisce una
disgiunzione ottenendo:
- Riga 2: A v B v —.C;
- Riga 5: —A v B v C;
- Riga 6: —A v B v —.C.

La formula JT è ottenuta come congiunzione delle formule appena introdotte:


(A v B v -,C) a (-A v B v C) a (-A v B v -,C).

Tabella 2.5 Tavola di verità per la formula J4 dell’Esempio 2.14.

ABC jzi
0 0 0 1
0 0 1 0
0 1 0 1
0 1 1 1
1 0 0 0
1 0 1 0
1 1 0 1
1 1 1 1
32 Informatica teorica

ESERCIZI
2.1 Si trasformino le seguenti fbf in FNC e in FND:
1. Ao(Bv(A^C)J
2. (B => A) a (->B (C v A))

2.2 Si sviluppino degli algoritmi per trasformare una qualunque fbf in FNC e
FND.

2.2 .3 Un sistema di assiomi e regole di inferenza per il


È possibile ora completare la definizione del come teoria formale. Gli assiomi
e le regole di inferenza vengono introdotti allo scopo di poter ottenere tutti e soli i
fatti veri come teoremi del L’attenzione viene rivolta unicamente alle fbf nella
forma senza-(A, v, ■»), al solo scopo di mantenere minimo il numero degli assiomi
e di non causare alcuna perdita di generalità. Si può considerare infatti ogni fbf
contenente le congiunzioni a, v, come una abbreviazione della corrispondente
forma normale, facente uso dei soli connnetiivi —i, =>. Di seguito vengono elencati
gli assiomi1*e l’unica regola di inferenza per il ‘AEP.

1. Assiomi
Se "K VK e TZ sono tre fbf del Ey; allora i seguenti sono assiomi del 2EU2
Al. V7 (W =>
A2. => uyy,
A3, => ((-,ìr

2. Regola di inferenza
La sola regola di inferenza del 'BA'A’e il Modus Ponens (MP): VF è una diretta
conseguenza di Ve V=> VP.

Si noti che il ha un insieme numerabile di assiomi. Infatti, Al, A2, A3 non


sono tre assiomi ma schemi di assioma, in cui V( fV, AL possono rappresentare una
qualunque fbf del

Esempio 2.15
Viene derivata ora la deduzione A B, B C <- A C, che afferma la
transitività dell’implicazione logica.

1 Si tenga presente che quella proposta in questo capitolo non è l’unica assiomatizzazione
(ossia l’unico insieme di assiomi) possibile per il AAEssa privilegia la minimalità del
numero di assiomi; altre assiomatizzazioni privilegiano ad esempio l’intuività degli assiomi
come “basi naturali” del ragionamento basato sulle proposizioni.
Elementi essenziali di logica matematica 33

1. La fbf (B Q => ((A (B Q) è un’istanza dello schema d’assioma Al,


dove Vè B C, Wè A. Essa è dunque un assioma.
2. Poiché B => C è un’ipotesi, è possibile applicare il MP, ottenendo
A => (5 => Q.
3. (A (B Q) ((A => B)=>(A=> Q) è un istanza di A2 se si pone V= A,
W=B, U=C.
4. Avendo precedentemente ottenuto A (B C), che è l’antecedente della
formula precedente, si può applicare ancora il MP, ottenendo
(A => B) => (A => C).
5. Poiché A B è un’ipotesi, si applica ancora il MP, ottenendo infine A C.

Si noti che la deduzione precedente resta valida anche se si sostituisce alle lettere
A, B, C ima qualunque formula ben formata. Si è ottenuto così uno schema di
deduzione "V W, Tl >- Il per ogni bf "V, W, U.

ESERCIZIO
2.3 Si dimostrino i seguenti schemi di teorema per ogni fbf Ve
1. v
2.
3. (V=>W)=>(Ov

I seguenti enunciati, esposti senza dimostrazione, possono essere di ausilio nello


sviluppo delle dimostrazioni del Arr.

Enunciato 2.2 (Teorema di Deduzione Sintattica)


Se T è un insieme di fbf, se Ve VV sono fbf e E, V•- VE, allora E •-V => VV. In
particolare, se V1- VE, allora >-V => VE.

Intuitivamente, il teorema di deduzione sintattica stabilisce che un’ipotesi in una
deduzione implica logicamente la formula dedotta. Ad esempio, si derivi la
deduzione dell’Esempio 2.15 usando l’Enunciato 2.2. Tutto ciò che occorre è la
deduzione A => B, B C, A >- C, che può essere ottenuta molto semplicemente.
Infatti B deriva dalla relazione A B e da A per mezzo del MP; C deriva poi dalla
B C e da B attraverso il MP. Infine si applica l’Enunciato 2.2 ponendo
rispettivamente T = {A=> B, B^>C}e^V=Ae VV= C.
Si noti la somiglianza strutturale tra l’Enunciato 2.2 e il teorema di deduzione
semantica (Enunciato 2.1). Questo è dovuto al modo in cui sono stati scelti gli
34 Informatica teorica

assiomi e le regole di inferenza per la teoria formale, che permettono di passare da


formule vere a formule vere attraverso la costruzione sintattica.
Si noti, inoltre, che il Teorema di Deduzione afferma una proprietà del ma
non è un teorema del Infatti, esso non può essere nemmeno enunciato come
fbf del Talvolta le proprietà di una teoria formale che non sono dimostrabili
all’interno della teoria (cioè come teoremi della teoria stessa), vengono chiamati
metateoremi. In questo testo verrà invece usato il termine “enunciato”, più generico
ed informale.
Si può osservare a questo punto che ogni assioma del g^ è una tautologia: la
verifica di ciò può essere ottenuta facilmente come esercizio. Il MP, inoltre, deduce
tautologie da altre tautologie: se cioè, per ogni fbf Ve 3V, (V => e Vsono
tautologie, altrettanto è 3V. Abbiamo così ottenuto i seguenti importanti risultati.

Enunciato 2.3 (Teorema di Correttezza)


Tutti i teoremi del z-sono tautologie.

L’Enunciato 2.3 garantisce la fondatezza (o correttezza) del ggg ossia il fatto che i
teoremi del rappresentino solo asserzioni vere, dato che una tautologia è una
formula che risulta vera indipendentemente dai valori assunti dai suoi argomenti.
Come corollario dell’Enunciato 2.3, si può dedurre la coerenza del g^, ossia
l’impossibilità di dimostrare, all’interno della teoria formale, la verità di una fbf V
e della sua negazione —fV. Se infatti V fosse una tautologia, -,"V non potrebbe
esserlo a sua volta, e viceversa. Vale anche l’opposto dell’Enunciato 2.3 (si omette
la dimostrazione):

Enunciato 2.4 (Teorema di Completezza)


Tutte le tautologie nella forma normale senza-(A, v, <=>) sono teoremi del g^.

Il (meta)teorema sopra esposto afferma la completezza del à'z, ossia la possibilità
di dimostrare come teoremi le asserzioni vere.
La fondatezza e la completezza sono proprietà molto importanti per il
poiché garantiscono che la teoria formale data racchiuda propriamente tutti i fatti
veri del calcolo proposizionale. Si comprenderà più avanti che tale piacevole
situazione non può essere sempre ottenuta.
Un’altra conseguenza positiva degli Enunciati 2.3 e 2.4 è la possibilità di
verificare, per mezzo di un algoritmo, se una fbf del g2g' sia o meno un teorema.
Infatti, essere un teorema è la stessa cosa che essere una tautologia, e la verifica se
una fbf sia o meno una tautologia può essere ottenuta enumerando tutti i possibili
(finiti) assegnamenti di valori di verità ai simboli di asserzione che compaiono
nella fbf e valutando le corrispondenti funzioni di verità per ciascun assegnamento.
Elementi essenziali di logica matematica 35

2.3 Calcolo dei predicati e teorie del primo ordine


Il permette di esprimere frasi che possono essere vere o false a seconda della
verità o falsità delle proposizioni atomiche che contengono e dei connettivi che
vengono usati per comporle. Tuttavia, le proposizioni atomiche sono frasi che
possono avere solamente un valore di verità “fisso”, la cui verità non dipende da
alcun parametro. Ad esempio, frasi del tipo “Giovanni ha fame” vengono trattate
come formule atomiche che possono essere vero o false; non viene fornita alcuna
regola per determinare la verità o meno di una singola proposizione. Quindi, frasi
come “x è un numero primo”, il cui valore di verità può essere vero (se, ad
esempio, x = 7) o falso (se, ad esempio, x = 6), non possono essere rappresentate
usando il &FF. Infatti x non è un “oggetto” costante e, poiché la valutazione
dell’intera frase dipende dal suo valore, non è possibile assegnarle un valore di
verità. Si consideri poi la frase “Ogni amico di Maria è amico di Piero”:
apparentemente questa frase è o vera o falsa (quindi rappresentabile in FFF), ma
per poterla valutare bisogna conoscere il significato di “essere amico di”, per poter
trovare tutti gli elementi x che possiedono questa relazione con Maria e verificare
che possiedano la stessa relazione anche con Piero. Anche questo tipo di
ragionamento non è quindi rappresentabile con il Lx
Si possono mostrare molti altri esempi che sottolineano i limiti del L?: Tali
limiti sono causati dall’astrazione che viene applicata al linguaggio naturale per
ottenere il Infatti, si può affermare che l’astrazione applicata dal è a
livello dell’intera frase e, quindi, per poter analizzare il significato di una frase a
seconda del suo contenuto è necessario utilizzare un livello diverso di astrazione.
In questa sezione vengono illustrate le cosiddette teorie del prim’ordìne e il
calcolo dei predicati del prim’ordine2-, questi concetti vengono visti come un
arricchimento del ^3?*, in modo tale che esso possa consentire la formalizzazione
della maggior parte dei ragionamenti logici e matematici, tra cui quelli evidenziati
precedentemente, in un modo molto naturale. Esistono diverse teorie del
prim’ordìne; in seguito con il termine logica del prim’ordine (. /Z ../, dall’inglese
First Order Logic) si indicherà il loro insieme: ogni definizione/proprietà attribuita
alla J^S^si intende applicabile ad ogni teoria del prim’ordine. Tra essi il calcolo
dei predicati ( ’F’FLF) rappresenta il più “piccolo” esempio di
Analogamente a quanto fatto per il viene dapprima definita la sintassi,
presentando informalmente gli elementi della successivamente se ne prende
in considerazione T interpretazione e validità ed infine si forniscono gli assiomi, le
regole di inferenza e le principali proprietà.

2 Per una spiegazione del termine “prim’ordine” si vedano i riferimenti bibliografici alla
fine di questo capitolo.
36 Informatica teorica

2.3.1 Elementi di base della logica del prim’ordine


La caratteristica principale della dFÌPS? è che permette di “spezzare” le frasi, che
nel erano rappresentate come formule atomiche, in diversi elementi, i cui
valori e le cui relazioni permettono di valutare il valore di verità dell’intera frase.
Si consideri la frase “Maria è bella”, che è una proposizione atomica in
Tale frase può essere espressa anche in .3RPSP osservando che “Maria è una
persona ben definita che ha la caratteristica di essere bella”, in cui “Maria” è un
oggetto costante e “essere bello” è una caratteristica che può essere posseduta o
meno da un oggetto. Se “l’oggetto” Maria ha la caratteristica di “essere bello”, la
proposizione è vera, altrimenti è falsa. Invece, nella frase “Una persona x è bella”,
x è ora una persona generica e la frase non è più una proposizione pura, ma il suo
valore di verità può dipendere dal valore di x. La può essere itilizzata per
esprimere frasi che si riferiscono a variabili definite su un qualche dominio e il
valore di verità di tali frasi dipende da chi o cosa è x.
Consideriamo ora la proprietà di “essere bello”. In generale, un oggetto
(costante o variabile) può avere diverse caratteristiche (una persona, oltre a essere
bella può essere alta, ricca, ecc.; un numero può essere primo, positivo, irrazionale,
ecc.) e può essere in relazione con altri oggetti rispetto a particolari proprietà (ima
persona può essere parente di un’altra persona, un numero può essere maggiore di
un altro, una persona può possedere un oggetto, ...). Queste proprietà sono
espresse matematicamente come relazioni (per maggiori dettagli si faccia
riferimento alla Sezione 1.1).

Esempio 2.16
“essere bello” è una relazione di arità 1 su un insieme P di persone. Se P = {Maria,
Lisa, Tommaso, Michele} and Essere_bello = {Lisa, Tommaso}, “Maria è bella” è
falso.

Si consideri ora la frase “Il padre di una persona x è bello”. Si è già osservato che x
è una variabile, mentre il padre di x è una persona la cui identità dipende dall’x
considerato. Più precisamente, “il padre di” è una funzione che data una persona
restituisce suo padre.
Le relazioni posso essere espressi attraverso nomi di relazione o predicati,
denotati da simboli del tipo A", dove i e n sono interi positivi e rappresentano
Tidentificativo del nome di relazione e Parità della relazione, rispettivamente.
Analogamente, le funzioni possono essere espresse attraverso nomi di funzioni f",
in cui, di nuovo, i e n sono interi positivi e rappresentano T identificativo del nome
di funzione e Parità della funzione, rispettivamente.
Attraverso i concetti di oggetti variabili o costanti e delle loro proprietà
(formalizzate attraverso relazioni matematiche), si possono costruire frasi come
quelle degli esempi visti precedentemente, che sono “sempre vere o sempre false”
Elementi essenziali di logica matematica 37

se contengono solo oggetti costanti o possono avere un “valore di verità variabile”


altrimenti.
Queste forme di frasi elementari possono essere composte nello stesso modo in
cui nel venivano composte le proposizioni. Infatti, la '-utilizza i connettivi
già introdotti in ^^(con le stesse regole di precedenza).
Un’altra importante caratteristica della che deriva dall’uso delle
variabili, è la necessità di esprimere attribute come “tutti”, “ogni”, “esiste”, che
specificano se e “quanti” elementi di un dominio godono della proprietà espressa
dalla frase. Queste espressioni sono introdotte in per mezzo dei
quantificatori, che possono essere di due tipi: universale e esistenziale.
Data una variabile x su un dominio D, se (x) significa che x ha una certa
proprietà, si potrebbe essere interessati a esprimere la frase che tutti gli elementi di
D hanno questa proprietà. In “per tutti” (più spesso “per ogni”) può essere
espresso attraverso il quantificatore universale, rappresentato dal simbolo V.
Sia x una variabile sul dominio D = {alr a2, afi, la formula Vx^j(x)su D
significa “per ogni elemento x in D, x ha la proprietà specificata dal predicato A’
Si noti che questo è equivalente a dire A^(afi a A'(a2) a A'(a3) . In generale,
per dire che tutti gli elementi di un dominio hanno una certa proprietà a' , si può

scrivere a a'(x.). Quindi, il simbolo V può essere visto come una

abbreviazione di tale congiunzione, che risulta particolarmente utile quando gli


elementi di D sono molti o addirittura infiniti.
Simmetricamente, si potrebbe essere interessati a esprimere che alcuni
elementi di D godono della proprietà A^. In “alcuni” può essere espresso
attraverso il quantificatore esistenziale, che si rappresenta con il simbolo 3.
Se si fa riferimento allo stesso domino D descritto precedentemente, la
formula BxA’(x) significa che “almeno un elemento x in D ha la proprietà
specificata dal predicato A* Si noti che questo è equivalente a scrivere la formula
A'fafiv A‘(a2) v Afa..). fri completa simmetria con il caso del quantificatore
universale, si può vedere la formula BxA' (x) come abbreviazione per x,.eD
v A'(x).

I quantificatori in J^Z?2 sono applicati solo alle variabili. Ad esempio,


assumendo che il predicato A' indichi la proprietà di essere primo, la formula
X/xA^ (3) formalizza la frase “Per ogni x, 3 è primo”, che, pur essendo
sintatticamente corretta, è poco significativa (ma comunque vera: 3 è un numero
primo indipendentemente dal valore considerato della variabile x). Invece, la frase
V3 A} (3) (la formalizzazione di “Per ogni 3, 3 è primo”) è priva di significato e, si
vedrà in seguito, anche sintatticamente scorretta; infine, una frase come “Ogni
38 Informatica teorica

sottoinsieme dei numeri naturali ammette minimo”, anche se assolutamente


sensata, non è formalizzabile (in modo immediato e naturale) in .777' e richiede
un approccio macchinoso per poter essere espressa in modo sintatticamente
corretto in 77(77?.
Se si vuole rappresentare in .TCTla frase “Se un numero primo divide il
prodotto di due numeri, allora divide almeno uno dei due”, è possibile riscrivere la
frase come “Per ogni numero x, per ogni numero y, per ogni numero z, se x è primo
e x divide y * z, allora x divide y o x divide z”. quindi si deve poter esprimere: “è
primo”, “divide” e “*”,dove “è primo” è una relazione di arità 1, “divide” è una
relazione di arità 2 e è una funzione di arità 2. Se si utilizzano i nomi di
relazione A, e A2 per “è primo” e “divide” rispettivamente e il nome di funzione
/j2 per la frase si formalizza come

VxVyVz(A‘(x) /\ A2 (xf 2 (y,z}) => A2 (x,y ) v A2 (x,z ))

Dopo questa introduzione informale a 77(77? nella prossime sezioni ne


descriveremo la sintassi e la semantica in modo più preciso e formale.

2.3.2 Sintassi della 77(777


Come già anticipato nell’introduzione, esistono molte teorie del prim’ordine
Vinsieme dei simboli di ciascuna di esse contiene:

- un insieme contabile {xb x2> • • •} di nomi o simboli di variabili.


- un insieme contabile {ff,f?,...} di nomi o simboli di funzione, dove
l’esponente w, del simbolo z'-esimo indica la sua arità. Per convenzione, se
ni = 0, f viene chiamato simbolo di costante ed è indicato da a;.
- un insieme contabile {a"',a"^,...} di nomi o simboli di predicati, dove
l’esponente n, indica Parità di ed è maggiore di 0.
- i simboli (, ), —i, => come nel 777, oltre al quantificatore universale V.
Tali elementi sono essenzialmente quelli introdotti informalmente nella Sezione
2.3.1 e partendo da essi è possibile introdurre formalmente la sintassi della 77(777.
Si noti che mentre nella Sezione 2.3.1 le costanti venivano introdotte come
elementi a sé stanti, nella definizione appena riportata esse sono un caso particolare
di funzione. In seguito useremmo il termine “costante” come elemento di 77(7J77fiex
indicare le funzioni di arità 0.3

3 II vincolo che i quantificatori si possano applicare solo a variabili genera il nome “logica
del prim’ordine”: le variabili sono “oggetti del prim’ordine” e i quantificatori possono
essere applicati solo ad essi. Esistono logiche di ordine superiore che permettono di
quantificare nomi di funzioni e di relazione. Come precedentemente anticipato, per ulteriori
dettagli si faccia riferimento alle note bibliografiche.
Elementi essenziali di logica matematica 39

Prendendo ispirazione dall’aritmetica tradizionale, la 222introduce il concetto di


termine per definire e calcolare i valori in un dominio dato.

Definizione 2.13
i termini sono definiti induttivamente come segue:
- i simboli di variabile e di costante sono termini;
- se f"1 è un simbolo di funzione e t\, t2, ..., tn sono n, termini, allora
f”‘ (r,..., t ) e un termine4*
.

Esempio 2.17

fi (x)) è un termine su un alfabeto che contiene il simbolo di varibile x, i


simboli di funzione a,7 f
"Z 1
2 e "Z 1
. Infatti a è una costante,7 x è una variabile,7 -Zf1 2 e
fi1 sono nomi di funzione. Invece, f2(fì(x)f') e //(//(x)//(«), x) non
sono termini. Nel primo caso, il simbolo di funzione unario non è applicato ad
alcun termine, mentre, nel secondo caso, il nome di funzione f2 ha arità 2, ma è
utilizzato con tre argomenti.

A partire dalla definizione di termini, è possibile esprimere le loro proprietà
sfruttando le relazioni, identificate attraverso i loro nomi.

Definizione 2.14
Si definiscono le formule atomiche di 222 come le espressioni del tipo
A(Zj,...,tn ), dove Aè un simbolo di predicato e t^... ,tn sono i termini.

Esempio 2.18

A2 (f2 (.fi1 (x), a ), y ) è una formula atomica, infatti, A2 è un predicato di arità 2


e f2(fi(x), a) e y sono termini.

Si noti che gli argomenti dei nomi di relazione e di funzione devono essere termini:
utilizzare un predicato come argomento di un predicato o di una funzione non è

4 D’ora in poi, nelle definizioni induttive del tipo qui esposto, non si ripeterà più
esplicitamente che solo gli oggetti costruiti in tale modo soddisfano la definizione.
40 Informatica teorica

sintatticamente corretto nelle 222. Le formule atomiche sono le formule “più


piccole” in J^?’e possono essere considerate come i suoi “mattoni”. A partire da
esse è possibile definire le formule ben formate (fbf) in modo tradizionale.

Definizione 2.15
Una fbf è definita induttivamente come segue:.
1. Ogni formula atomica è una fbf5.
2. Se V e 3V sono fbf e x è un simbolo di variabile, allora (—> V), (V => "W) e
(VxV) sono fbf.

Esempio 2.19

(A2(a,b) => (Vx(-id12(/12(x/22(o,r)),A)))) è una fbf: infatti


y42(/12(xt/22(a,x))A) è una formula atomica e quindi una fbf. Poiché essa è una
fbf, —<A2(f2(xf2(a,x)),b) è una fbf; di conseguenza
(Vx(-iyf2 (f2 (xf 2 (a,x )) ,b ))) è una fbf. Poiché A2 (a,b ) è una formula atomica,
e quindi una fbf, l’intera formula è una fbf.

Come per il 22, sono fbf anche formule del tipo V a fV, V v fV, V <^> fV, che
sono da considerarsi come abbreviazioni di —>(V => —>3V), —iV => 3V e
—1(( V => 3V) => —, (ÌV => V)). Analogamente, le formule che contengono il
quantificatore esistenziale 3 sono definite come abbreviazioni di formule che
contengono solo il quantificatore universale. Precisamente, (3x V) viene usato al
posto di -.(Vx (-iV)).
Le convenzioni sull’omissione delle parentesi che sono state fatte per il 22
vengono impiegate anche qui, con la convenzione aggiuntiva che i quantificatori
vengono inseriti in ordine di precedenza crescente fra <^>, e =>, v, a, —,.
Analogamente a quanto fatto per il 22 possiamo ora introdurre i concetti di
sottoformula e insieme di sottoformule, come segue.

Definizione 2.16
Una sottoformula di ima fbf è una parte della fbf che è a sua volta una fbf.

5 Analogamente a quanto fatto nel caso di 22, le fbf verranno indicate con lettere corsive
maiuscole.
Elementi essenziali di logica matematica 41

Esempio 2.20
Data la formula

3xA2 (x,a) Vx(—'A2 (f2 (xf 2 (a,x)),b) a A2 (x,x)) (J4)


- Ext’ (x,a ) è una sottoformula di 24, infatti è contenuta in J4 ed è una fbf;
- 'VxA2 (x,a ) non è una sottoformula di 24, poiché, anche se è una fbf, non è
contenuta in 24;
- 3xA2 (x,a ) => Vx non è una sottoformula di 24, poiché, anche se è contenuta
in 24, non è una fbf.

Definizione 2.17
Data una formula 24, l’insieme delle sottoformule di 24, Stfm(24), è definito come
segue:
- Se 24 è una formula atomica A, Stfm(24) = {A};
- Se 24 ha la forma -fB, VxS (3xB), Stfm(24) = {24} u Stfm(S);
-Se 24 ha la forma B => C (B /\ C, Bv C, Bo C),
Stfm(24) = {24} u Stfm(S) u Stfm(C).

Esempio 2.21

Se si considera nuovamente la formula 24 dell’Esempio 2.20 e si applica la


Definizione 2.17 si ottiene:
Stfin( 3xA2 (x,a) => Vx(-<A2 (f2 (xf 2 (a,x )),b ) a A2 (x,x )) =
(zlxA2 (x,a ) => Vx(—iA2 (f2 (xf2 (a,x )) ,b ) a A2 (x,x))ju Stfin (3xA2 (x,ay)

u Stfin (\t x(—iA2 (f2 (xf2 (a,x )),b ) a A2 (x,x ))) =

3xA2 (x,a ) => Vx(^A2 (f2 (xf 2 (a,x )) ) a A2 (x,x )),3xA2 (x,a ),
Vx^A2 (f2 (xf2 (a,x )) ,b ) a A2 (x,x ))

U Stfin (A2(x,aD u Stfin (—tA2 (f2 (xf2 (a,x')),b') a A2(x,x)) =


I
42 Informatica teorica

\HxA2(x,a) => 'dx(^A2(fi2(x,f2(a,x')'),b') a A2(x,x)),

{ 3xA2 (x,a),\/x(—,A2 (fi^ (x,fi2 (a,x)),b) /\ A2 (x,x)),A2 (x,a), ■


[-•A2(f12(xf2(a,x)),b)/\A2(x,x) j

u Stfin (^A2(f2(x,f2(a,x y),b)) u Stfin (zt2(x,x)) =


3xA2(x,a) => Vx(—<A2 (f2 (x,f2 (a,x )),b) a A2 (x,x )), ì

3xA 2 (x,a ),\/x(—iA2 (fi2 (x,f2 (a,x )),/>) a A2 (x,x )),Z(2 (x.a ),
-4 (fi2 (xf2 (a,x )),b)^ A2 (x,x ),—iA2 (// (x/22 (a.x )fib\
A2 (x.x )

u Stfin (A2 (// (x/22 (a,x )),/>)) =


\3xA2 (x,a) => Vx(—\A2 (f2 (x,f 2 (a,x)) ,b) a A2 (x,x)),3xA2 (x,a), ì

<; Vx(—,/l2 (fi2 (x,f2 (a,x )) ,b ) a A2 (x,x )) A 2 (x,a ),-.A2 (f2 (x,f 2 {a,x )) ,b ), (.
i(f2(x,f2(a,x)),b)
p/l|2(/12(x/22(«,x)),/>) A A2(x,x),A2(x,x),A2 j


Per concludere questa introduzione alla sintassi di si devono analizzare i
quantificatori. Un quantificatore agisce su una variabile. Se si scrive Vx (o Ex), il
quantificatore si riferisce ad x. I quantificatori hanno una visibilità limitata, in
modo analogo alle variabili nei linguaggi di programmazione. Ad esempio, nella
formula Vx J4 => 'B, V si riferisce alle occorrenze di x in J4, ma non a quelle di x
in B.

Definizione 2.18
Sia J4 una fbf e sia Vx B (Ex B) un elemento di Stfìn(J4), allora B è chiamato
campo d’azione del quantificatore Vx (Ex)

Esempio 2.22
Si consideri nuovamente la formula J4. dell’Esempio 2.20:
3xA2 (x,a) => Vx(^X12(/12(x,/’22(a,x))J6) a A2(x,x))
- Il campo d’azione di Ex è la sottoformula zt2 (x,a ) ;
- Il campo d’azione di Vx è la sottoformula -.z^2(fi2(xfi2(a,x)),b) a A2(x,x).
Elementi essenziali di logica matematica 43

Una variabile può apparire più volte in una fbf e ogni occorrenza può essere libera
o vincolata. Formalmente, un’occorrenza di una variabile x si dice libera quando
non è nel campo d’azione di un quantificatore Vx o 3x, altrimenti, si dice vincolata.
L’occorrenza di una variabile che appare nel quantificatore è vincolata per
definizione.

Definizione 2.19
Un termine t in una formula 34 è libero per una variabile x in 34 se non ci sono
occorrenze libere di x in A nel campo d’azione di un quantificatore che quantifica
una variabile che appare in t.

Come vedremo nella Sezione 2.3.4, la Definizione 2.19 ha un ruolo importante
nella definizione delle regole che permettono di manipolare formule di3^32

Esempio 2.23

Si consideri la formula fi
A2 (a,b)v 3yA2(f2 (x,y),f2 (a.xj) => Ax(^.A2 (fi2 (xf2 (a,x )),b ) a A2 (x.x ))

- f? (x>y ) è libero per_y (non ci sono occorrenze libere di_y);


- fy (x>y ) non è libero per x (x è libero nel campo d’azione di 3_y).

ESERCIZIO
2.4 Per le seguenti coppie <termine, fbf> si stabilisca se il termine risulta libero
o meno per x nella bf.

1. (x,-.V xX/yA2(x, y) )
2. (fi2(x,y) , xVyA2(x,y) => VxAÌ(x))
3- {fi{x,y}, x(A2 (x,y) VxAÌ(y)) )
4. (f2(z,y), —iV xV_vVz(^422 (x,_v) => YxA2(z,y)))

Definizione 2.20
Una fbf è detta chiusa se non contiene variabili libere.

Un esempio di formula chiusa è la seguente fbf:


A2 (a,b ) v 3yA 2 (fi2 (a.y ),y ) => Ax(-..A2 (fi2 (xfi 2 (a,x )) ,Z> ) a A2 (x,x )) .
44 Informatica teorica

Quando una fonnula non è chiusa, può essere resa chiusa premettendo all’intera
formula o il quantificatore universale o il quantificatore esistenziale delle variabili
che nella fbf abbiano occorrenze libere.

Definizione 2.21
Si consideri la fbf J4, denotata con J4(xb x2,... , x„), che contiene come uniche
variabili libere xb x2,... , xm. Vxi Vx2... Vx„J4(xb x2,... , x„) è chiamata chiusura
universale di AL

Definizione 2.22
Si consideri la fbf J4, denotata con J4(xb x2,... , x„), che contiene come uniche
variabili libere xb x2,... , xm. 3xi 3x2... 3x„J4(xb x2>... , x„) è chiamata chiusura
esistenziale di AL

Esempio 2.24
Si consideri nuovamente la fbf fidell’Esempio 2.23
Z (a,b ) v JyA 2(f2 (x,y )f2 (a,x )) => V x(-,A2 (f2 (x,f 2 (a,x )),&) a A2
2 (x,x ))

Tale formula non è chiusa poiché le occorrenze di x in A2 (Zi2 (*,y (a,x )) sono
libere. La sua chiusura universale e la sua chiusura esistenziale sono
rispettivamente:

Vx(^2 (a,b ) v JyA 2 (f2 (x,y )f2 (a,x )) => Vx(-,^2 (// (x/22 (a,x )),b)A. A2 (x,x )))
Bx(A2 (a,b) v 3yA2 (f2 (x,y )f2 (a,x)) => Vx(-,A2 (// (xf2 (a,x )) ,b ) a A2(x,x)))


Ottenute le formule e la loro struttura, bisogna ora interpretarle, ossia assegnare
loro un significato. Si ricordi che, nel interpretare una fbf significa assegnare
ad essa un valore di verità. Analogamente, in ZAgf l’interpretazione di una fbf
comporterà l’assegnazione di valori (o insiemi di valori) ai simboli di costante e di
variabile, di funzioni “vere” ai simboli di funzione, e di relazioni ai simboli di
predicato, in modo tale da poter stabilire, alla fine, se una formula è vera o falsa.
Per giungere a questo risultato occorrono però più definizioni e spiegazioni di
quanto fosse necessario per il
Elementi essenziali di logica matematica 45

2.3.3 Semantica della 2222


Come nell’analisi della semantica di qualsiasi linguaggio, e come è stato fatto per il
iS^la semantica della 2222verrà analizzata a partire dall’interpretazione dei suoi
simboli di base per poi definire l’interpretazione delle fbf.
Quindi, come primo passo, verrà assegnato un dominio alle variabili, un valore
nel dominio ai simboli costanti (o simboli di funzione di arità 0) e funzioni e
relazioni con appropriate arità ai simboli di funzione e relazione rispettivamente.

Definizione 2.23
Dato un linguaggio del primo ordine, cioè un insieme di fbf definite su un insieme
fissato di variabili, costanti, simboli di funzione e di relazione, una struttura
interpretativa è una coppia M = (D, 7), in cui
- D è un insieme non vuoto, chiamato dominio-,
- I è una tripla di funzioni6 che assegnano rispettivamente a ciascun simbolo di
costante c un elemento di D, a ciascun simbolo di funzione f”’ una funzione
di arità n.f: D"’ —>D e a ogni simbolo di relazione A"' una relazione di arità
ni A c D .

Si noti che le funzioni I possono assegnare lo stesso valore a costanti diverse;
inoltre, possono esserci strutture interpretative diverse per lo stesso linguaggio del
prim’ordine.

Esempio 2.25
Si consideri un linguaggio del prim’ordine che ha a, b, c come simboli di costante,
x e sy come variabili,7 Jf,l 17, Jf?
[
,
7 J 2.
come nomi di funzione e A?,
1’2
A:} come simboli
di relazione. Una possibile struttura interpretativa è = (Db f), in cui D, è
l’insieme dei numeri naturali e f è definita come segue:
- Zì(«) = 1,Z1(Ò) = 2, Z!(c) = 3;
- 7i assegna a f' l’operatore unario che restituisce per ogni numero il suo
successore, a il prodotto e a /22 la somma;
- 7i assegna a A: la relazione di uguaglianza e a /l2 la disuguaglianza “minore
di”.
In questa struttura la formula J7

6 Si osservi che in realtà sarebbe sufficiente vedere 7 come una coppia di funzioni in quanto
le costanti sono un caso particolare di simboli di funzione con arità 1.
46 Informatica teorica

(a,b ) v 3yA' (J? (x,y (a,x )) => Vx^A? (fi2 (xf2 (a,x )),/>) a A2 (x,x ))
>
introdotta nell’Esempio 2.23, viene interpretata come “Se 1 < 2 o esiste un numero
naturale y tale che x * y = 1 + x, allora per ogni numero naturale x, x * (1 + x) ^2
e x < x”.

Si noti che nella formula dell’Esempio 2.25 appaiono sia occorrenze libere sia
occorrenze vincolate della variabile x: intuitivamente, si può dedurre che, anche se
viene utilizzato lo stesso simbolo, il significato che il simbolo x assume nella
formula cambia con le diverse occorrenze, a seconda che esse siano libere o
vincolate. A breve questa differenza verrà analizzata più in profondità.
In una struttura le costanti sono interpretate come elementi del dominio D, i
termini sono composizioni di funzioni e le formule atomiche sono relazioni fra i
termini. Data una struttura, il valore di verità delle fbf dipende dal valore assegnato
alle variabili in quella formula.

Definizione 2.24
Un assegnamento o in una struttura interpretativa di un linguaggio L di JEf è
una funzione (parziale) dall’insieme di variabili di L a D.

Esempio 2.26
Si consideri nuovamente la formula jF usata negli Esempi 2.22-2.24 e
l’assegnamento c> tale che o(x) = 5 e c>(y) = 2. La formula sarà interpretata come
“Se 1 < 2 o esiste un numero naturale y tale che 5 * y = 6, allora per ogni numero
naturale x, x * (1 + x) 2 e x < x”.

Si noti che nell’Esempio 2.26 non è stato assegnato alcun valore alle occorrenze
vincolate delle variabili. Infatti, esse non fanno riferimento a uno specifico
assegnamento, come verrà mostrato nella Definizione 2.27.
Un valutazione di un termine f"' (xp... ,x n ) dipende dall’assegnamento o e da
I. Essa viene fatta considerando l’interpretazione f"' della funzione f e applicando
c> alle variabili. Formalmente, è definita induttivamente come segue.
Elementi essenziali di logica matematica 47

Definizione 2.25
Una valutazione di un termine t = f"'^,...,^) rispetto all’assegnamento cr è
definita come = I(f”' a n o) , dove I(f"‘ ) è l’operazione di arità m, su
D associata da lai simbolo funzionale f"‘ .

Esempio 2.27
Si consideri la struttura interpretativa M2 = (Z)2,Ej, in cui D2 è l’insieme dei numeri
naturali e I2 è definita come segue:
- h(a) = 2,
- I2 assegna a f' l’operazione di arità 1 che restituisce per ciascun numero il
suo successore, a il prodotto e a /22 la somma,

e l’assegnamento c tale che <j(x) = 5, <j(y) = 2.


Se si valuta il termine fi(fl(x,y),f^(a)) rispetto a ere I2, si ottiene

fi (f2 (x,y ),f^ (a)) o = )( f2 (x,y ) a,//(«)a) = //(x,y )c* //(a)a =


Z(/22)(o(x), = (5 + 2)*(2 + 1) = 21

Si può estendere ora il concetto di valutazione alle formule atomiche, come segue.

Definizione 2.26
[In una struttura M = (Z>,7)] un assegnamento c soddisfa la formula atomica
A"' (tv... ,t n ) se e solo se ,... ,t „d) e 7(J” ),dove I(A"') è la relazione di
aritàn, che interpreta A"' .

Esempio 2.28
Si consideri come dominio D l’insieme dei numeri naturali e si interpreti a come 2,
// interpretata come l’operazione di arità 1 che assegna a ciascun numero il suo
successore, /22 come l’operatore di somma e A: come la disuguaglianza “minore
di” e l’assegnamento c tale che <j(x) = 5, <s(y) = 2. la formula atomica
A^ (ffaìfi (x,y )) diventa 2 + 1 < 5 * 2. Questo significa che l’assegnamento c
soddisfa la formula nella struttura data.
48 Informatica teorica

11 concetto di soddisfacibilità si estende dalle formule atomiche alle fbf nel modo
seguente.

Definizione 2.27
Data una struttura M = (D, I), si dice che un assegnamento a soddisfa la fbf 34 se e
solo se:
- Se 34 è della forma —>2?, allora o non soddisfa B;
- Se34 è della forma B => C, allora o non soddisfa B o soddisfa C;
- Se34 è della forma Vx®, allora tutti gli assegnamenti r che differiscono da <y
al più per il valore della variabile x soddisfano 2>;
- Se 34 è della forma B a C, allora o soddisfa sia B sia C7;
- Se 34 è della forma B v C, allora o soddisfa (almeno uno fra) B o C;
- Se 34 è della forma B C, allora a soddisfa sia B sia C o o non soddisfa ne’
Sne’ C;
- Se 34 è della forma 2xB, allora esiste un assegnamento r che differisce da o
per al più il valore della variabile x e soddisfa B.

Si noti che, come anticipato nell’Esempio 2.26, l’assegnamento di una variabile x
non è significativo per le occorrenze vincolate di x; infatti, il valore di verità di
formule chiuse non dipende dal particolare assegnamento.

Esempio 2.29
Data la struttura = (Db li), in cui Di è l’insieme dei numeri naturali, Ifa) = 1,
Ifb) = 2, Zi(c) = 3 e E interpreta come l’operazione unaria che assegna a
ciascun numero il suo successore, // come il prodotto, /22 come la somma, A2
come l’uguaglianza e A2 come la disuguaglianza “minore di”, presentata
nell’Esempio 2.25. Si consideri nuovamente l’assegnamento o definito come
o(x) = 5 e o(y) = 2. Tale assegnamento non soddisfa la formula fi (Esempi 2.18­
2.21)
A2(a,b)v 3yA2(fi2(x,y)f2(a,x)) V x(^A2 (fi2 (xfi 2 (a.xf ,/>) a A2(x,x))
in Mb infatti la formula è interpretata come “Se 1 < 2 o esiste un numero naturaley
tale che 5 * y = 6, allora per ogni numero naturale x, x * (1 + x) 2 e x < x”. Poiché
“1 < 2” è vera l’antecedente è vero, ma il conseguente è falso.

7 Da questo punto in poi le definizioni sono conseguenza delle abbreviazioni introdotte in


precedenza
Elementi essenziali di logica matematica 49

La formula 34 è soddisfacibile in una struttura M = (D, I) se esiste un


assegnamento che soddisfa 24, altrimenti si dice che 34 è insoddisfacibile in M. Se
la formula 34 è soddisfacibile in una struttura .M, allora34 è soddisfacibile in
generale.
Si noti che la soddisfacibilità in una struttura implica la soddisfacibilità in
generale e che una formula soddisfacibile può non essere soddisfacibile in una
particolare struttura.

Esempio 2.30
Si consideri la struttura M2 = (D2, I2), che differisce da Af solo perchè I2 assegna a
Al la disuguaglianza “maggiore di” al posto della disuguaglianza “minore di”; in
questo caso, lo stesso assegnamento ct(x) = 5 e ct(v) = 2 soddisfa la formula J7
dell’Esempio 2.29. Infatti, la formula è interpretata come“Se 1 > 2 o esiste un
numero naturale y tale che 5 *y = 6, allora per ogni numero naturale x,
x*(l+x)*2ex> x”. Poiché 1 non è ovviamente maggiore di 2 e non esiste un
numero naturale che moltiplicato per 5 dia 6, entrambi gli elementi della
disgiunzione nell’antecedente sono falsi, quindi l’antecedente è falso e la formula
J7 è valutata come vera a prescindere dal valore del conseguente.
Quindi, la formula J7 è soddisfacibile in M2 e quindi è soddifacibile in
generale. La stessa formula, perciò, può essere soddisfacibile in una o più strutture,
ma non soddisfacibile in altre.

Definizione 2.28
Una formula 34 è vera in una struttura M = (D, I) se tutti i possibili assegnamenti
su D soddisfano JU

Si osservi che la soddisfacibilità delle formule chiuse, non dipende
dall’assegnamento scelto; esse non possono essere soddisfacibili ma non vere in
una data struttura M. Inoltre, la chiusura universale di una formula 34 è vera in M
se e solo se 34 è vera in M e la chiusura esistenziale di una formula 34 è vera in M
se e solo se 34 è soddisfacibile in M (ovviamente ogni formula vera è anche
soddisfacibile).
Analogamente, si può definire la falsità di una formula in una data struttura
come segue.
50 Informatica teorica

Definizione 2.29
Una formula 34 è falsa in una struttura M = (D, I) se nessun assegnamento in M
soddisfa 34.

Quindi, una formula 34 è falsa in una struttura M se e solo se è insoddisfacibile in
M. La chiusura universale di una formula 34 è falsa in M se e solo se esiste almeno
un assegnamento che non soddisfa 34 e la chiusura esistenziale di una formula 34 e
falsa in M se e solo se 34 è insoddisfacibile in M.

Esempio 2.31
Si consideri la struttura M3 = (Z)3, Z3) del tutto uguale alla struttura definita
nell’Esempio 2.29, con la sola differenza che, mentre Z2 assegna a X2 la
disuguaglianza “minore di”, I3 assegna a A 2 la disuguaglianza “minore di o uguale
a”. Si definisca poi l’assegnamento a, tale che a(x) = 1 e o(y) = 2. La formula Q:

^22(a,Z>)v 3yA2 (J2 (x,y)f2 (a,x)) => Bx(A2 (J2 (xf2 (a,x )),b ) a A2(x,x))

viene quindi interpretata come 1 < 2 v By(y = 2) => 3x(x * (1 + x) = 2 a x < x).
Quindi, (f è soddisfatta da a in M3 e, poiché esiste una assegnamento che
soddisfa Q, la formula è soddisfacibile in M3 ed è soddifacibile in generale. Inoltre,
nella struttura M3 esiste un assegnamento r che soddisfa il conseguente, quindi il
conseguente è vero in M3 e, di conseguenza, lo è anche la formula (f Se si
considera, invece di M3, la struttura MdeH’Esempio 2.29, (g viene interpretata
come 1 < 2 v Ey(y = 2) => 3x(x * (1 + x) = 2 a x < x) e l’assegnamento o non
soddisfa la formula in Mh Inoltre, poiché x < x è falso per ogni x e l’antecedente è
sempre vero, non esiste alcun assegnamento che soddisfa la formula in M3, e quindi
la formula è falsa in M3.

Ci sono poi casi in cui è possibile assegnare il valore di verità a formule della
indipendentemente dalla struttura scelta.

Definizione 2.30
Una formula 34 è (logicamente') valida se e solo se è vera in tutte le strutture;
questa proprietà è indicata con la notazione |= 34.
Elementi essenziali di logica matematica 51

Esempio 2.32

La formula 2x\!yA2x(x,y ) \!y2xA f (x,y ) non è logicamente valida se esiste


una struttura in cui l’antecedente è vero e il conseguente falso. Se Jx'dyA f (x,y ) è
vero, allora esiste un valore per x tale che, per tutti i possibili valori di y, x è in
relazione Af con Ma, se esiste tale valore di x per tutti i possibili valori di j, x è
in relazione A? con y, e quindi il conseguente non può essere falso. Quindi la
formula è valida.
Invece, la formula 'Vy2xA^(x,y)^2x'VyA^(x,y) non è logicamente
valida. Infatti, se si considera la struttura in cui D è l’insieme dei numeri naturali e
A' è interpretato come la disuguaglianza “minore di o uguale a”, la formula
diventa “Se 'dx2y(x < y) allora 2y'dx(x < y)”, che non è vera.

Definizione 2.31
Una formula JL è detta esempio di tautologia se è possible ottenerla da una
tautologia B (in sostituendo le lettere proposizionali in B con fbf di ,
Ovviamente lettere uguali in B sono sostituite da fbf uguali.

Gli esempi di tautologia sono sempre formule logicamente valide, mentre le
formule logicamente valide non sono necessariamente esempi di tautologia. Ad
esempio, 3xV;M12(x,iy) => VylxA* (x,y) è logicamente valida, ma non è un
esempio di tautologia.
Dopo avere definito la semantica della AA2ES2 si possono analizzare le
equivalenze e le implicazioni fra formule.

Definizione 2.32
Una fbf. JL è semanticamente equivalente a una fbf B se per ogni struttura
M = (D, I) e per ogni assegnamento <7, <7 soddisfa JL se e solo se <7 soddisfa B. In
tal caso si scrive JL = B.

Di conseguenza, una fbf 2A. è semanticamente equivalente a ima fbf B se e solo se


la formula JL o B è logicamente valida.
52 Informatica teorica

Definizione 2.33
Una fbf B è conseguenza semantica di una fbf 34 se, per ogni struttura M = (Z), 7) e
per ogni assegnamento c>, che soddisfa 34, c> soddisfa B. Questa proprietà è
indicata dalla notazione 34 |= B.

Si noti che 34 |= B se e solo se |=34 => B, infatti |= 34 => B significa che la
formula 34 => B è valida.

Esempio 2.33

La formula (A 2 (x,x ) \/xA2 (x,xj) => (~iA2 (x,x ) v VxA2 (x,x )) è logicamente
valida, poiché -<A2 (x,x) v VxA2 (x,x) è conseguenza semantica di
A2(x,x) <=> VxA2(x,x) . Infatti, se A2 (x,x ) VxA2 (x,x ) è soddisfatta da un
assegnamento in una struttura, allora anche -.^f2 (x,x ) v PxA2 (x,x ) è soddisfatta.

Analogamente a quanto fatto con si possono introdurre forme speciali in
e in particolare, verrà introdotta la forma normale prenessa (FNP). Infatti, a volte,
forme particolari per le fbf sono necessarie, sia allo scopo di comprendere meglio
la formula sia a scopi algoritmici. Una fbf in J^2?può essere manipolata in modo
da ottenere la forma desiderata, comunque semanticamente equivalente alla
formula originale.
La FNP di una fbf 34 è una fbf B, semanticamente equivalente ad 34, che ha
tutti i quantificatori all’inizio della formula. In B, la parte iniziale, composta da
tutti e soli i quantificatori viene chiamata prefisso e la stringa senza quantificatori
matrice.
Per trasformare una fbf 34 in FNP, bisogna applicare le seguenti
trasformazioni ricorsivamente:
- —iVx34 è sostituito con la formula equivalente Ex—,34;
- —13x34 è sostituito con la formula equivalente Vx—,34;
- Vx34(x) a B è sostituito con la formula equivalente \/y(2A.[y/x] a B);
- Ex34(x) a B è sostituito con la formula equivalente 3y(34([j/x] a B);
- Vx34(x) v B è sostituito con la formula equivalente Vy(34([j/x] v B);
- Ex34(x) v B è sostituito con la formula equivalente 3y(34([j/x] v B);
- Vx34(x) => B è sostituito con la formula equivalente 3y(34[j/x] => B);
- Ex34(x) => B è sostituito con la formula equivalente \/y(2A.[y/x] v B);
- B Vx34(x) è sostituito con la formula equivalente VjfS => 34[y/x]);
- S => 3x34(x) ) è sostituito con la formula equivalente EvfS => 34[y/x]);
Elementi essenziali di logica matematica 53

in cui J4(x) significa che la variabile x è libera in è l’espressione


ottenuta sostituendo tutte le occorrenze libere di x in J4 con il termine t e y è una
nuova variabile introdotta (che non era già presente in J4 e non ha occorrenze
libere in B).
Si osservi che quando il campo d’azione di un quantificatore universale
(esistenziale) è l’antecedente di un’implicazione, esso viene sostituito in FNP con il
quantificatore esistenziale (universale). Questo è dovuto all’equivalenza fra la
formula VxJTfx) => B (3xJ4(x) => 2>) e la formula —iVxJTfx) v B (—dxJ4(x) v B)
e dal fatto che —iVxJ4(x) (rispettivamente, —i3xJ4(x)) può essere sostituito con
3x—iJ4(x) (rispettivamente, Vx—iJ4(x)). Inoltre, si osservi che la FNP non è unica,
ma dipende dall’ordine con cui le sostituzioni vengono applicate; perciò in termini
rigorosi, essa non è una vera forma normale.

Esempio 2.34

Si consideri la formula 3xA2(x,a ) => —>V xA2 (f2 (xf2 (a,x )),y) a 3yA2 (x,y )) .
Tale formula può essere trasformata in FNP, applicando i seguenti passi:
Vz(A2 (z,a)=> —.V xA 2 (f2 (xf 2 (a,x )) ,y ) a 3yA 2 (x,y )) =
Vz(A2 (z,a ) => 3 x->A2 (f2 (xf 2 (a,x )) ,y ) a 3yA 2 (x,y )) =
Wz(A2(z,a) => 3w(^A2(f2(wf2(a,w)),y)/\3yA2(x,y))) =
V z(A2 (z,a) => 3w3v(^A2(f2 (wf 2 (a.w^y) a A2(x,vf) =
Wz3w(A2 (z,a) => 3v(^A12(f12(wf2(a,w)')y)AA2(x,v))) =
Vz3w3v(A2 (z,a ) => (^A2 (f2 (wf2 (a,w)),y) a A2 (x,v))) =

ESERCIZIO
2.5 Si dimostri che ogni esempio di tautologia è logicamente valido.

2.3.4 Cenni all’assiomatizzazione delle teorie delprim 'ordine


Si può ora procedere all’assiomatizzazione delle teorie del prim’ordine, ossia a
fornire opportuni assiomi e regole di inferenza in modo tale che alcune (o
possibilmente tutte) delle “verità” della teoria possano essere dimostrate come
teoremi. Si ricorda comunque che alcune verità dipendono dalla particolare
interpretazione della teoria, mentre altre no (e precisamente le formule logicamente
valide). Si consideri, ad esempio, una qualunque proprietà P in una qualunque
teoria. Se si è a conoscenza del fatto che la proprietà P è valida per tutti gli
elementi di un insieme, si può dedurre che P sia ugualmente valida per ogni
54 Informatica teorica

particolare elemento dell’insieme. Formalmente ciò si può descrivere come


deduzione della fbf P(a) dalla \fxP(x), indipendentemente dalla interpretazione di
P e di a. La deduzione di A(x, z) dalla A(x, y) e dalla A(y, z) è valida invece, in
aritmetica, se A è interpretata come la relazione “minore di”, ma non se A è
interpretata come “diverso da”. Inoltre, se si cambia il dominio di interpretazione
passando dai numeri agli esseri umani, la stessa deduzione è valida se A è
interpretata come “fratello di” (supponendo che un uomo sia fratello di se stesso),
ma non lo è se A è “padre di”. Per questo motivo è ragionevole dividere gli assiomi
e le regole di inferenza in assiomi logici e regole di inferenza logiche, che risultano
validi indipendentemente dalla particolare interpretazione, ed assiomi propri e
regole proprie, che sono invece specifici di particolari teorie e dipendono dal modo
in cui vengono interpretati. Qui di seguito vengono ora descritti e brevemente
commentati gli assiomi e le regole di inferenza logiche per le teorie formali del
prim’ordine e verrà introdotto il calcolo dei predicati ( che rappresenta il
più “piccolo” esempio di La sezione successiva è dedicata alla
assiomatizzazione dell’aritmetica classica come teoria del prim’ordine.

Assiomi e regole di inferenza logiche della


Siano Tl, 'Ve fKtre fbf di una qualunque teoria del prim’ordine. I seguenti tre
schemi d’assioma sono gli stessi del 33e sono necessari per permettere deduzioni
su ogni proposizione.

Al.
A2. ( 7/)) => (( T7^ X) => ( T7^ 7/))
A3. (-^ 7^ T) => ((-^ 7^ T7) => 7T)

Prima di fornire i rimanenti schemi d’assioma è necessario che il lettore ricordi la


definizione di occorrenze libere delle variabili nelle fbf e la Definizione 2.19, ossia
la definizione di termine libero per una variabile in una fbf Si ricordi inoltre che la
notazione 34.[t/x] indica che le occorrenze libere della variabile x nella fbf J4. sono
sostituite con il termine t.
Possono ora essere introdotti i rimanenti schemi logici d’assioma e le regole di
inferenza di ogni ,333"
A4. (Vx T7) 'V[t/x]se il termine t è libero per x in T7
A5. (Vx( T7^ T/7)) ( T7^ Vx T/7) se T7 non contiene alcuna occorrenza
libera di x.
Le regole logiche di inferenza sono
II. Modusponens (MP): come nel 33, TFsegue da J^e T7^ T/7
12. Generalizzazione (Gerì)'. Vx T7 segue da T7 dove x è una qualunque
variabile che occorra o no in T7
Elementi essenziali di logica matematica 55

Gli assiomi e le regole precedenti consentono, ad esempio, di ottenere la deduzione


“Tutti gli uomini sono mortali. Socrate è un uomo. Quindi Socrate è mortale”
all’interno della struttura di Infatti il ragionamento precedente può essere
tradotto nella seguente deduzione logica formale: Vx(Af(x) => D(x)), M(s) >- D(s),
dove i predicati M e D di arità 1 sono interpretati rispettivamente come “essere un
uomo” ed “essere mortale”, mentre 5 è una costante interpretata come l’individuo
“Socrate”. La precedente deduzione può essere ricavata nel modo seguente:
Vx(M(x) => D(x)) => (M(s) => D(s)) è un istanza dello schema d’assioma A4,
poiché il termine s è libero per x in M(x) => D(x); M(s) => D(s) segue, in virtù del
MP, dall’istanza precedente di A4 e dall’ipotesi Vx(Af(x) => D(x)); D(s) segue, per
MP, da M(s) => D(s) e dall’ipotesi M(s).
Prima di procedere ulteriormente è necessaria qualche spiegazione per le
restrizioni in A4 e A5. Intuitivamente, A4 stabilisce che se una proprietà è valida
per un qualunque valore di una variabile, allora rimane tale anche dopo la
sostituzione del simbolo di variabile con qualche termine t: è possibile ad esempio
dedurre A2(a,b) da VxVy A2(x, y). Qualche cautela va comunque usata nella
sostituzione: se infatti si sostituisse Vx3 A2 (x, y) con By A2 (y, y), rimpiazzando il
termine x con il termine y, si otterrebbe qualcosa di non logicamente valido. Si
interpreti infatti A2 come la relazione “diverso da” nell’insieme dei numeri
naturali. Allora \/xByA2(x,y) è vera, mentre 3j4]2(v,v) e falsa. Quindi
(\/xBy A2 (x, y))=> (By A2 ( y, y) ) non è logicamente valida.
Il lettore che non voglia annoiarsi con i particolari tecnici della Definizione
2.19, quando si trova ad impiegare lo schema d’assioma A4 può servirsi del
seguente piccolo trucco. Ogni volta che è richiesta un’istanza di A4, si usi una
variabile che non appare in t come variabile quantificata. Ciò garantisce a priori
che t sia libero per quella variabile in V. D’altra parte, ciò non provoca alcuna
perdita di generalità, poiché una fbf ÌV, ottenuta da un’altra fbf V, sostituendo
tutte le variabili vincolate di V con altre nuove variabili, non precedentemente
presenti in V, è chiaramente equivalente dal punto di vista logico a V. Ad
esempio, è sempre possibile sostituire \fxBy A2 (x, y) con VzByA2(z,y).
Nel caso di A5, la restrizione imposta è richiesta per prevenire che una fbf
del tipo Vx(A‘ (x) => A^ (x) ) => (A, (x) => VxA, (x) ) sia un assioma.

ESERCIZI

2.6 Si dimostri che \/xByA(x,y) •- 'VzBvA(z,v).


2.7 Si formalizzino i seguenti ragionamenti come deduzioni nelle .^SAe li si
dimostrino per mezzo degli schemi di assioma Al, A2, A3, A4, e delle
regole di inferenza II, 12.
56 Informatica teorica

1. Nessun uomo ha tre gambe. Bobby ha tre gambe. Quindi Bobby non è
un uomo.
2. Nessun uomo può essere felice e affamato. Giovanni è un uomo ed è
affamato. Quindi Giovanni non è felice.

Definizione 2.34
Una teoria formale del prim’ordine dotata unicamente degli schemi d’assioma Al,
..., A5 e delle regole di inferenza II, 12 è chiamata calcolo dei predicati del primo
ordine (

Intuitivamente, un è una teoria priva di verità dipendenti da qualche
particolare interpretazione. Non è quindi sorprendente che le seguenti affermazioni
fondamentali valgano per ogni

Enunciato 2.5
Ogni ^3^ è
- fondato (corretto), ossia ogni teorema è una fbf logicamente valida;
- coerente, ossia per nessuna fbf V valgono sia >- V che > V;
- completo, ossia ogni fbf logicamente valida è un teorema.

Senza entrare nella dimostrazione tecnica dell’enunciato, si invita il lettore a
verificare, su base intuitiva, la validità logica degli schemi d’assioma Al, ..., A5
(si ricordi l’Esercizio 2.5). Chiaramente, le regole II e 12 mantengono la loro
validità logica, ossia se T •- Ve ogni fbf di T è logicamente valida, altrettanto è V.
La coerenza è un ovvio corollario della fondatezza, poiché, per assurdo, •-'V e
>- -i V implicherebbero che sia V che —fV siano logicamente valide, ossia vere
sotto ogni interpretazione. La completezza è invece molto più difficile da
dimostrare, anche su base intuitiva, quindi ci limitiamo ad asserirla.

ESERCIZIO
2.8* Si formalizzi il seguente ragionamento in un “Un barbiere di una città
taglia la barba esattamente a quegli uomini che non se la tagliano da soli.
Quindi non ci sono barbieri nella città”. Si stabilisca se il fatto che non ci
siano barbieri nella città sia o meno una conseguenza logica della prima
frase.
Elementi essenziali di logica matematica 57

Abbiamo visto che, nel il Teorema della Deduzione Sintattica (Enunciato 2.2)
permette di ottenere F >- V=> "VP, da una deduzione T, V >- Tale enunciato è
stato utile nel rendere più semplici le deduzioni, è quindi naturale chiedersi se sia
altrettanto valido per le Si verifica a questo proposito che il Teorema della
Deduzione Sintattica, come formulato dall’Enunciato 2.2, non vale per Si
supponga infatti che V sia A^(x), dove A.‘ sia interpretata come la relazione
unaria “è pari” sui numeri naturali. Da una parte è dunque possibile costruire la
deduzione zf‘(x) |-Vxz(11(x) attraverso la regola 12 (Gerì), ma dall’altra è chiaro
che A'(x) 'VxA'(x) non risulta vera. Infatti ogni sequenza di numeri pari
soddisfa zf’(x), ma nessuna sequenza di numeri soddisfa VxA'(x). Quindi
A*(x) VxA^(x) non può essere logicamente valida e, quindi, per l’Enunciato
2.5 non può essere un teorema di alcun rìy-rì.
Queste osservazioni chiariscono meglio la regola 12. Precisamente, essa è di
aiuto nella deduzione di formule vere da formule vére, ma non ha il significato di
implicazione logica.
In ogni caso il Teorema della Deduzione Sintattica può essere adeguatamente
riformulato all’interno della struttura della Qui sotto (Enunciato 2.6) ne
vengono date, senza dimostrazione, due versioni semplificate, ma ugualmente utili.

Enunciato 2.6
1. Se una deduzione T, V |-Ì¥ non implica alcuna applicazione della regola di
inferenza logica 12 (Gerì), in cui la variabile quantificata sia libera in V, allora
i - v... >
2. Se T, V '-ÌV e Vè chiusa, allora P- V

Vengono ora fomiti assiomi propri per alcune semplici teorie formali del
prim’ordine. Per comodità si suppone che tali teorie non abbiano regole di
inferenza proprie. Ogni volta che la verità di ÌV debba essere dedotta dalla verità
dell’insieme {Vi, ..., V„}, si può affermare l’assioma Va...aV„ =>
consentendo la deduzione {V, ..., V„} •- ÌVmediante il MP.

Esempio 2.35
Presentiamo una formalizzazione degli ordinamenti parziali come una teoria
formale del prim’ordine. È sufficiente introdurre, a tale scopo, un solo predicato
A^, con arità due, mentre non è necessaria alcuna funzione. Allo scopo di rendere
la notazione più vicina al suo significato consueto, si scrive x < y al posto di
Af (x, y) e x > y al posto di -^Af (x, y). Si richiama comunque l’attenzione del
58 Informatica teorica

lettore affinché distingua attentamente il simbolo inteso come predicato, dalla


sua (eventuale) interpretazione costituita dalla relazione “strettamente minore di”.
Gli assiomi propri degli ordinamenti parziali sono:
POI : Vx(—ix < x). POI viene chiamato irriflessività di <.
P02: VxVjVz(x <y/\y<z=ì>x<z'). P02 viene chiamato transitività di <8.
Un esempio di semplice teorema in tale teoria è il seguente:
\fx\fy\fz\/v((x < y a y < z a z < v) => (x < v))
La sua deduzione formale è la seguente:
1. \/x\fy\/z(x <y Ajy <z => x <z) è P02.
2. x <y /\y <z => x <z è ottenuta dalla (1) e dal seguente esempio di A4:
(\/x\/y\fz(x <yz\y<z^>x<z))^>(x<y/\y<z^>x<z')
applicando successivamente il MP, per x, y e z.
3. (xj <x2 /\x2<x3 xi <x3) si ricava da POI e da A4.
4. VxiVx2Vx3(xi < x2 a x2 < x3 X, < x3) viene ottenuta dalla (3) applicando
ripetutamente la Gen.
5. y <z a z < v =>y < v deriva dalla (4) e da A4.
6. x<y,y<z, z<v|-x<vsi ottiene nel seguente modo:
6.1 y < z => (z < v => (y <z a z < v))
Questa formula è un esempio della tautologia V => (ÌV (V a 74)). (Si
verifichi per esercizio che la precedente formula del Z:zsia effettivamente
una tautologia)
6.2 y <z /\z<v è ottenuta dalla (6.1) e dalle ipotesi y < z e z < v, applicando
ripetutamente il MP.
6.3 y < v si ricava dalla (6.2) e dalla (5) mediante il MP.
6.4 x < v è ottenuto dalla x <y e dalla (6.3) grazie a P02.
7. x<;y=>(y<z=>(z<v=>x<v))è ottenuta dalla deduzione (6) applicando
ripetutamente l’enunciato 0.5 (si noti che la Gen non viene impiegata al punto
(6).
8. x<>,A>’<ZAZ<v=>x<vèun istanza della tautologia
V => ("VP => (Tl => ]/)) = (V a 3V a Tl ) => P (si verifichi ciò per
esercizio).
9. Infine la tesi è ottenuta dalla ripetuta applicazione della Gen alla (8).

Il lettore sarà rimasto probabilmente un po’ sconcertato per la lunga e noiosa
dimostrazione che si è resa necessaria per un’affermazione tanto semplice. Si può
intuire, comunque, che molti passaggi banali possono essere saltati, all’atto pratico,
da un esperto “dimostratore di teoremi”. Una situazione analoga si verifica nelle

8 Si noti che POI e P02 non sono schemi d’assioma ma veri assiomi.
Elementi essenziali di logica matematica 59

dimostrazioni matematiche, in cui è facile imbattersi in espressioni del tipo “è


chiaro che...”. In tali casi è responsabilità del dimostratore assicurarsi che un fatto,
intuitivamente vero, possa essere effettivamente provato nel dettaglio.
Le dimostrazioni formali si contrappongono a quelle basate sull’intuizione umana,
che sono, da un lato, molto più intuitive, ma dall’altro mancano della precisione e
del rigore tipici degli strumenti matematici. Tale precisione, di cui le prove formali
sono dotate, le rende adatte per essere supportate da strumenti “meccanici”. Si
faccia riferimento alle note bibliografiche per approfondimenti su tali strumenti.

ESERCIZIO
2.9 Si dimostri che, nella teoria dell’ordinamento parziale
VxVy(x < y =^> —i(y < x))
Suggerimento', si usi la tautologia V=^> (3Va —= —iV.

Definizione 2.35
Un modello di una teoria formale del prim’ordine è un’interpretazione della teoria
stessa in cui tutti i suoi assiomi sono veri.

Vengono ora elencate alcune semplici ma importanti conseguenze della
Definizione 2.35.

Enunciato 2.7
1. In un modello di una teoria formale del prim’ordine tutti i teoremi della teoria
sono veri. Ciò discende dal fatto che le regole di inferenza MP e Gen
conservano la verità delle formule in ogni interpretazione.
2. Se una teoria ha un modello, allora è coerente. Infatti, poiché i teoremi della
teoria sono veri in una interpretazione, quella costituita dal modello, non può
esistere alcuna fbf V per la quale '-"Ve'- —TV, poiché in tale modello "V
risulterebbe contemporaneamente vera e falsa.

Ogni insieme parzialmente ordinato è un modello della teoria avente per assiomi
propri POI e P02. I numeri naturali, ad esempio, corredati dalla relazione
“strettamente minore di” sono un modello di tale teoria. Ciò non vale però se la
relazione è “minore o uguale a”. Un altro modello di questa teoria è dato
dall’insieme degli esseri umani, con la relazione “essere antenato di”, una volta che
si sia convenuto che nessun uomo è antenato di se stesso.
60 Informatica teorica

Esempio 2.36
Diamo ora un’assiomatizzazione dei monoidi. A tale scopo sono necessari: una
funzione con arità 2, un simbolo di costante e un predicato con arità 2. Come
effettuato in precedenza, questi sono indicati rispettivamente dai simboli “w”,
“=”, usando la notazione infissa, allo scopo di rendere le formule immediatamente
comprensibili. Gli assiomi propri per i monoidi sono i seguenti:
MI. Vx(x = x) Riflessività
M2. Vx Vy(x = y => y = x) Simmetria dell’uguaglianza
M3. VxVyVz(x = _yA_y = z=>x = z) Transitività dell’uguaglianza
M4. VxVyVz(x o (y o z)) = ((x o y) ° z) Associatività della concatenazione
M5. Vx(m °x = xax°m = x) Identità
M6. VxVyVz((y = z) => Sostitutibilità dell’uguaglianza
(x°y = x°zAy°x = z°x)
Ovviamente ogni monoide è un modello della teoria precedente.

ESERCIZI
2.10 Si dimostri che l’identità è unica aH’intemo della teoria dei monoidi, ossia
che Vy(Vx(x ° y = x a y ° x = x) => y = u).
2.11 Si fornisca una «teoria formale del prim’ordine per gli ordinamenti totali
riflessivi.
2.12 Si fornisca una teoria formale del prim’ordine per i gruppi. Si noti che gli
assiomi per i gruppi devono includere gli assiomi per i monoidi. Se lo si
desidera, M5 può essere sostituito dal più “debole” M5: Vx(m°x = x). Perché?

2.4 Studio di un caso: una teoria formale per l’aritmetica e le sue funzioni
In questo paragrafo si accenna brevemente ad un’importante applicazione delle
teorie formali, e precisamente aH’assiomatizzazione dell’aritmetica, una teoria
fondamentale per quasi tutti i procedimenti matematici.
Questa sezione è a sua volta divisa in altre tre sottosezioni. Nella Sezione 2.4.1
viene descritto e commentato brevemente il classico sistema assiomatico di Peano
per l’aritmetica: una teoria formale del prim’ordine chiamata (Peano
Arithmetic). La Sezione 2.4.2 presenta invece una classe fondamentale di funzioni
definite sui numeri naturali: le funzioni ricorsive. La Sezione 2.4.3, infine, contiene
una panoramica sui risultati fondamentali dell’assiomatizzazione dell’aritmetica,
senza tuttavia dilungarsi in dimostrazioni formali.
Elementi essenziali di logica matematica 61
2.4.1 Gli assiomi di Peano
Peano fu. il primo matematico che suggerì di definire un insieme di “verità
elementari”, riguardanti i numeri naturali e le loro applicazioni, da cui si potesse
dedurre la maggior parte delle altre verità. Una descrizione informale di tali verità
elementari è la seguente.
PEI. Esiste un numero naturale indicato da 0.
PE2. Se x è un numero naturale, esiste allora un altro numero naturale, indicato
con x", chiamato successore di x.
PE3. Dato un numero naturale x, il suo successore x' è diverso da 0.
PE4. Se i successori di due numeri x e y sono uguali, allora x e y sono uguali.
PE5. (Principio di Induzione matematica). Sia Q una proprietà dei numeri
naturali. Se la proprietà Q vale per 0 (base dell’induzione) e la validità della
proprietà Q per un qualsiasi numero x implica la validità della stessa
proprietà per x' (passo dell’induzione), allora Q vale per tutti i numeri
naturali.
Si noti che, informalmente, da PEI, ..., PE4 si può dedurre l’esistenza di infiniti
numeri naturali. Da PEI e PE2 si ottengono infatti i numeri 0, 0', (0')', ... . Per
comodità si usi la notazione 0x e (F al posto della ((0')'...)', dove x e y
corrispondono al numero di apici. Se x è diverso da y, allora anche (fi deve risultare
diverso da (F, ossia deve indicare un numero diverso. Si supponga, per assurdo, che
0x = (F e, ad esempio, x > y. Da PE4 segue allora che 0*’1 = CF"1, ..., 0^ = (F^, ossia
il simbolo 0 con esattamente 0 apici e quindi il simbolo 0 stesso, risulta uguale a 0
con x-y apici, ossia (0*9'4)'. Questa conclusione è però in contraddizione con PE3,
quindi si è giunti ad un assurdo, come volevasi dimostrare.
Il principio dell’induzione matematica è uno strumento molto potente per
dimostrare le proprietà dei numeri naturali. Si consideri ad esempio la nota
n

proprietà P: £ < = n■(n + r)/2 . P risulta chiaramente valida quando n è 0, il che


f=0
costituisce la base dell’induzione. Si consideri ora il passo dell’induzione: si
supponga che P valga per un dato n (ipotesi induttiva): bisogna allora dimostrare
n+1 n
che P è valida anche per (n + 1). Dato che + (n +1), si ottiene, per
1= 0 i=0

l’ipotesi induttiva, che la prima parte del secondo termine vale n(n + l)/2 e quindi
che
n+1
y i =n(n + 1) / 2 + (n + 1) = (n(n + 1) + 2(n + 1)) / 2 =
i =0
= ((n +1) • (n + 2)) / 2 = (n +1) • ((n +1) +1) / 2, dimostrando in questo modo il passo
induttivo. Si può dunque concludere che P è valida per ogni n.
L’induzione matematica verrà usata nel corso di questo testo come principale
strumento di dimostrazione. Essa sarà applicata non solo all’insieme dei numeri
62 Informatica teorica

naturali, ma anche a qualunque insieme numerabile. Infatti, una proprietà valida


per il primo elemento di un insieme numerabile e per l’elemento (z + l)-esimo,
posto che sia valida per l’elemento z'-esimo, risulta valida per ogni elemento
dell’insieme.
Vengono ora ridefiniti gli enunciati PEI, ..., PE5 come una teoria formale che
verrà chiamata '/(Peano Arithmetic). Gli assiomi seguenti comprendono anche
l’assiomatizzazione delle operazioni aritmetiche fondamentali, e precisamente
dell’addizione e della moltiplicazione. A ha un solo predicato binario, una costante,
una funzione unaria e due funzioni binarie, che verranno rispettivamente indicati,
come al solito, dai simboli =, 0, ', +, • per rendere la notazione più vicina al suo
significato intuitivo. Si userà inoltre la notazione infissa per i simboli binari,
scrivendo x' al posto di '(x); x y è ima abbreviazione di ->(x = y). Gli assiomi
propri di ^Wlsono:
PAI. (x =y a x = z) y =z
PA2. x = y => x= y'
PA3. 0 x
PA4. x=y’=>x=y
PA5. x + 0=x
PA6. x + y = (x + y)'
PA7. x-0 = 0
PA8. x-(y') =x-y + x
PA9. Per ogni fbf V la seguente fbf è un assioma:
(X a Vx(V=> X' )) => VxV

Gli assiomi PAI, ..., PA9 consentono di dedurre la maggior parte delle note
proprietà dell’aritmetica in un modo molto semplice, sebbene talvolta noioso.
Eccone alcuni esempi:

Esempio 2.37
Le seguenti fbf TH1 e TH2 sono teoremi di ///
TH1 : x = x, come dimostra la seguente deduzione.
1.1 x + 0 =x (per PA5).
1.2 (x + 0 = x) a (x + 0 = x) (x = x), come si ricava da PAI sostituendo x, y,
z rispettivamente con x + 0, x, x. In particolare, si supponga che V sia
PAI. Si deduce dunque VxVyVzV, applicando ripetutamente la Gen. Si
definisca poi Vi come VyVzV e si consideri l’istanza di A4,
VxVl => V7("J. Si ponga V2 pari a VzX ° e si consideri l’istanza di
A4 Vy V2 V2 x. Infine, si definisca V3 come (X e consideri
l’assioma VzV? V3*. Applicando ripetutamente il MP, si ottiene,
Elementi essenziali di logica matematica 63

finalmente, la 1.2. Si noti che le condizioni per la validità di A4 sono


sempre soddisfatte poiché x + 0 risulta libero per x in Vi, e così via. Nel
seguito si sostituirà in ogni fbf la variabile x con il termine t, avendo cura
che t risulti libero per x, evitando questa noiosa sequenza di deduzioni.
1.3 x = x (dalla 1.2 e 1.1, per il MP).

TH2: x=y =>y = x, come si dimostra sulla base delle seguenti deduzioni.
2.1 ((x = y) a (x = x)) =5- y = x (da PAI, sostituendo z con x).
2.2 (x = x) => ((x =7) => (y = x)), dalla 2.1, applicando la tautologia ((A a B)
=> C) = B => (A => C).
2.3 x = x(daTHl).
2.4 x =y =>y = x (dalle 2.2 a 2.3, per il MP).

ESERCIZIO
2.13 Si dimostrino i seguenti teoremi.
1. x=yA_y = z=>x = z;
2. x=yAz = _y=>x=z.

Esempio 2.38
0 + x = x è un teorema, che può essere dedotto nel modo seguente servendosi
dell’induzione matematica.
Base dell 'induzione
0 + 0 = 0 è ottenuto da PA5 sostituendo x con 0.
Passo dell 'induzione
La seguente deduzione deriva la tesi 0 + x = x partendo dall’ipotesi induttiva
0 + x = x.
1. 0 +x=x (da PA6 sostituendo x con 0 e y
2. 0 + x = (0 + x)' conx)
3. 0 + x = x => (0 + x)' = x (da PA29)
4. (0 + x)' = x (da 1, 3 e dal MP)
5. (0 + x = (0 + x)') a ((0 + x)' = x')
=> 0 + x' =x' (dal punto 1 dell’esercizio 2.13)
6. 0 +x =x (dalle 5, 2, 4 tramite
l’applicazione di un’ovvia
tautologia)

9 D’ora in poi, omettiamo di specificare le sostituzioni ovvie.


64 Informatica teorica

Si applica infine il teorema della deduzione a 0 + x = x •- 0 + x' = x' (si verifichi


che ciò è consentito) ottenendo 0 + x = x=>0 + x'=x', che completa il passo
dell’induzione. Quindi, servendosi di A9, si può dedurre Vx(0 + x = x).

ESERCIZIO
2.14 Si dimostrino i seguenti teoremi.
1. x =y x + z =y + z
2. x+y=y + x
3. x-y=y-x

L’assiomatizzazione PA9 del principio dell’induzione matematica merita qualche


commento. Si noti dapprima che è essa uno schema d’assioma valido per ogni fbf
V, mentre PAI, ..., PA8 sono veri assiomi. Secondariamente si osservi che PE5 si
riferisce a una proprietà Q, mentre PA9 si riferisce ad una fbf V. I due termini non
sono completamente equivalenti, poiché una proprietà è una relazione unaria su un
insieme. Quindi, per un dato insieme S, l’insieme delle sue proprietà è p(5) e, nel
caso di N, p(N) ha cardinalità2!' . L’insieme delle possibili fbf in ogni teoria
formale del prim’ordine è invece numerabile. Quindi PE5 e PA9 non sono
esattamente identici. Per gli scopi di questo testo, comunque, si può considerare
PA9 come un’assiomatizzazione del principio di induzione.
Risulta evidente come l’aritmetica (ossia, i numeri naturali insieme alle loro
solite operazioni) sia un modello di Oltre a ciò, tutte le consuete notazioni
dell’aritmetica possono essere facilmente espresse mediante i simboli precedenti.
La relazione x < y (x è strettamente minore di y), ad esempio, è l’interpretazione
della fbf 3z(z * 0 a x + z = y).

ESERCIZI
2.15 Si determino le fbf aventi per interpretazione, nell’aritmetica consueta, le
seguenti relazioni.
1. x <y
2. x>y
3- -(x<p)
2.16 Si dimostrino i seguenti teoremi.

1- ~<x<y)
2. 0<x'
3. x <y = x'< y
Elementi essenziali di logica matematica 65

2.17 Si determini un sistema di assiomi per l’algebra dei numeri interi relativi con
le consuete operazioni di addizione, moltiplicazione e divisione intera.
2.18 Si dimostri che per ogni fbf V il seguente Principio di Induzione Completa è
un teorema di .^^(basato su PA9): (Vx(Vy(y < x) => V’' ) => V* ) =>
Vx'V' , dove y e x sono liberi per z in V.

2.4.2 Funzioni ricorsive


Molte funzioni fondamentali possono essere definite sull’insieme dei numeri
naturali, oltre alla funzione successore, all’addizione e alla moltiplicazione già
definite nella Sezione 2.4.1. Fra le2s" funzioni il cui dominio è No N^per qualche
k finito, la classe delle funzioni ricorsive gioca un ruolo determinante. Le funzioni
ricorsive vengono definite usando un piccolo insieme di funzioni di base, o iniziali,
ed una ridotta quantità di tecniche che consentono la costruzione di nuove funzioni
partendo da quelle esistenti. Si constaterà in seguito come l’insieme delle funzioni
ricavabili in questo modo contenga la maggior parte delle funzioni rilevanti.
Si noti che tale classe di funzioni rappresenta il fondamento matematico della
programmazione ricorsiva, che rappresenta un potente strumento informatico. Si
invita il lettore a riconoscere nella sezione la radice comune dei due formalismi.

Definizione 2.36
Le seguenti funzioni vengono chiamate funzioni di base o iniziali.
1. La funzione zero, Z: N —> N definita come Z(x) = 0 per ogni x.
2. La funzione successore'. N —> N definita come '(x) = x + 1 per ogni x. Per
coerenza con la Sezione 2.4.1 si adopera la notazione postfissa x' al posto
della prefissa'(x)-
3. Le funzioni di proiezione: U. : N* —> N dove 1 < i < k, definite come
u) (Xj,..., xk ) = x,, per ogni &-upla (xb ..., xfi.

Definizione 2.37
Le seguenti operazioni consentono di definire nuove funzioni a partire da altre
funzioni.
1. Sostituzione (o composizione)
Siano date le funzioni g: N* —> N, hi. N” —> N, con 1 < i < k. Allora la
funzione fi N” —> N, definita come/(xi, ..., x„) = gfhfxi, ..., x„), ..., hfxi, ...,
x„)), si dice ottenuta per sostituzione (o composizione) dalle g, hi, ..., hk.
66 Informatica teorica

2. Ricorsione primitiva
Siano date due funzioni g: N" N, /?: N"+2 N qualsiasi, con n > 0. Allora
la funzione fi N"+7 N, definita dalle due equazioni
/(%!,... ,X„,0) = g(xp... ,xfi .
/(Xi,... ,xn,y') = h(xi,...,xn,y,f(xl,...,xn,y))
si dice ottenuta per ricorsione primitiva da g e h (se n = 0 allora g è una
costante in M).
3. Minimizzazione (attraverso Eoperatore-fi)
Sia data una funzione g: N'!l/ N, qualsiasi, con n>0. Si indichi con
//yCgCxi, ..., x„,y) = 0) il minimo numeroy, se esiste, tale che g(xb ..., xmy) = 0
In generale, per ogni relazione (n + l)-aria R, si indica come py(R(xi, ..., x„,y)), il
minimo y, se esiste, tale per cui R risulti valida per (xb ..., x„,y). La funzione fi
N” —> N, definita come/(xi, ..., x„) = pfijfixi, ..., x„,y) = 0), si dice ottenuta da g
per minimizzazione o tramite l’operatore-p.

Si noti che se/(xi, ..., x„) = py(g(x\, xmy) = 0), allora f(xx, xn) è indefinita,
ossia, fifii, ..x„) = ±, ogni volta che non esiste uny tale che g(xb ..., x„,y) = 0.

Definizione 2.38
La classe delle funzioni ricorsive primitive viene definita nel seguente modo.
1. Tutte le funzioni di base sono ricorsive primitive.
2. Tutte le funzioni ottenute per composizione o per ricorsione primitiva da
funzioni ricorsive primitive sono funzioni ricorsive primitive.
La classe delle funzioni ricorsive parziali è definita in modo analogo, consentendo
però l’uso dell’operazione di minimizzazione.

Si userà il termine generico funzione ricorsiva quando non verrà richiesta ima netta
distinzione fra funzioni ricorsive primitive e funzioni ricorsive parziali.
Gli esempi seguenti mostrano la potenza delle operazioni precedenti nel
definire nuove funzioni.

Esempio 2.39
Vengono ora presentati alcuni esempi di funzioni ricorsive primitive.
1. fi(x, y) = x + y è definibile per mezzo della ricorsione primitiva nel modo
seguente.
/i(x, 0) = x
ffit, y} =fi(x,yY
Elementi essenziali di logica matematica 67

Nella definizione precedente /i(x, 0), ossia, x + 0, è definita per mezzo della
ricorsione primitiva usante Lf come funzione g; f(x, y') è definita usando
come funzione h la composizione C/22 = (y, f(x, y)').
2. f2(x, y) = x-y è definibile per mezzo della ricorsione primitiva nel modo
seguente.
x • 0 = 0 (g è la funzione zero)
x-y’ = x-y + x (h è la funzione h(x, y,z) = x + z)
3. y(x) = if x > 0 then x - 1 else 0 è definibile nel modo seguente.
/3(0) = 0
y3(x')=x
4. /i(x, y) = if x>y then x -y else 0 è definibile nel modo seguente.
/4(x, 0) = x
f&, y) =f3(ffx,y)')
n

5. y(x) = i è definibile nel modo seguente.

/5(0) = 0
/5(x') = x’ +f5(x)
Le seguenti funzioni sono ricorsive parziali.
6. /6(x) = if x = 0 then 0 else ± è definibile per mezzo della minimizzazione nel
modo seguente.
/6(x) = pfy + x = 0)
7. /7(x) = if x > 0 then x - 1 else ± è definita da.
/7(o)=/6(oy
=X
Si noti che f7, diversamente day, è una funzione parziale sul dominio N
poiché non risulta definita per x = 0.
8- fifx,y) = ifx > y thenx-y else ± è definita da.
/8(x,0) = x
/8(x,j/) =fi(f(x,y))
9. y(x) = radice quadrata esatta di x, ossia il valore y, se esiste, tale per cui x = y2,
è definibile nel modo seguente.
/7(x) = ^(x-y2 = 0)
(Ovviamente la funzione “quadrato” è ricorsiva primitiva).

E immediato riconoscere che tutte le funzioni ricorsive primitive sono totali,
mentre alcune funzioni ricorsive parziali non lo sono.

ESERCIZI
2.19 Si mostri che le seguenti funzioni sono ricorsive primitive.
68 Informatica teorica

/io(x) = radice quadrata intera di x, ossia quel valore y tale che


1-
/<x<(y+l)2.
2. = |x-a| = if x >y then x-y elsej-x
2.20 Si mostri che se /e g sono funzioni ricorsive parziali w-arie, altrettanto è la
funzione h, definita da h(x[, ..., x„) = if/(xi, • • -, xn) > g(xb ..., x„) then 1 else
0.

Si è visto come l’induzione sia uno strumento molto potente per dimostrare le
proprietà dei numeri naturali. Analogamente, la ricorsione primitiva è uno
strumento potente per costruire funzioni sull’insieme dei numeri naturali. Questi
due strumenti concettuali sono fortemente correlati l’un l’altro, come mostrato dal
seguente esempio.

Esempio 2.40
Dimostriamo per induzione la proprietà associativa dell’addizione, ossia il fatto
che, per ogni x, y, x, (x + y) + z = x + (y + z). Non si entrerà in tutti i noiosi dettagli
della deduzione formale poiché il lettore dovrebbe ormai essere in grado di
svilupparli da solo, se necessario. L’induzione è basata sulla variabile z.
Base dell’induzione
Dobbiamo dimostrare che (x + y) + 0 = x + (y + 0).
Ciò, a sua volta, può essere dimostrato per induzione su y nel modo seguente.
- (x + 0) + 0 = x + (0 + 0) poiché x + 0 = xe0 + 0 = 0.
- Sia (x+y) + 0 =x + (y + 0). Allora (x+yr) + 0 = ((x +yf + 0) = (0 + (x
+ L)') = (0 + (A +.Q)' = ((x + y) + 0)' = (x + (y + 0))' = (x + (y + 0)') =
(x + (0 +y)') = (x + (0 + a')) = x + (y' + 0).
Passo dell 'induzione
Si supponga, come ipotesi induttiva, che (x + y) + z = x + (y + z). Allora (x + y)
+ z = ((x + y) + z)' = (x + (y + z))' = (x + (y + z)') = x + (y + z'). La
dimostrazione è quindi completa.

ESERCIZIO
2.21 Si dimostri che, per ogni x, y e z, x^z = x’' • x2.

2.4. 3 * Alcune proprietà fondamentali dell’assiomatizzazione dell’aritmetica


In questa sezione vengono schematicamente illustrati alcuni importanti risultati
concernenti l’assiomatizzazione dell’aritmetica; il lettore interessato a raggiungere
Elementi essenziali di logica matematica 69

una comprensione più profonda e completa di questi argomenti è comunque


invitato a far riferimento ad una letteratura più specializzata.
Nella Sezione 2.4.1 si è affermato intuitivamente che la consueta
interpretazione dei simboli dell’aritmetica sull’insieme dei numeri naturali è un
modello per i suoi assiomi, ossia, è tale che tutti i suoi assiomi, compresi gli
assiomi logici fomiti nella Sezione 2.3, e quindi tutti i suoi teoremi, risultano veri
per quella interpretazione. Se una teoria ha un modello, allora essa è anche
coerente, poiché non può accadere che valgano sia ►-V che •- -, Jzper ogni fbf
ciò comporterebbe infatti la contemporanea verità e falsità di "Lall’interno del
modello. Questo viene espresso rigorosamente nel seguente enunciato.

Enunciato 2.8
L /ha un modello, quindi coerente.

Si fa notare, comunque, che l’Enunciato 2.8 non può essere dimostrato come un
teorema della stessa teoria, ossia non esiste alcuna fbf che stabilisca la coerenza di
e che sia deducibile dai suoi assiomi. Per questo motivo, l’enunciato 2.8 non è
in contraddizione con il seguente enunciato fondamentale.

Enunciato 2.9 (Teorema di Incompletezza di Gddel)


è incompleta, ossia, esiste una fbf chiusa V per la quale né ►- Vné ►- -, V
sussistono.

L’Enunciato 2.9 ha conseguenze molto importanti. Si noti infatti che, in ogni
interpretazione, una fbf chiusa deve risultare vera o falsa. L’Enunciato 2.9 implica
dunque l’esistenza di qualche formula vera dell’aritmetica che non può essere
dimostrata come teorema di Ciò spiega l’uso del termine “incompletezza”.
Non verrà illustrata la dimostrazione dell’Enunciato 2.9, ma si cercherà di far
emergere l’idea su cui si fonda, in quanto esso non è altro che una formalizzazione
di classici paradossi filosofici. Intuitivamente, la dimostrazione dell’Enunciato 2.9
consiste nella costruzione di una fbf Vche asserisca l’indimostrabilità di se stessa:
V deve ciò essere un enunciato formale del fatto che non esiste alcuna
dimostrazione di V all’interno della teoria. Ora, poiché ha un modello,
all’interno di esso Vdeve risultare vera o falsa. Se Vè falsa, allora esiste una
dimostrazione di V, quindi -iV e V devono essere vere nel modello, il che è una
contraddizione. In conclusione V risulta vera in ogni modello di ma ciò
implica anche la sua non dimostrabilità all’interno della medesima teoria.
La costruzione della suddetta fbf V richiede uno strumento tecnico
fondamentale, che vale la pena di menzionare a parte. Esiste infatti un metodo che
permette di descrivere i numeri come termini di e le funzioni su numeri
70 Informatica teorica

naturali per mezzo di fbf di /'. / D’altro canto, i numeri naturali vengono poi
associati alle fbf, ai teoremi ed alle dimostrazioni di cosicché è possibile
parlare del “numero di una fbf’, del “numero di una dimostrazione” ecc. Tali
numeri possono essere calcolati come valori di funzioni che, a loro volta, vengono
rappresentate per mezzo di qualche fbf. Tratteggiamo alcuni punti fondamentali
della precedente esposizione.

Definizione 2.39
xv otte
r-“A'—~<

Per ogni numero naturale x, il numerale x è il termine 0, ossia la funzione


“successore” applicata x volte alla costante 0.

Definizione 2.40
Una funzione fi N"~>N si dice rappresentabile in .A'/se e solo se esiste una fbf V
con variabili libere Xj, ...,x„ tale che, per ogni numero naturale kx, ..., kn+1.
1. Se/Uj, U) = U+), allora

2. i-VxVX^ 1"
X1 >•• • >xn + I
A XI ’••• ’xn + l
=>x=y)

Si noti che se fi è totale e rappresentabile, allora per ogni k\, ..., kn •-
Bxn+yV*'” . Infatti, per ogni kx, ...,kn esiste una k^ tale che i-V*1’””*”1. Si può

quindi dedurre Bx^iV*1’' ’*”+‘ (in generale, per ogni fbf Ve termine t, libero per x

in V, se >- V'x, allora •- Bx'V). Ad esempio, la funzione zero Z è rappresentata


dalla fbf Xi = Xi Ax2 = 0, la funzione addizione è rappresentata dalla fbf x3 = Xi + x2,
ecc.

ESERCIZIO
2.22 Si dimostri che tutte le funzioni base sono rappresentabili in

Tralasciando alcuni dettagli tecnici si può dimostrare il seguente enunciato.

Enunciato 2.10
Tutte le funzioni ricorsive parziali sono rappresentabili in
Elementi essenziali di logica matematica 71

Si è così riusciti a rappresentare ogni funzione ricorsiva parziale per mezzo di una
fbf di AFsXL’opposto dell’enunciato 2,10 si ottiene associando biiettivamente i
numeri naturali agli elementi di e di ogni teoria del prim’ordine nel modo
seguente.

Definizione 2.41
Si consideri una qualunque teoria formale del prim’ordine, dove X = {xj indichi
l’insieme delle sue variabili, A = {a,} l’insieme delle sue costanti, F = {f"‘}
l’insieme delle funzioni, la cui arità è diversa da 0, PL = {A"' } l’insieme dei
predicati ed <5 l’insieme dei simboli, ossia XuAuFuPLu {(,)/,’, V, =^>};
siano poi E l’insieme delle espressioni ed SE l’insieme di tutte le sequenze di
espressioni.
1. Si definisce, nel modo seguente, una funzione biiettiva g: S u E u SE -> N.
1.1 g(‘C) = 3, g(‘)’) = 5, g(‘,’) = 7, g(V) = 9, g(^) = 11, g(^) = 13.
1.2 Per ognix; in X, g(x() = 5 + 8 • i.
1.3 Per ogni in A, g(a.) = 7 + 8 • i.
1.4 Per ogni f"‘ in F, g( /,"' ) = 9 + 8 • i.
1.5 Per ogni A"‘ in PL, g( A"' ) = 11 + 8 • i.
2. Sia e = 5152 • • • 5„ una qualunque espressione appartenente ad E. Risulta allora
g(e)=2g(’1’ -3g(j2)... psn(Sn), dove p, indica l’z-esimo numero primo,
supponendo che pi = 2.
3. Sia se = {eb e2, ..., en} una qualunque sequenza di espressioni in SE. Si
ottiene allorag(se)= 2g(e') •3g(<'2) ••• .

È facile verificare, per esercizio, che g è totale ed uno-a-uno. Infatti, i simboli


diversi risultano legati a numeri dispari diversi, espressioni diverse a numeri pari
diversi, la cui unica fattorizzazione nel prodotto di numeri primi è una potenza
dispari di due. Sequenze di espressioni diverse posseggono numeri diversi, in cui
l’esponente di 2 nella fattorizzazione è dispari. Si noti che il numero di una
espressione costituita da un unico simbolo è diverso dal numero associato al
simbolo stesso. Il numero g(zz), dove u è contenuto in S u E u SE, è chiamato
numero di Godei di u, in onore del matematico Kurt Godei, che inventò questa
tecnica. Il procedimento con cui si definisce una funzione uno-a-uno che correli gli
elementi di una teoria matematica ed i numeri viene chiamato aritmetizzazione e si
è rivelato molto utile nella dimostrazione di proprietà di molti formalismi. Una sua
importante applicazione verrà esaminata nel Capitolo 8.
Fra i numeri di Godei di una teoria, e di XXX in particolare, si focalizzi ora
l’attenzione sui numeri corrispondenti alle fbf, alle dimostrazioni ed ai teoremi.
Una dimostrazione piuttosto lunga ma concettualmente semplice mostra che le
72 Informatica teorica

relazioni unarie su N che stabiliscono che un numero x è il numero di Godei di una


fbf, di un assioma e di una dimostrazione di .Assono ricorsive primitive10.
Ad esempio, il fatto che x sia il numero di Godei di un’espressione costituita
da una variabile è espresso dalla relazione 3z(l < z a x = 25+8z). Come ulteriore
esempio, siano El, E2, E3 le espressioni aventi come numeri di Godei,
rispettivamente, z, x, y. Si supponga che El sia la conseguenza di E2 e E3 per MP,
ossia che E3 sia pari a (E2 => El). Siano xb ...,xi gli unici numeri interi per i quali

x = 2*1 ••• p*'* e si definiscano in modo analogo yb ...,yi , zb ...,zi . Si conclude


allora che = 3, _y2 = X], x;] =x; ,y. = 13, y. = zb ..., yf i=zj ,yì = 5 e

che la relazione y = 23 -3*1 ••• p5t ha come funzione caratteristica una funzione
ricorsiva primitiva di x, y, z.

ESERCIZIO
2.23 Si dimostri che le relazioni, che esprimono il fatto che x è il numero di Godei
di un termine di e di una formula atomica di sono ricorsive
primitive.

Ulteriori analisi, che si sviluppano a partire dai concetti ora esposti, portano alla
dimostrazione del seguente enunciato, che è la controparte dell’enunciato 2.10.

Enunciato 2.11
Una funzione f sui numeri naturali, che sia rappresentabile in A, è ricorsiva
parziale.

Un ovvio corollario degli Enunciati 2.10 e 2.11 afferma che una funzione risulta
ricorsiva parziale e totale se e solo se esiste una fbf V, con variabili libere xb ...,
x„+b nella quale, per ogni numero k}, kn+i, valgano le seguenti condizioni:
1. Se/(£i, • ••, kn) = kn+1, allora >- 'V^1’ ’^"+1

2. -Vx„+1V‘.. a VxVXV1’-J"’x aV"=>x=y)

Mediante gli Enunciati 2.10 e 2.11 si può ora costruire, seguendo il procedimento
accennato qui di seguito, una fbf di ^<3/ che stabilisca la sua indimostrabilità. Si

10 Una relazione R è ricorsiva primitiva se e solo se la sua funzione caratteristica/^ data da

fR (x},..., x„) = if (xb


..., x„) g R then 1 else 0 è ricorsiva primitiva. In modo analogo viene
definita una relazione ricorsiva parziale.
Elementi essenziali di logica matematica 73

consideri dapprima la relazione R(u,v), che stabilisce che u è il numero di Godei di


una fbf V avente la variabile libera x, e che v è il numero relativo ad una
dimostrazione di "V" . Tale relazione è ricorsiva primitiva e può essere quindi
rappresentata mediante una bf WR, con variabili libere x e y che rappresentano,
rispettivamente, u e v. Si può allora costruire la bf Wl : \/y(—<WR). Wl stabilisce che
non esiste la dimostrazione di "V" , dove u è il numero di Godei di V. Ora, sia W2
la fbf Vy(—), dove m è il numero di Godei di Wl. Si deduce quindi che W2
afferma l’inesistenza della dimostrazione di quella bf che si ottiene dalla bf il cui
numero di Godei è m (ossia di Wl) e nella quale la variabile libera x venga
sostituita da m . Una fbf siffatta è W2 stessa, quindi W2 afferma la propria
indimostrabilità.
In modo analogo si può dedurre l’impossibilità di dimostrare la coerenza di
come teorema della stessa
La filosofia che ispira il ragionamento che ha condotto all’Enunciato 2.9 può
essere applicata ad una grande varietà di formalismi, allo scopo di mostrare i loro
limiti. Se ne vedranno ulteriori esempi nel Capitolo 7 e nell'Appendice 7.A questo
punto il lettore, anche se convinto della validità dell’Enunciato 2.9, potrebbe
ancora sospettare che la responsabilità di ciò sia da attribuirsi ad un difetto nella
costruzione di .ÉAofln altre parole, gli assiomi PAI, ..., PA9 potrebbero essere
troppo deboli, cosicché alcuni fatti veri dell’aritmetica potrebbero essere sfuggiti.
Si potrebbe quindi sperare di poter includere tutte le verità aggiungendo nuovi
assiomi. E stato però dimostrato che l’aritmetica è essenzialmente incompleta, ossia
che non vi è alcuna possibilità di estendere ÉPstf aggiungendo nuovi assiomi in
modo tale che risulti ancora coerente e che, per ogni fbf chiusa di "V, risulti valida
la •- Vo •- —iV Poiché le “verità aritmetiche” sono spesso necessarie, ad esempio,
nella pratica della programmazione, l’impatto negativo di questo fatto è molto
forte.

RIASSUNTO DEL CAPITOLO


In questo capitolo sono stati presentati i fondamenti della logica matematica,
rivolgendosi particolarmente al lettore privo di conoscenze relative a questa teoria.
Non si è inteso comunque fornire una trattazione completa di tale campo.
Si è introdotta la notazione di teoria formale come strumento per descrivere i
ragionamenti logici per mezzo di formule matematiche. Un primo e semplice
esempio di teoria formale è il calcolo delle proposizioni 9^, che introduce l’uso
delle connessioni logiche fra le frasi. Sono state poi descritte le teorie del
prim’ordine e il calcolo dei predicati, che consentono l’impiego di variabili,
funzioni, predicati e quantificatori. Le interpretazione delle teorie del prim’ordine
assegnano valori di verità alle loro frasi; i sistemi assiomatici vengono introdotti
allo scopo di derivare la verità di una teoria come teoremi.
74 Informatica teorica

L’aritmetica è il principale esempio di teoria del prim’ordine. È stata descritta


un’assiomatizzazione dell’aritmetica che segue le linee suggerite da Peano. È stata
poi presentata l’importante classe delle funzioni ricorsive sui numeri naturali,
mediante le quali si è mostrata l’intrinseca incompletezza della teoria, ossia
l’impossibilità di derivare come teoremi tutte le sue verità.

Note bibliografiche
La logica matematica è un campo importante della matematica che ha le sue radici
negli antichi scritti di Platone, Aristotele, Descartes, ecc. La maggiore spinta in
avanti verso l’attuale formulazione matematica è stata data tuttavia alla fine del
1800 e nella prima metà del 1900. Peano (1891) ha dato contributi fondamentali
soprattutto nella trattazione assiomatica dell’aritmetica. Godei (1930, 1931, 1934)
ha fornito importanti contributi sui sistemi completi e incompleti ed ha dimostrato
l’incompletezza dell’aritmetica. Church (1936) e Rosser (1936) hanno esteso
considerevolmente i risultati di Godei.
Alcuni testi classici che vengono suggeriti per una trattazione approfondita di
questa materia sono: Kleene (1952, 1967), Church (1956) e Mendelson (1964).
Alcuni capitoli sulla logica matematica sono spesso inseriti all’interno di testi
di informatica teorica. Si vedano ad esempio: Manna (1974), Lewis e
Papadimitriou (1981) e Manna e Waldinger (1985).
Si ricorda che, recentemente, alcuni argomenti di logica matematica più
complessi di quelli introdotti qui, hanno trovato rilevanti applicazioni
nell’informatica (Pnueli, 1979). Un esempio importante è dato dalle teorie di
ordine superiore. In contrasto con le teorie del prim’ordine, che, prendono il nome
dal vincolo che i quantificatori si possano applicare solo a varibili, ossia “oggetti
del prim’ordine”, le teorie di ordine superiore permettono di quantificare nomi di
funzioni e di relazione; inoltre, tali teorie consentono ai predicati di avere altri
predicati o simboli di funzione come argomenti. Hasenjager e Scholz (1961)
fornisce alcune nozioni fondamentali su questo argomento. Si noti che, sotto
un’opportuna semantica, è stato dimostrato che qualsiasi logica di ordine superiore
può essere “tradotta” in perdendo brevità e naturalezza (Montagne, 1965).
Infine, si sottolinea che recentemente sono stati sviluppati strumenti a supporto
delle prove formali in logica del prim’ordine. La maggior parte di tali strumenti,
quali Spass (Weidenbach et al., 2009) e Prover9 (McCune, 2005), utilizzano come
tecnica di dimostrazione la risoluzione (Gallier, 1986).
Capitolo 3

L’USO DEI MODELLI IN CAMPO SCIENTIFICO E


INGEGNERISTICO

Nella maggior parte dei campi dell’ingegneria, la fase di progetto si fonda sull’uso
di modelli, in quanto risulta spesso impossibile, o poco pratico, verificare se la
soluzione di un problema sia adeguata o meno, applicandola direttamente al mondo
reale. Ad esempio, nella maggior parte dei casi pratici, sarebbe assurdo costruire un
ponte senza prima effettuare determinate scelte di progetto fondate su un opportuno
modello del problema.
In alcuni casi l’ingegnere può usare modelli fisici', le osservazioni e le misure
su un ponte reale, anche se di dimensioni notevolmente ridotte, possono aiutare a
prevedere il comportamento del ponte che deve essere effettivamente costruito. I
modelli fisici non possono tuttavia risolvere tutti i problemi ingegneristici per una
serie di motivi:
a. Può risultare difficile, o molto costoso, costruire un modello fisico del
fenomeno che si sta studiando.
b. Un modello fisico può rivelarsi utile, dopo che tutte le principali decisioni di
progetto siano state prese, come strumento sperimentale per il controllo di
qualità. La costruzione di molti modelli fisici diversi, per esaminare
approfonditamente progetti differenti, risulterebbe inaccettabilmente costosa
nella maggior parte dei casi pratici. Di conseguenza, i modelli fìsici sono più
utili per la valutazione del progetto piuttosto che per la sua sintesi.
c. Può risultare difficile tradurre i risultati di esperimenti effettuati sul modello in
risultati validi per il caso reale.
d. Può risultare difficile ottenere un controllo accettabile sulla accuratezza delle
misurazioni effettuate sul modello fisico.
Il progetto può anche essere coadiuvato dall’uso di modelli formali. Un modello
formale opera su oggetti matematici che rappresentano le astrazioni delle entità
reali che devono essere modellate. I modelli formali consentono all’utente di
applicare il rigore del ragionamento matematico per dedurre le proprietà delle
entità modellate. Ad esempio, la struttura di un ponte e le forze ad esso applicate
possono essere descritte da un sistema di equazioni matematiche, la cui risoluzione
consente al progettista di conoscere in anticipo le intensità e le direzioni degli
sforzi interni.
I modelli formali richiedono fondamentalmente:
1. La formalizzazione del problema, ossia la traduzione del problema reale in
una notazione propria di qualche formalismo matematico.
78
Informatica teorica

2. La risoluzione del problema formalizzato mediante gli strumenti offerti dal


formalismo scelto.
3. L’interpretazione dei risultati ottenuti dal modello allo scopo di dedurre o
valutare le scelte di progetto.
Anche nel corso di questo processo si possono ovviamente verificare molti errori,
sia durante i passi informali 1. e 3. che durante i calcoli del passo 2. Per questo
motivo, la stesura di un progetto ingegneristico complesso richiede spesso un uso
attento di molti modelli di tipo diverso.
I modelli svolgono un ruolo essenziale in ogni campo della scienza; in questo
ambito, tuttavia, il loro impiego è generalmente più orientato verso
l’interpretazione della realtà piuttosto che verso la progettazione, come invece
avviene in campo ingegneristico. Un modello, in campo scientifico, racchiude le
conoscenze relative ad un determinato fenomeno; per questo motivo, esso è in
grado di astrarre da molti, sottili dettagli riflettendo solo alcune proprietà essenziali
del fenomeno sotto osservazione. È esattamente attraverso questo processo di
astrazione che il modello consente di dominare l’enorme complessità dei fenomeni
reali.
Un modello è adeguato se i risultati ottenuti, sperimentando o ragionando su
di esso, riflettono proprietà osservabili del fenomeno in corso di studio, ovviamente
entro i limiti di approssimazione accettabili. In altre parole, l’astrazione racchiusa
dal modello comprende esattamente tutte le proprietà del fenomeno che sono
interessanti per lo studio che si intende svolgere. Tutte le più note leggi fìsiche
(come le leggi del moto, dell’elettricità e della termodinamica) sono modelli di
fenomeni fisici che esprimono alcune astrazioni.
La Sezione 3.1 di questo capitolo analizza più approfonditamente questi
concetti con l’aiuto di semplici esempi. Nella successiva Sezione 3.2 si discute
l’uso e il ruolo dei modelli nell’informatica e lo si paragona all’uso e al ruolo dei
modelli in campi più classici dell’ingegneria e della scienza.

3.1 Esempi introduttivi

Esempio 3.1
Si consideri il problema seguente. In un’aia vi è un certo numero di polli ed un
certo numero di conigli. La loro somma è 20. Inoltre il numero totale delle zampe è
60. Quanti polli e quanti conigli vi sono nell’aia?
Uno studente che conosca l’uso delle equazioni probabilmente ragionerebbe
nel seguente modo: “Siano x e y, rispettivamente, il numero dei polli e dei conigli.
Le conoscenze relative agli animali nell’aia possono essere rappresentate dalle
seguenti equazioni
x + y = 20
2x + 4y = 60
79
L’uso dei modelli in campo scientifico e ingegneristico

La soluzione dell’equazione fornisce i risultati x = 10 e y = 10. Nell’aia vi sono


dunque 10 polli e 10 conigli”.
La risoluzione del problema sarebbe molto più difficoltosa per uno studente
non abitutato all’uso delle equazioni. La ragione di questa difficoltà risiede
essenzialmente nell’incapacità di astrarre dal problema contingente, ossia di
tradurre lo stesso in un’apposita notazione formale (le equazioni) dotata anche di
qualche strumento meccanico di risoluzione (la sostituzione delle variabili, ad
esempio).

Esempio 3.2
Si supponga che una mela matura cada dalla cima di un melo alto h metri. Si
chiede di calcolare il tempo t richiesto dalla mela per raggiungere il suolo.
La fisica elementare suggerisce una semplice astrazione del fenomeno che si
sta studiando. La mela può essere rappresentata da un punto materiale; la sola forza
applicata al punto è il suo peso. Secondo la legge della gravità di Newton, la mela
ha un’accelerazione g = 9.81 m/sec2. Poiché la velocità di partenza della mela è
nulla, la relazione fra h e t è rappresentata dalla seguente equazione.

À=--g-r2 (El)
2
L’equazione (El) mostra che t dipende solo da h. Da osservazioni pratiche,
tuttavia, è noto che ciò non vale, in generale, per ogni oggetto. Ad esempio, il
tempo ta richiesto da una mela è diverso dal tempo ts richiesto da un foglio di carta
per raggiungere il suolo dalla medesima quota h.
Per comprendere meglio le ragioni per cui si è giunti a questa apparente
contraddizione, bisogna analizzare attentamente il modello formale che è stato
impiegato e le astrazioni che esso comporta. In questo caso, infatti, si è
rappresentato l’oggetto che stava cadendo come un punto materiale soggetto solo
alla forza di gravità: nel modello formale, quindi, l’attrito dell’aria è stato
completamente ignorato. Le osservazioni pratiche mostrano che questa è un’ipotesi
ragionevole nel caso della caduta di una mela, ma produce risultati inaccettabili nel
caso di un foglio di carta.

Come si è avuto modo di osservare nell’introduzione di questo capitolo, i modelli
formali forniscono una visione astratta e semplificata della realtà; i risultati che si
ottengono ragionando sui modelli sono quindi a loro volta un’approssimazione dei
risultati osservabili nella realtà. Se la discordanza fra essi è troppo ampia, il
modello deve essere ripetutamente raffinato, fino a raggiungere
un’approssimazione soddisfacente (ossia un astrazione opportuna).
80
Informatica teorica

Esempio 3.3
Si supponga di voler progettare un paracadute. Sia hm la minima altezza per aprire
il paracadute e sia h la quota dell’aeroplano da cui il paracadutista si lancia. Si
desidera conoscere il massimo ritardo di sicurezza tm dopo il quale il paracadute
deve essere necessariamente essere aperto per garantire una discesa sicura.
L’equazione (El) può essere nuovamente usata come modello del fenomeno
fisico sotto esame nella fase iniziale del moto, ossia prima dell’apertura del
paracadute. Quindi tm deve soddisfare l’equazione:

h-hm=^-g-tm2 (E2)

Secondo quanto si è visto nell’Esempio 3.2, il valore di tm fornito dall’equazione


(E2) risulta certamente inferiore al massimo ritardo ammissibile. Tuttavia, dato che
lo scopo è progettare un paracadute sicuro, il modello può essere considerato
perfettamente adeguato.

3.21 modelli per l’informatica


L’astrazione e la costruzione di modelli giocano un ruolo fondamentale in ogni
branca della scienza e dell’ingegneria. Un ruolo ancora più essenziale viene svolto
nel campo dell’informatica.
In molte applicazioni, i programmi per calcolatori vengono progettati allo
scopo di sostituire procedure manuali. Ciò significa che i progettisti di sistemi
informatici devono “riprodurre” o “imitare” il comportamento “essenziale” della
procedura manuale mediante un programma per calcolatore: il programma è quindi
un modello di tale procedura manuale.
La produzione del software, tuttavia, non consiste solo nella programmazione,
così come i programmi per calcolatore non sono l’unico tipo di modello utilizzato
durante la progettazione di sistemi informatici. I principi generali dell’ingegneria
insistono, fra l’altro, sulla necessità di un’accurata analisi dei requisiti e di
un’accurata fase di progetto. Il risultato dell’analisi dei requisiti è il documento di
specifica che definisce esattamente le richieste che il sistema deve soddisfare. In
altre parole, questo è un modello che enfatizza il comportamento esterno del
sistema.
Analogamente, la fase di progetto produce un documento che specifica la
struttura del sistema, spesso articolandolo in moduli tra loro interconnessi. Questo è
un altro tipo di modello, che pone l’accento sulla struttura complessiva del sistema,
astraendo dai dettagli interni di ciascun modulo.
In conclusione, dunque, la produzione di sistemi informatici procede
attraverso una serie di modelli. Tradizionalmente questi modelli vengono definiti
informalmente, attraverso il linguaggio naturale: molto spesso perciò risultano
incompleti, incoerenti ed ambigui. I programmi software sono la sola eccezione:
81
L’uso dei modelli in campo scientifico e ingegneristico

essi devono essere formali, ossia devono conformarsi alla sintassi e alla semantica
del linguaggio di programmazione scelto.
L’evoluzione dell’ingegneria informatica e dell’ingegneria del software in
particolare, invece, si dirige sempre più verso l’impiego di linguaggi e modelli
formali in tutte le fasi della progettazione. La formalità è richiesta per migliorare
non solo l’affidabilità e la facilità di manutenzione dei programmi, ma anche per
consentire l’impiego di strumenti automatici di supporto alla progettazione. Molti
esempi di tali linguaggi formali sono già apparsi in letteratura, ma il loro impiego
sistematico nelle applicazioni pratiche richiede ancora molta ricerca e
sperimentazione per adeguarli ad esigenze in continua e spesso disordinata
evoluzione. È importante notare che i fondamenti di questi numerosi formalismi
sono radicati nei concetti teorici che verranno analizzati in questo testo. I concetti
che verranno studiati qui forniranno, perciò, soprattutto la base su cui si può
fondare la definizione e l’analisi di rigorosi linguaggi formali impiegabili nella
definizione di modelli per le varie attività dell’ingegneria informatica.
L’astrazione e la costruzione di modelli svolgono dunque un ruolo essenziale
anche nella comprensione e nel progetto dei sistemi informatici, in maniera molto
simile a quanto si è visto per altri campi della scienza e deH’ingegneria. Ad
esempio, allo scopo di comprendere il funzionamento, o anche di progettare, una
nuova automobile, è perfettamente inutile modellarla come un insieme di molecole.
L’utente dell’automobile potrebbe modellarla come insieme di blocchi costruttivi
di alto livello (astrazioni), quali i freni, il volante, il pedale della frizione ecc. il cui
interesse risederebbe principlamente nelle prestazionii funzionali (spazio di frenata,
raggio di curvatura ecc.). Il progettista invece dovrebbe servirsi di un modello più
dettagliato, che evidenzi, ad esempio, i meccanismi che collegano il volante con le
ruote; quest’ultimo modello sarà a sua volta definito in termini di un altro insieme
di astrazioni, in modo da poter garantire le prestazioni funzionali richieste
dall’utente.
Analogamente, per poter usare un programma di elaborazione testi su di un
personal computer, l’utente può considerare il calcolatore come una macchina per
scrivere sofisticata, dotata di una tastiera, di uno schermo video e di una stampante,
in grado di fornire un insieme specifico di comandi per impaginare e manipolare
testi. Il progettista del programma di elaborazione testi, da parte sua, richiede
invece una visione del sistema molto più dettagliata, anche se non ha ancora la
necessità di rappresentarlo a livello di circuiti hardware. Questo argomento verrà
ora sviluppato in maggior dettaglio.
Si consideri la CPU (ossia l’unità di controllo) di un moderno calcolatore.
Essa è un insieme di un enorme numero di dispositivi a semiconduttore: alcuni di
essi sono del tipo mostrato in Figura 3.1. Il circuito di Figura 3.1 è un modello
formale di un dispositivo reale: è formato da diodi, transistori e resistor! ideali.
Questi componenti ideali possono essere rappresentati da opportune equazioni che
legano le tensioni e le correnti ai loro terminali. A seconda del tipo di analisi che il
progettista desidera svolgere sul modello, queste equazioni possono esibire diversi
82
Informatica teorica

Figura. 3.1 Rappresentazione a livello circuitale di un elemento di CPU.

di precisione: un diodo, ad esempio, è talvolta rappresentato dal diagramma di


Figura 3.2a e talvolta da quello di Figura 3.2b. La soluzione dell’insieme completo
di equazioni per il circuito di Figura 3.1 permette di ottenere la tensione di uscita
Vo come funzione della tensione di ingresso, per i = 1, 2, 3 (entro
l’approssimazione del modello impiegato).
Come si può immaginare, però, l’analisi di un calcolatore reale per mezzo di
un modello elettrico sarebbe praticamente impossibile a causa dell’enorme quantità
di circuiti coinvolti. Un’ulteriore livello di astrazione conduce a riconoscere che
solo due livelli di tensione sono effettivamente interessanti durante l’analisi del
circuito precedente. Si definiscano questi livelli di tensione come alto e basso,
rispettivamente (il valore effettivo della tensione alta o bassa dipende dalla
tecnologia usata per l’implementazione). Quando tutte le tensioni di ingresso sono
alte, l’uscita è bassa; se almeno un’ingresso è basso, allora l’uscita è alta.
Astraendo quindi dalla tensione reale, si può supporre che U e Vo abbiano entrambi
un valore convenzionale “0” e “1”. Il comportamento del circuito può allora essere
modellato mediante un modello completamente differente: la cosiddetta porta
NAND, mostrata in Figura 3.3, che rappresenta la funzione booleana NAND, ossia
NOT AND (si veda la Sezione 2.2).
83
L’uso dei modelli in campo scientifico e ingegneristico

n 1

se---- •—
V

I (a/
W

Figura 3.2 Due modelli diversi di un diodo.

Figura 3.3 Una porta NAND.

Si noti che il cambiamento di modello implica anche un cambiamento degli


strumenti formali necessari per studiarne le proprietà. L’analisi del comportamento
di una CPU, descritta mediante circuiti del tipo di quello mostrato in Figura 3.1,
comporta l’uso di equazioni differenziali sul dominio dei numeri reali, mentre lo
studio del comportamento di una CPU, vista come insieme di blocchi costruttivi
come quelli mostrati in Figura 3.3, richiede l’impiego teN algebra booleana. In
quest’ultimo caso si è in grado di rappresentare una CPU come insieme di
equazioni contenenti gli operatori AND, OR e NOT su variabili Booleane. Il
numero di variabili coinvolte sarebbe però inimmaginabile: è richiesta dunque
un’astrazione maggiore.
Ulteriori astrazioni, con conseguenti cambiamenti di modello, condurranno
infine alla classica descrizione della CPU di un calcolatore mediante un insieme di
registri, ciascuno dei quali costituito da una sequenza finita di flip-flop, in cui
ciascun flip-flop è un dispositivo con due stati stabili chiamati, rispettivamente, “0”
e “1”. Molte operazioni, come load, store, add, shift ecc. vengono definite sulle
parole di memoria. L’aspetto fondamentale di questa descrizione è che il
progettista, quando lavora sulla CPU, può servirsi semplicemente della definizione
84
Informatica teorica

di queste operazioni elementari, senza prestare attenzione, ad esempio, alle leggi


del moto degli elettroni all’interno dei diodi durante un’operazione.
Questa rappresentazione della CPU a livello di linguaggio assemblatore può
essere appropriata se lo scopo è quello di programmare in linguaggio assemblatore,
o di scrivere un compilatore che traduca programmi sorgente scritti in un
linguaggio ad alto livello in programmi scritti in linguaggio assemblatore. Non è
decisamente appropriata, invece, se lo scopo è quello di scrivere applicazioni in
qualche linguaggio ad alto livello, come Pascal o Java. In tal caso si può
considerare la CPU come un processore in grado di manipolare dati caratterizzati
da un tipo (intero, carattere, reale ecc.) e il cui repertorio di istruzioni comprenda la
valutazione di espressioni complesse, gli assegnamenti, le frasi condizionali, i cicli,
le chiamate a sottoprogramma con passaggio di parametri ecc. In altre parole, il
processore viene visto come un processore astratto in grado di interpretare
direttamente le istruzioni del linguaggio di alto livello.
Finora si è visto che l’importanza della costruzione di modelli e
dell’astrazione ha, nel campo dell’informatica, due aspetti. Prima di tutto lo
studioso di informatica necessita di modelli per rappresentare un sistema di calcolo
a diversi livelli di astrazione, per comprenderlo, per progettarlo o anche per usarlo.
Ciò è molto simile a quanto avviene in ogni altro campo della scienza e
dell’ingegneria. Secondariamente, l’essenza intrinseca del lavoro dello studioso di
calcolatori è la costruzione di modelli: da modelli puramente astratti, come nel caso
dei linguaggi formali per la specifica dei requisiti di sistema, a modelli
concretamente eseguibili, come nel caso dei programmi.
I modelli usati nelle branche tradizionali dell’ingegneria sono basati su un
insieme di strumenti formali abbastanza assestati, in particolare il calcolo
differenziale e integrale. Molta più flessibilità è richiesta invece dallo studioso di
informatica nell’uso di strumenti formali come supporti per la fase di
progettazione. Il precedente esempio relativo alla CPU ha già mostrato come il
formalismo da usare sia soggetto a drastici cambiamenti quando si aumenta il
livello di astrazione. Si illustrerà successivamente come il progetto e l’analisi dei
sistemi informatici suggerisca l’impiego di ulteriori modelli completamente
differenti.
Nonostante le diversità di tali modelli, essi si possono classificare, anche se
non sempre in modo netto, in due principali categorie: modelli operazionali e
modelli descrittivi. I primi descrivono un sistema dettagliando le operazioni
necessarie a raggiungere gli obiettivi stabiliti, cioè formalizzano il funzionamento
del sistema che si sta progettando. Sono spesso basati sul concetto di stato del
sistema e definiscono le operazioni in grado di descriverne l’evoluzione. I secondi,
invece, descrivono le proprietà o requisiti che si vogliono ottenere dal sistema, o
simmetricamente, le proprietà che si vogliono evitare. Tali modelli formalizzano,
cioè, in modo astratto i comportamenti ammissibili del sistema, svincolandosi dal
“come” si ottengano.
85
L’uso dei modelli in campo scientifico e ingegneristico

Figura 3.4 Rappresentazione intermedia nella costruzione di un’ellisse.

Analizziamo meglio questa differenza attraverso due esempi: il problema di


descrivere un’ellisse e un problema più propriamente informatico, l’ordinamento.
Si supponga di voler descrivere un’ellisse, cioè il luogo dei punti in un piano la
cui somma delle distanze da due punti fissi dati, detti fuochi, è costante. Questa
descrizione può essere modellizzata sia operazionalmente che in modo descrittivo:
nel primo caso si vuole spiegare come ottenere un’ellisse dati i due fuochi, mentre
nel secondo ci si limita a enunciare formalmente le caratteristiche che si vogliono
ottenere. Una descrizione operazionale è quindi il seguente:
1. Si prendano due punti A e B (i fuochi) e una corda di lunghezza /;
2. Si fissino le estremità della corda rispettivamente in A e in B;
3. Si collochi una penna nella corda nel punto C come riportato in Figura 3.4;
4. Mantenendo la corda sempre tesa, si muova la penna in senso orario fino a
tornare al punto di partenza.
Questa procedura descrive formalmente un’ellisse, senza dame le caratteristiche,
ma spiegando come poterne costruire una.
L’ellisse può anche essere descritta però attraverso le proprietà di equidistanza
dai fuochi con una formula matematica, nel seguente modo:

dove 2a è la lunghezza dell’asse maggiore e 2b la lunghezza dell’asse minore.


Questa seconda rappresentazione è descrittiva: infatti, non dice nulla sul come
ottenere un’ellisse, ma descrive le proprietà dei punti di coordinate (x, y) del piano
che la rappresentano.
Si consideri ora un esempio più propriamente informatico: il problema di
ordinare in modo crescente una sequenza S data. Tale problema può essere definito
in modo descrittivo, formalizzando le proprietà di cui deve godere la sequenza S
dopo essere stata ordinata o in modo operazione descrivendo i passi che portano
alla sequenza ordinata.
Descrittivamente, si può definire Tordinamento come il problema di trovare
una permutazione di S tale per cui Vi 5[i] <5[i + 1]. Tale descrizione non dice
nulla su come trovare la soluzione del problema, ma ne identifica le caratteristiche
in modo preciso.
86
Informatica teorica

Il modello operazionale invece descrive i passi che portano alla soluzione; un


possibile esempio è il seguente:
1. Si calcoli il minimo in 5 e lo si porti al primo posto;
2. Si calcoli il minimo fra gli elementi rimasti di 5 e lo si porti al secondo posto;
3. ...;
cioè, in modo più generale;
- per ogni i in [1..N] dove N è il numero di elementi di S,
calcola il minimo tra gli elementi dall’z-iesimo all’N-esimo in 5 e mettilo al
posto i.
Anche in questo esempio, appare evidente la differenza tra i due diversi tipi di
modelli; mentre il modello operazionale del problema dà una descrizione su come
ottenere il risultato voluto, quello descrittivo si limita a definire le caratteristiche
che deve avere la soluzione desiderata.
In letteratura esistono diversi modelli formali che possono essere classificati in
modo più o meno netto nell’ambito di queste categorie. Nei capitoli successivi, tra
essi, verranno analizzati i formalismi fondamentali per l’informatica.
In particolare, nel Capitolo 4 verranno introdotti gli automi, che rappresentano
un classico esempio di modello operazionale. Nel Capitolo 5 si descriveranno le
grammatiche, che, con un termine un po’ grossolano, potremmo classificare come
un modello descrittivo-costruttivo; come suggerisce il termine, questo formalismo
è orientato alla descizione di come devono essere composte le frasi di un
linguaggio, intendendo il termine “linguaggio” in maniera estremamente ampia..
Nel Capitolo 6 si introdurranno due esempi di modelli descrittivi, detti anche
denotazionali. Infine nel Capitolo 7 si mostrerà l’utilità della logica matematica
nella modellizzazione informatica e dei sistemi complessi ed eterogenei in
generale. Tutti i modelli formali verranno presentati integrando definizioni e
proprietà matematiche con esempi che ne illustrino l’utilità pratica.

RIASSUNTO DEL CAPITOLO


Questo capitolo rappresenta un’introduzione alla Parte II del testo. Infatti, esso
motiva, anche attraverso alcuni esempi, l’importanza dei modelli nell’ingegneria e,
più specificatamente, nell’informatica. Inoltre, il capitolo introduce la
classificazione dei modelli formali usati nella specifica e nell’analisi di sistemi
informatici, dividendoli in operazionali e descrittivi. I classici modelli che verranno
presentati nei capitoli successivi saranno classificati seguendo questo schema.
Capitolo 4

AUTOMI

Come anticipato nel Capitolo 3, gli automi rappresentano un classico esempio di


modello operazionale. Essi permettono di descrivere un sistema mostrando gli stati
in cui esso si può trovare e come sia possibile passare da uno stato all’altro. Più
precisamente, con il termine automa si intende un modello astratto che mostra
l’evoluzione di un sistema come sequenza di configurazioni dei suoi stati, in
seguito agli ingressi ricevuti. Per questo motivo un automa viene anche definito in
letteratura come macchina a stati discreti.
In questo capitolo vengono descritte ed analizzate diverse famiglie di automi e
le loro proprietà. Viene mostrata l’utilità pratica di tali formalismi in informatica e,
più in generale, nella progettazione di sistemi ingegnerisici attraverso esempi di
vario tipo.
Il capitolo è articolato in diverse sezioni, ciascuna delle quali introduce un
particolare tipo di automa. In particolare, le Sezioni 4.1, 4.2 e 4.3 descrivono tre
diverse famiglie automi di crescente “potenza descrittiva”. La Sezione 4.4
introduce il concetto di non-determinismo e ne mostra l’effetto sui modelli
introdotti nelle sezioni precedenti; infine, nella Sezione 4.5 verranno introdotte le
reti di Petri, una particolare famiglia di macchine a stati intrinsecamente legata al
concetto di non-determinismo, che ha trovato ampia applicazione nell’ingegneria
del software, nell’automazione e in svariati altri campi dell’ingegneria.

4.1 Automi a stati finiti


Gli automi a stati finiti (AF) sono probabilmente il modello più usato in campo
informatico; grazie alla sua semplicità e intuitività esso trova ampia applicazione
anche in molti altri settori dell’ingegneria e non solo. Intuitivamente, un AF
rappresenta un sistema che può trovarsi in un numero finito di stati diversi. Come
conseguenza di qualche ingresso, che può assumere anche esso un insieme finito di
possibili valori, l’AF effettua una transizione da uno stato all’altro.
Un grafo offre una rappresentazione di un AF di immediata evidenza intuitiva:
i nodi indicano gli stati e gli archi indicano le transizioni. Ad esempio, il grafo di
Figura 4.1 rappresenta un automa che modella le attività di una persona nel corso
di una settimana. In questo caso gli stati rappresentano i giorni della settimana e
l’attività ad essi correlata, mentre gli archi, cioè le transizioni che connettono uno
stato all’altro, rappresentano il passaggio da un giorno al giorno successivo. Il
grafo in Figura 4.2 rappresenta invece un automa che modella il comportamento di
un semplice interruttore della luce; in questo caso gli stati sono solamente due, luce
spenta e luce accesa, e la transizione tra essi avviene a seconda di come si gira
l’interruttore.
88
Informatica teorica

Lunedi, Martedì,
lavoro Giorno lavoro
in ufficio successivo in ufficio

Giorno
successivo

Giorno Giorno Mercoledì,


successivo lavoro
successivo
in ufficio

Giovedì,
lavoro
in ufficio

venerdì,
Giorno
lavoro
successivo
in ufficio

Divertimento Giorno
con oh amici
successivo

Sabato,
Giorno pulizia della
successivo casa e del
giardino

Figura 4.1 Un AF che modella le attività nel corso della settimana.

Gira l'interruttore a sinistra

Gira l'interruttore a destra

Figura 4.2 Un AF che modella un interruttore della luce.


89
Automi

Costruiamo ora una definizione formale di AF sulla base della precedente


presentazione intutiva del modello.

Definizione 4.1
Un automa a stati finiti è una tripla (Q, I, 8\ dove
1. Q è un insieme finito di stati.
2. Z è un insieme finito di simboli d’ingresso.
3. 5 è la funzione di transizione, che può anche essere una funzione parziale:
8: gX I^Q

È immediato stabilire una corrispondenza con la precedente rappresentazione
grafica: i nodi rappresentano gli stati, cioè nel grafo ci sarà un nodo per ogni
elemento di Q, mentre un arco etichettato con z e diretto da q a q', con q, q'e Q
indica che 8 definita su q e i come 8(z/, z) = q’. Nel seguito, il termine AF verrà
usato indifferentemente per indicare la definizione matematica e la sua
rappresentazione grafica.
Estendiamo ora la funzione 8, che rappresenta la transizione da uno stato ad un
altro, in modo da rappresentare una sequenza di transizioni', in altre parole, se 8
indica, a partire da uno stato, quale sarà lo stato in cui si troverà l’automa in
conseguenza della ricezione di un simbolo in ingresso, introdurremo il simbolo 8 *
per definire lo stato in cui si troverà l’automa, partendo da un dato stato, dopo aver
ricevuto zero o più simboli in ingresso in sequenza. Quindi 8* definisce
l’evoluzione dell’automa, partendo dallo stato q, come conseguenza di una
sequenza qualunque di valori di ingresso x e Z*. Formalmente, 8*: Q X Z*—> Q è
tale che

5*(q, e) = q &*(q, xi) = 3(3*(#, x), z), x e Z*, z eI


Come si può notare la definizione viene data in modo ricorsivo, partendo dalla
funzione base 8. In particolare, 8*(z/, e) = q indica che. se ci si trova in uno stato q e
si legge la stringa vuota, ossia non si riceve alcun ingresso, si rimane nello stato q.
5*(q, xz) = 8(8*(zy, x), z) invece definisce 8* quando la stringa in ingresso contiene
almeno un simbolo, cioè si può vedere come una generica stringa x seguita dal
simbolo z. In questo caso la sequenza a partire da uno stato q porterà l’automa nello
stato che si raggiungerebbe con ingresso i, a partire dallo stato che si raggiunge da
q con x (cioè 8*(g, x)). Ad esempio, nel caso dell’automa di Figura 4.2, 8*(“luce
spenta”, “gira l’interruttore a sinistra”, “gira l’interruttore a destra”) è “luce
spenta”.
Tra i molteplici impieghi degli automi a stati finiti, si trova anche la teoria
della commutazione. Ad esempio, il flip-flop S-R può essere modellato come l’AF
mostrato in Figura 4.3. Il flip-flop ha due stati stabili, indicati convenzionalmente
con 0 e 1, e due ingressi S e R. Ad ogni ciclo di clock, viene attivato uno dei due
ingressi. Se viene attivato S, allora lo stato diventa 1 ; se viene attivato R, lo stato
Informatica teorica

Figura 4.3 Un flip-flop S-R.

diventa 0, indipendentemente dallo stato di partenza. Si noti che il comportamento


del flip-flop è descritto dall’automa indipendentemente da come verrà realizzato il
dispositivo fisico corrispondente. L’automa è perciò un modello astratto del flip-
flop S-R.
ESERCIZI

4.1 II commutatore che seleziona il canale di un televisore (di vecchio tipo) può
essere ruotato sia in senso orario che in senso antiorario per scegliere fra tre
possibili canali. Nel primo caso i canali A, B e C vengono scelti in
quest’ordine. Si costruisca un modello di questo dispositivo usando un AF.
Si arricchisca poi la soluzione proposta al fine di descrivere gli odierni
telecomandi dei televisori, senza necessariamente entrare nei dettagli, ma
confrontando tale modello con l’AF precedentemente.
4.2 Si descriva il seguente gioco per mezzo di un AF. Due giocatori, detti A e B,
tirano a turno un dado. Ogni volta gioca per primo il vincitore del lancio
precedente, ad esempio A-, successivamente gioca B. Siano x e y, con
l< x, y < 6 i punteggi, rispettivamente, di A e di B. A vince se x > y,
altrimenti vince B. Si trascuri l’inizio e la fine del gioco.
4.3 * Si descriva un registro a scorrimento ad otto bit per mezzo di un AF. (Nota-.
l’esercizio richiede conoscenze elementari di hardware).

4.1.1 Automi a stati finiti come riconoscitori di linguaggi


Per descrivere sistemi mediante modelli, è spesso utile estendere la definizione di
AF con le notazioni di stato iniziale e stato finale, per indicare, rispettivamente, lo
stato in cui si trova inizialmente l’AF quando comincia a funzionare e lo stato in
cui si dovrebbe trovare l’AF dopo aver terminato le sue operazioni, in modo tale da
poter verificare se il suo funzionamento è conforme agli obiettivi. In questo modo
un AF viene impiegato per modellare il riconoscimento di una sequenza finita di
ingressi, rappresentino essi azioni, eventi o, in generale, oggetti, ossia per stabilire
se tale sequenza gode o meno di alcune proprietà
Ad esempio, si supponga di voler descrivere la sintassi degli identificatori
ammessi nel linguaggio di programmazione Pascal: “ un identificatore Pascal è
91
Automi

Figura 4.4 Un riconoscitore degli identificatori Pascal. Gli archi etichettati


“Lettera” sostituiscono n-uple di lati etichettati rispettivamente
A,B, ■ ■■■, Z, a, b, ...z. Analogamente, i lati etichettati con “Cifra”
sostituiscono n-uple di lati etichettati rispettivamente 0, 1, 9.

indicato da una sequenza di lettere e cifre. Il primo carattere deve essere una
lettera”. Un semplice modello può essere costruito mediante un AF in cui ciascun
carattere della sequenza di ingresso produca una transizione dell’AF e una
sequenza di ingresso corretta provochi l’evoluzione dell’AF da uno stato iniziale ad
un determinato stato finale. Tutto ciò è descritto, intuitivamente, dal grafo di Figura
4.4, in cui q0 è lo stato iniziale e qF è lo stato finale. Allo scopo di rendere
chiaramente visibili gli stati iniziale e finale nella rappresentazione grafica, lo stato
iniziale q0 viene indicato da una freccia entrante nel nodo corrispondente e che non
proviene da alcun altro nodo, mentre gli stati finali vengono indicati da un doppio
cerchio.
La rappresentazione grafica della Figura 4.4 modella la struttura degli
identificatori Pascal ammissibili, servendosi di un ipotetico processo di
riconoscimento. L’automa raggiunge il suo stato finale qF, partendo dal suo stato
iniziale qo, se e solo se la sequenza in ingresso è un identificatore Pascal corretto,
fri tal caso, si dice che TAF riconosce, o accetta, la sequenza di ingresso. La
sequenza di ingresso non è riconosciuta, ossia è rifiutata, se l’automa non
raggiunge il suo stato finale partendo dal suo stato iniziale. La tabella seguente
mostra alcuni esempi di sequenze accettate e rifiutate dall’AF mostrato in Figura
4.4.
Accettato_____ Rifiutato______
ALPHA 512
ALPHA29 9AB3C
AB3C 17
92
Informatica teorica

Si noti che l’automa di Figura 4.4 rifiuta (ossia non accetta) stringhe che iniziano
con una cifra, poiché nessun arco etichettato da una cifra esce dallo stato iniziale,
ossia la funzione 8 non è definita in q0 per un ingresso i e {0, 1, 2, ...9}; in altri
casi la funzione 8* potrebbe invece essere definita in corrispondenza di una stringa
in ingresso, ma non “portare” l’automa in uno stato finale.
Formalizziamo ora il concetto di riconoscimento di stringhe mediante AF,
introdotto intuitivamente attraverso l’esempio degli identificatori in Pascal, fri
generale, le stringhe accettate da un AF sono tutte e sole le stringhe che, se lette a
partire dallo stato iniziale, portano con una sequenza di transizioni a uno stato
finale.

Definizione 4.2
Un accettare (riconoscitore) a stati finiti è una quintupla (Q, I, 8, qn, F), dove Q, I
e 8 sono definite come nella Definizione 4.1, q0 e Q è detto stato iniziale qF cz Q è
detto insieme degli statifinali (o stati di accettazione).

Si noti che, mentre intuitivamente si era parlato di uno stato iniziale e uno stato
finale, la Definizione 4.2 introduce un insieme di stati finali. Si tornerà su questo a
breve, dopo aver definito che cosa si intende per stringa accettata (o riconosciuta)
da un AF.

Definizione 4.3
Una stringa x e /* è accettata (o riconosciuta) da un accettare a stati finiti se e
solo se 8*(^o, x) e F. Il linguaggio accettato (o riconosciuto) da un accettare a stati
finiti è l’insieme delle stringhe accettate dall’automa. Se A è un riconoscitore a stati
finiti, L(A) indica il linguaggio accettato da A.

Nelle pagine seguenti, il termine automa a stati finiti e la sigla AF verranno usati
anche nel caso di accettori a stati finiti, ogni volta che l’esistenza degli stati iniziale
e finali risulterà evidente dal contesto. Vengono ora presentati altri esempi di
accettori a stati finiti.

Esempio 4.1
Una sequenza di bit rappresenta un numero dispari se la cifra meno significativa è
un 1. Ciò può essere modellato mediante l’AF mostrato in Figura 4.5, supponendo
che l’automa legga le cifre di ingresso dalla più alla meno significativa.
93
Automi

Figura 4.5 Un accettare a stati finiti dei numeri dispari.

L’AF si trova inizialmente nello stato q<h dove rimane finché continua a leggere
dall’ingresso la cifra 0. Appena legge un 1 una transizione fa passare l’automa allo
stato finale qx, poiché 1’1 letto potrebbe essere l’ultima cifra della stringa in
ingresso e, quindi, la sequenza rappresenterebbe un numero dispari, cioè una
sequenza che deve essere accettata dall’automa. Se dopo 1’1 vengono lette altre
cifre, con la cifra 1 l’AF resta in q-,, seguendo lo stesso ragionamento fatto per il
primo 1 letto, mentre se legge uno 0 una transizione riporta l’AF in q(i', infatti, se
l’ultima cifra letta è uno 0, l’automa non si deve trovare in uno stato finale.

Esempio 4.2
L’AF di Figura 4.6 accetta sequenze di 0 e 1 che cominciano con almeno due 0 e
terminano con almeno due 1. Formalmente, l’automa accetta il seguente
linguaggio:
L = {0”xlm| n > 2, m > 2, x e {0, 1}*}

0 1

Figura 4.6 Un accettare a stati finiti che riconosce stringhe che iniziano con
almeno due 0 e terminano con almeno due 1.
94
Informatica teorica

L’AF dallo stato iniziale ha definito solo una transizione verso qx quando legge 0 e,
analogamente da qi, ha la sola transizione etichettata con 0 verso g2. Questo
cammino da q0 a q~, è l’unico definito e serve per verificare che i primi due simboli
della stringa letta siano due 0.
In q-2., se in ingresso c’è uno 0, l’AF resta in g2, mentre, con un 1, si muove in
q3, poiché quell’1 potrebbe essere il primo dei due 1 necessari per terminare una
sequenza accettata. Da q3, se in ingresso c’è uno 0, l’automa toma in g2, poiché la
sequenza di 1 è stata interrotta, mentre, con un 1, si muove verso lo stato finale g4.
L’AF in q3 toma in g2 con uno 0, poiché la sequenza di almeno due 1 finali è stata
interrotta, mentre rimane in #4 con un 1; infatti il linguaggio da riconoscere deve
terminare con almeno due 1 e non con esattamente due 1.

ESERCIZI

4.4 Si costruisca un AF che riconosca il linguaggio L di alfabeto {0,1}, definito


in modo tale che x e L se e solo se x contiene una sequenza di almeno tre 0
consecutivi.
4.5 Si costruisca un AF che riconosca il seguente linguaggio:
£7= {anb3mc\n > l,m > 0}
Una stringa di £1 consiste di un numero pari di a, maggiore di zero, seguito
da un numero di b multiplo di 3, seguito a sua volta da un’unica c.
4.6 Si costruisca un AF che riconosca il linguaggio ■ £2 = l\ , dove £1 è definito
nell’esercizio precedente (si veda la Sezione 1.5 per la definizione di L ).
4.7 Si costruisca un AF che accetti il seguente linguaggio £:
£={0, l}* 00-{0, l}*-ll-{0,1}*
Una stringa di £ contiene almeno due 0 consecutivi, seguiti, anche non
immediatamente, da due 1 consecutivi.

4.1 .2 Gli automi a stati finiti come traduttori di linguaggi


Molto spesso bisogna costruire un modello per processi che producono un’uscita
come conseguenza di qualche ingresso. I programmi per calcolatore ne
costituiscono il caso più familiare. Si consideri ad esempio la seguente specifica
informale di un programma:

Un testo in ingresso contiene solo lettere minuscole e il carattere speciale $. Il


programma deve emettere il testo in uscita, convertendolo in base alle
seguenti regole:
- La prima lettera dopo un $ va convertita in maiuscolo;
- Due occorrenze consecutive di $ vanno convertite in un carattere “ritorno
carrello” (CR);
95
Automi

Inoltre non più di due occorrenze consecutive di $ possono apparire nel testo
in ingresso, esclusa la sequenza di tre $ che indica la fine dell’ingresso. Il
carattere $ non viene riportato sul testo in uscita; la sua presenza nel testo in
ingresso ha esclusivamente la funzione di carattere di controllo per le
conversioni.

Questa specifica lunga e informale può essere formalizzata da un AF, dotato di


uscita, che descriva come viene prodotto il testo in uscita in conseguenza del
riconoscimento del testo in ingresso. Intuitivamente, l’AF con uscita mostrato in
Figura 4.7 è un modello astratto del programma desiderato. Qui gli archi vengono
etichettati con una coppia (a,y) (indicata con a/y), dove a è un carattere di ingresso
e y è una stringa di uscita, eventualmente vuota.
Molte specifiche informali, lunghe e molto spesso ambigue, incomplete ed
incoerenti possono essere convertite in una notazione più concisa, precisa e
formale, usando automi a stati finiti con uscita. Un caso tipico è rappresentato dai
problemi di traduzione da un linguaggio ad un altro. Per sottolineare questo
concetto, gli automi a stati finiti con uscita vengono spesso chiamati trasduttori a
stati finiti.
I trasduttori finiti (TF), introdotti informalmente dall’esempio di Figura 4.7,
sono definiti formalmente nel modo seguente.

Figura 4.7 Un AF con uscita.


Informatica teorica

Definizione 4.4
Un trasduttore a stati finiti (TF), ossia un automa a stati finiti con stati iniziale e
finale e dotato di uscita, è una 7-upla (Q, I, ò, qlt, F, O, p), dove Q, I, 5, qo, F sono
definiti come nella Definizione 4.2. O è un insieme finito di simboli di uscita e p è
la funzione di uscita (eventualmente parziale).

Le funzioni 8 e p sono definite sul medesimo insieme di definizione. L’AF ottenuto


da un TF omettendo O e p viene chiamato accettare soggiacente al TF.

Nella rappresentazione grafica di un TF, il valore di p viene aggiunto come nuova
etichetta sugli archi. Siano ti(q, z) = q' e p(p», z) = y e (?*; allora i/y etichetta un arco
che connette q con q'. Analogamente a quanto fatto per la funzione di transizione
8, è conveniente estendere la definizione di p sul dominio Q X /*, ottenendo la
funzione p *. Intuitivamente p * produce la stringa in uscita come conseguenza della
lettura, non di un simbolo in ingresso, ma di una sequenza di simboli in ingresso.
Formalmente, p*: Q X I* O* è definita nel modo seguente:
p*(z?, e) = e;
p*(g, xz) = p*(<7, x) ■ p(8*(<?, x), z);
cioè se l’automa è in uno stato q e non legge nessun ingresso, allora non produce
alcuna uscita (p*(z?, e) = e), mentre se l’automa legge almeno un simbolo z,
preceduto da una quasiasi stringa, eventualmente vuota, x, allora l’automa produce
in uscita quello che produrrebbe da q leggendo x (p*(g, x)) seguito da ciò che
produrrebbe dallo stato che raggiunge da q leggendo x (8*(zj, x)), avendo i come
ingresso.

Definizione 4.5
Sia dato un trasduttore a stati finiti T. La traduzione if. O* associata a T è così
definita:

r/x) = P*(tfo, x) se e solo se 8*(^0, x) e F



T definisce quindi una traduzione solo sul linguaggio riconosciuto dal suo
riconoscitore soggiacente.

Esempio 4.3
Si supponga di voler costruire il modello di un robot dotato di due “mani”. Il robot
riceve da un nastro trasportatore in ingresso una sequenza di piatti e tazze, e opera
nel modo seguente all’atto del ricevimento di un oggetto.
97
Automi

1. Se entrambe le mani sono libere, il robot prende l’oggetto con la mano


sinistra, sia che si tratti di un piatto che di una tazza.
2. Se il robot ha già un oggetto nella mano sinistra, il nuovo oggetto ricevuto
deve essere di tipo diverso; in tal caso l’oggetto viene preso con la mano
destra.
3. Il robot depone la tazza sul piatto e deposita il “pezzo” così assemblato in un
nastro trasportatore in uscita.
4. Quando termina la sequenza di oggetti in ingresso, entrambe le mani del robot
devono essere libere.
La Figura 4.8 mostra una raffigurazione informale del robot, mentre la Figura 4.9
mostra un TF che modella il robot; in questo caso, t sta per tazza, p sta per piatto ed
a indica l’insieme costituito dalla tazza e dal piatto.

Figura 4.8 Un robot che assembla piatti e tazze.


98
Informatica teorica

Figura 4.9 Un TF che modella il robot di Figura 4.8.

L’automa si può anche considerare come un trasduttore di stringhe', esso infatti


accetta stringhe appartenenti all’insieme {tp, pt}* e le traduce in stringhe
appartenenti ad {a}*. Più precisamente, una sequenza di n stringhe di 2 caratteri (tp
o pt) viene tradotta in una sequenza di n occorrenze del carattere a.

Esempio 4.4
Una rappresentazione interna binaria comunemente usata per codificare i numeri
interi è la cosiddetta rappresentazione “in complemento a due”. Con questa tecnica
gli interi positivi vengono rappresentati dal loro consueto valore binario preceduto
dal bit di segno posto a 0, mentre la negazione di un numero positivo è ottenuta
complementando ciascun bit e sommando 1 al numero così ottenuto.
Vi è un altro modo equivalente di effettuare la negazione di un numero m: si
scandisce la rappresentazione binaria dalla cifra meno significativa a quella più
significativa. Le cifre vengono copiate identicamente in uscita fino al primo 1
(compreso) che si incontra. Da questo punto in poi, ogni cifra viene copiata
complementata.
La Figura 4.10 illustra un TF che modella la negazione di un numero
rappresentato in complemento a due, utilizzando la tecnica appena spiegata.
99
Automi

Figura 4.10 Un TF che effettua la negazione di un numero rappresentato in


complemento a due, scandendo la striga di bit in ingresso da destra
a sinistra.

ESERCIZI
4.8 Si progetti un TF che:
a. Riconosca il linguaggio L = Aab)nccc(ba)in | n > 1, m > 0}
b. Traduca ogni stringa del linguaggio nella stringa dove n e m
sono gli stessi numeri di cui al punto a e, come al solito, [m/2\ indica il
troncamento intero di m/2.
4.9 Si progetti un TF che scriva un $ ogni volta che riconosce, all’interno del
testo in ingresso x e {a, b,0, 1} *, tre b consecutive con almeno uno 0 nei
precedenti cinque caratteri. Ad esempio, abbabbb(ìa\bbb produrrebbe $,
a()bbbb\cM\ab\abbb(Abbb produrrebbe $$$, etc...
4.10 Si costruisca un programma che simuli il comportamento di ogni automa a
stati finiti A usato come riconoscitore di linguaggi. Il programma deve
ricevere in ingresso una descrizione dell’automa da simulare e la stringa x da
riconoscere e deve produrre in uscita un messaggio che afferma se x e L(A).
Suggerimento: A può essere descritto da ima struttura dati costituita da:
a. Una lista degli stati di A, il cui primo elemento è lo stato iniziale.
b. Una lista di stati finali.
c. Una lista di simboli di ingresso.
d. Una matrice rettangolare di dimensioni |(?| X \I\ rappresentante la
funzione 5.
e. La simulazione del comportamento di A consiste nella lettura, carattere
per carattere, della stringa in ingresso x e nella determinazione, ad ogni
passo, del nuovo valore dello stato corrente di A, che è inizializzato a q0.
100
Informatica teorica

Terminata la lettura della stringa, è necessario verificare se lo stato


corrente appartiene all’insieme F degli stati finali.

4.1.3 Proprietà degli automi a stati finiti


I modelli formali sono descrizioni rigorose di determinati fenomeni. La creazione
di un modello formale richiede una profonda osservazione del fenomeno dato. Il
modello formale risultante, infatti, rappresenta, in termini non ambigui, precisi e
concisi, ciò che si è compreso del fenomeno. Come molti formalismi, anche i
modelli possono essere “manipolati”: si possono derivare proprietà dai formalismi
e attraverso tali proprietà si può giungere ad una visione sempre più profonda del
fenomeno che si sta modellando. Ad esempio, in algebra lineare si dimostra che un
sistema di n equazioni lineari in n incognite ammette una e una sola soluzione se e
solo se il determinante della matrice dei coefficienti delle incognite è diverso da 0;
da questa proprietà si ricavano anche algoritmi per il calcolo della soluzione. Per
questo motivo è utile analizzare le proprietà e le caratteristiche dei modelli che si
hanno a disposizione. A tal fine, questa sezione illustra qualche proprietà
fondamentale degli automi a stati finiti.
Le proprietà descritte dai Teoremi 4.1, 4.2 e 4.3 illustrano i tipi di linguaggi
che possono essere riconosciuti dagli accettori a stati finiti, dando una risposta alle
seguenti domande: tali linguaggi sono finiti o sono infiniti? Qual è la loro struttura?

Teorema 4.1
Sia dato un automa a stati finiti A = {Q, I, ò, q0, F), dove Q ha cardinalità n. Il
linguaggio riconosciuto da A è non vuoto se e solo se A accetta una stringa x con
|x| < n.
" i
Dimostrazione
La parte relativa al “se” è banale, infatti se A accetta una stringa x con |x| <n, x
appartiene al linguaggio riconosciuto da A, che quindi è non vuoto. Per quanto
concerne invece la parte relativa al “solo se”, sia x e L(A) una stringa tale che
|x| < [y|, per ogni y e L(A), ossia x sia dia lunghezza minima tra le stringhe
riconosiute da A. Se |x| < n, allora il risultato è dimostrato. Si supponga dunque per
assurdo che |x| > n. Poiché A effettua una mossa ad ogni lettura di un simbolo di
ingresso, A deve passare attraverso lo stesso stato più di una volta durante il
riconoscimento di x. Si può dunque trovare uno stato q in Q e scrivere x come
X|X2x3, x2 f s, in modo tale che:
- 8*(^0, xi) = q-,
- 8*(q, X2) = q;
- &*(q, x3) e F.
101
Automi

Ciò implica che 8*(^0, Xix3) e F e quindi X]X3 g L(A). Viene perciò contraddetta la
supposizione che |x| < |y|, per ogniy e L(A). Infatti, poiché x2 * e, |xix3| < |x|.

Teorema 4.2
Sia dato un automa a stati finiti A = {Q, I, S, qo, F), dove Q ha cardinalità n. Il
linguaggio riconosciuto da A è infinito se e solo se A accetta una stringa x con
n < |x| < 2n.

Dimostrazione
Parte relativa al “se”.
La dimostrazione del Teorema 4.1 indica che per x e L(A), |x| > n implica
l’esistenza di uno stato q e delle stringhe xlrx2rx3 con x2 # s tali che x = x^2x3 e
S*(/7o, Xi) = q, ò*(q, x2) = q, ò*(q, x3) e F. Quindi 8*(^0, x;x™ x3) e F per ogni
m > 0 e infinite stringhe del tipo Xj x'" x3 appartengono a L(A).
Parte relativa al “solo se”.
Se L(A) è infinito, esiste una stringa x e L(A) tale che |x| > n. Si decomponga x
come xix2x3, in modo analogo a quello visto nella precedente parte relativa al “se”.
Se si ottiene che |xix3| < n e che |x2| < «, allora |xi x2x3| < 2n e la tesi è dimostrata.
Altrimenti, si supponga dapprima che |X]X3| > n. Se |xix3| < 2n la tesi è dimostrata
poiché X]X3gL(A). Nell’altro caso la medesima decomposizione viene applicata a
xjx3 e così via, fino a che si trova una stringa y tale che y = TiTùT con 8*(^o, Li) =
qh 8*(^i, y2) = qi, &*(qi, x3) g F e |yiT3| < n- Se [y2| <«, la tesi è dimostrata.
Altrimenti y2 può ancora essere decomposto come ziz2z3, in modo tale che z2 s e
che 8*(^i^iz3)=^i. Questo procedimento continua fino a che non si ottiene un y2
tale che 8*^!, y2') = q\Q \y2\ < n.

I teoremi precedenti sono basati sul fatto che un AF può presentare cicli nella sua
rappresentazione grafica. Se ciò non avviene, il linguaggio accettato è finito, in
quanto, poiché con una stringa in ingresso può far passare l’automa una sola volta
in ciascuno dei suoi stati, tutte le stringhe che appartengono al linguaggio
riconosciuto dall’automa sono lunghe al più come il numero di stati meno 1 (e
l’insieme delle stringhe di lunghezza limitata su un insieme finito è finito).
L’eventuale presenza di cicli giustifica anche il seguente teorema, che descrive
un’importante proprietà generale dei linguaggi riconosciuti dagli AF e che è alla
radice dei Teoremi 4.1 e 4.2.
102
Informatica teorica

Teorema 4.3 (Pumping Lemma)


Sia A un AF. Esiste allora una costante k per la quale, se x g L(A) e |x| > k, x può
essere scritta come ywz, dove 1 < |w| < k e yw'z g L, per ogni i > 0.

Il Pumping Lemma fonisce una condizione necessaria ma non sufficiente sulla
struttura dei linguaggi che vengono riconosciuti da AF. Intuitivamente esso,
sottolineando l’essenza dei Teoremi 4.1 e 4.2, afferma che, poiché, se una stringa x
è lunga almeno quanto il numero degli stati dell’automa A che l’accetta, x viene
riconosciuta da una sequenza di transizioni in A che passa per un ciclo, allora tutte
le stringhe che si ottengono da x, ripetendo la sottostringa di x che “attraversa” il
ciclo di A, sono pure accettate da A. Così pure sono accettate quelle stringhe
ottenute da x cancellando qualsiasi sua sottostringa che attraversi un ciclo di A.
La dimostrazione formale del Pumping Lemma è lasciata al lettore per
esercizio. Suggerimento’. A: è il numero di stati di A.
Il Pumping Lemma ha anche diversi risvolti pratici che verranno analizzati a
breve.
Consideriamo ora un’altra classe di problemi concernenti gli AF. Prima di
tutto, si osservi che esiste più di un AF in grado di riconoscere un linguaggio dato.
Ad esempio, la Figura 4.11 mostra un AF che riconosce esattamente lo stesso
linguaggio riconosciuto dall’AF mostrato in Figura 4.4, cioè il linguaggio degli
identificatori Pascal.
Sorgono quindi naturalmente due domande. Come si può decidere se due AF
riconoscono lo stesso linguaggio? Si può definire una “forma canonica” per gli
automi, ricorrendo ad esempio all’automa a numero minimo di stati, e determinare
una procedura che trasformi ogni AF in tale forma canonica? Sì mostrano ora
alcuni teoremi in grado di rispondere a queste due domande, cominciando dalla
seconda.

Lettera cifra

Lettera

Figura 4.11 Un AF che riconosce gli identificatori Pascal.


103
Automi

Teorema 4.4
Sia ^l’insieme dei riconoscitori a stati finiti che riconoscono un dato linguaggio
L <z /*. Sia A = (q ,1,5 ,q0,F^~un automa a stati minimi di -s/, ossia tale che

\Q | < |2| per ogni A = (Q, I, 5, q0, F). Aliorari è unico a meno di un
isomorfismo, ossia di un riassegnamento dei nomi agli stati.

Dimostrazione
Per dimostrare questo teorema bisogna analizzare alcune interessanti proprietà
algebriche dei linguaggi e degli AF. Si osservi innanzitutto che è possibile
associare una relazione naturale di equivalenza EL ad ogni linguaggio Z.c /*. xEyy
vale per ogni x e y e I* se e solo se x e y appartengono o non appartengono
entrambe a L. E immediato verificare che EL è effettivamente una relazione di
equivalenza su /*. Si modifichi ora EL in una nuova relazione RL, definita in modo
tale che xR^y se e solo se, per ogni z in /*, xz e yz appartengono, o non
appartengono, entrambe a L. E altrettanto immediato comprendere come anche RL
sia una relazione di equivalenza e che RL <z EL, ossia RL implichi EL. Inoltre, RL è
una congruenza destra rispetto alla concatenazione, ossia xRy implica xzRiyz per
ogni z e I*.
Si consideri ora un automa a stati finiti A e si definisca la relazione RA su /*, in
modo tale che xRyy se e solo se 5*(^0, x) = 5*(^o, y), ossia se x e y conducono al
medesimo stato partendo da q0. Si intuisce facilmente che RA è una relazione di
equivalenza ed è anche una congruenza destra, così come si era visto per RL. Inoltre
xRyy implica evidentemente xRL^Ay, poiché A raggiunge, per x e per y, il medesimo
stato, appartenente o meno a F. Si ottiene dunque il seguente enunciato.

Enunciato 4.5
Per ogni automa a stati finiti A, la relazione RA è un raffinamento della relazione
Rl(a), ossia Ra cz Rjyi). Inoltre, poiché Vindice di RA, ossia il suo numero di classi
di equivalenza, è pari a \Q\, per ogni linguaggio L accettato da qualche AF, segue
che Rl è di indice finito. In generale, indice^Ruff) < indiceiRff

Si verifica anche la validità dell’enunciato inverso.

Enunciato 4.6
Si consideri un qualunque linguaggio L, tale che RL sia di indice finito. E allora
possibile costruire un AF AR = (Qr, I, 8r, q0R, FR} tale che L = L(AR) e
I2à| = indice(RL).
104
Informatica teorica

Allo scopo di dimostrare l’enunciato, si pongono le seguenti definizioni:


- eÀ={[x]|xe/*};
- 8À([x], z) = [xz] per ogni [x](=QR, i e /;
- q<sR = [e];
- Fr= {[x]|xe L}.
È facile verificare che
- La definizione precedente è ben posta poiché, se [x] = [y] per qualche x e y e
/*, allora 8r([x], z) = 8À([y], z) per ogni z, dal momento che RL è una congruenza
destra e [x] e [y] appartengono o non appartengono entrambi a FR.
- L(AR) = L, poiché 8*R ([e],x) = [x], che appartiene aFR se e solo sex e L

La coppia di Enunciati 4.5 e 4.6 è nota anche con il nome di teorema di Myhill-
Nerode.
A questo punto è possibile completare la dimostrazione del Teorema 4.4,
osservando che AR è l’automa a stati minimi A , a meno di un eventuale
riassegnamento dei nomi agli stati. Infatti, sia q uno stato di A . Deve allora
esistere un x tale che 5* (qQ ,x) = q, poiché altrimenti q potrebbe essere rimosso
da Q senza alterare L(a ), contraddicendo così la minimalità di A . In
corrispondenza di tale x, si indichi q con il simbolo [x] e si verifichi la coerenza
della definizione attraverso il fatto che se 8*(qQ,x) = 8* (q(i,y) allora xR~y e
quindi xRyy, a causa dell’Enunciato 4.5. Si ha quindi [x] = [y].
La minimalità di A = AR si deduce allora dal fatto che
|2r| = indice(Ri) < indice(RA) = |0| per ogni automa a stati finiti A che riconosce
L.

L’automa a stati finiti AR è chiamato riconoscitore (accettare) canonico di L poiché
è unico, a meno di una ridefinizione del nome degli stati. La dimostrazione del
Teorema 4.4, tuttavia, afferma 1’esistenza di un automa minimo senza fornire
un’esplicita procedura per costruirlo. Allo scopo di ottenere una dimostrazione
costruttiva del Teorema 4.4, si può procedere nel modo seguente. Si consideri un
qualunque automa a stati finiti A.
1. Si eliminino dapprima tutti gli stati inutili da Q. Uno stato q si dice inutile se
non esiste un x per cui 8*(g0, x) = q oppure se non esiste un y per cui
8*(g, y) e F. È facile costruire un algoritmo che effettui tale eliminazione.
Tale costruzione viene lasciata per esercizio al lettore.
2. Si definisca poi una relazione di equivalenza D su Q in modo tale che qDq se
e solo se, per ogni x, entrambi o nessuno dei due stati 8*(q, x), 8*(q',x)
appartengono a F. D può essere facilmente verificata per mezzo di un
algoritmo, poiché, come al solito, se per qualche q e qualche q' esiste un x per
105
Automi

il quale 5*(<7, x) e Fe8*(<f,x) £ F, allora esiste anche un x' con la medesima


proprietà e con |x'| < |g|.
3. A questo punto, si definisca l’automa A' = {Q', I, 8', q0', F') con Q' = {[<?]},
ossia, l’insieme delle classi di equivalenza di Q rispetto a D, 8'([7], a) =
[ò(q,a)],eF' = {[?]|?eJ],
Come semplice esercizio si può verificare che
- A' è ben definito poiché la definizione di 8' non dipende dalla scelta del
particolare q in [<7].
- Z(J')=Z(J).
- Q'< indice(RLUA Infatti, per assurdo, si supponga che Q > indice(RuA))-
Allora, poiché in base all’Enunciato 4.5 Ra è un raffinamento di Ript),
dovrebbero esistere [<7] [<7'] e x y tale che xRuaV e 8'*([<7o], x) = [<7],
8'*([<7o],y) = [<?']. Supponendo [9] [q’] (ossia che non valga la qDq'\
dovrebbe esistere un w tale che 8*(<7o, xw) = ò*(q, w) e F e 8*(<70, yw) £ F, o
viceversa. Ciò contraddice tuttavia xRLijyy, poiché Rl è una congruenza destra.

Esempio 4.5
Si consideri l’AF di Figura 4.11. È immediato comprendere che q\Dq2 poiché
8*(<7i,x) e F, così come 8*(<72, x) per ogni x e {A, ..., Z, 0, 1, ..., 9}*. Tuttavia
q^Dqx non vale poiché 8 *(<70, s) £ F, mentre 8*(<7b e) e F. Quindi, applicando la
costruzione precedente, si ottiene l’AF di Figura 4.12, che coincide con l’AF di
Figura 4.4 a meno di un cambiamento di nomi per gli stati.

Figura 4.12 Risultato della minimizzazione dell’AF di Figura 4.11.


106
Informatica teorica

ESERCIZI
4.11 Si minimizzi l’AF soggiacente al TF di Figura 4.7.
4.12 Si minimizzi l’AF di Figura 4.13.
4.13 Si suggerisca una procedura per minimizzare i TF e si costruisca un
programma che la implementi
4.14 Si minimizzi il TF di Figura 4.7.
107
Automi

Corollario 4.7
Dati due AF Ai e A2, risulta L(AA = L(A2) se e solo se gli automi minimi associati
e A2 sono identici, a meno di una ridefinizione dei nomi degli stati.

11 Teorema 4.4 e il Corollario 4.7 sono esempi che mostrano come l’applicazione
delle proprietà matematiche di un modello possa fornire risultati di interesse
pratico. Minimizzare il numero di stati di un automa, infatti, corrisponde spesso a
minimizzare le risorse necessarie alla realizzazione del sistema da esso modellato.
Analizziamo ora le proprietà di chiusura dei linguaggi riconosciuti dagli AF
rispetto alle comuni operazioni insiemistiche, la concatenazione e l’operazione
Il concetto di chiusura rispetto a un’operazione OP si estende in modo naturale dal
comune concetto di chiusura matematica, che definisce un insieme X chiuso
rispetto a un’operazione OP, se, presi due qualsiasi elementi xi e x2 di X, Xi OP x2 è
ancora un elemento di X. Ad esempio, è noto che i numeri naturali sono chiusi
rispetto alla somma e alla moltiplicazione, ma non rispetto alla sottrazione e alla
divisione, mentre i numeri interi sono chiusi rispetto alla somma, alla sottrazione e
alla moltiplicazione, ma non rispetto alla divisione. Per una famiglia di linguaggi L,
tale nozione si estende in modo banale. Diremo, cioè, che la classe L è chiusa
rispetto a un’operazione OP se e solo se per ogni coppia di linguaggi Li e L2 in L,
Li OP L2 è ancora in L.
Analogamente al problema della minimizzazione degli AF, lo studio delle
operazioni applicate a linguaggi non ha un puro interesse matematico, ma è di
notevole utilità nella pratica. Si consideri ad esempio il seguente problema. Sia Li il
linguaggio dei documenti Word Mac-compatibili e L2 il linguaggio dei documenti
Word Windows-compatibili. Quali sono i documenti Word non compatibili con
Windows? E l’insieme dei documenti compatibili con entrambi i sistemi operativi?
Per rispondere a queste domande basta calcolare il complemento di L2 nel primo
caso e l’intersezione tra Li e L2 nel secondo.
Esistono molti altri esempi in cui le operazioni sui linguaggi si dimostrano
utili per risolvere problemi pratici. Questa considerazione rende molto interessante
studiare le proprietà di chiusura della famiglia di linguaggi riconosciuti da una
classe di automi rispetto a tali operazioni. I seguenti teoremi analizzeranno quindi
tale problema per gli AF.
In particolare i Teoremi 4.8, 4.9 e 4.10 mostrano che la classe di linguaggi
accettati dagli AF è chiusa rispetto all’intersezione, al complemento e all’unione.
Altre proprietà di chiusura verranno dimostrate successivamente.

Teorema 4.8
La classe dei linguaggi accettata dagli automi a stati finiti è chiusa rispetto
all’intersezione.
108
Informatica teorica

Traccia di dimostrazione
Siano dati due generici AF Ar = (Qi, E, 6b <701, Fi) e A2 = (Q2,12, 82, q02, F2). Si
supponga che I\= I2 = T, si noti che tale ipotesi non comporta nessuna perdita di
generalità, infatti qualora gli alfabeti su cui sono definiti Ai e A2 fossero diversi,
poiché la funzione di transizione non deve essere necessariamente totale, si
possono considerare i due automi come definiti su I\ u I2. Siano poi Q\, Q2
disgiunti; anche questa ipotesi non lede la generalità della dimostrazione, poiché
può essere banalmente garantita cambiando il nome agli stati di imo dei due. Si
consideri quindi l’AF A = (Q, I, 8, q0, F) costruito nel modo seguente.

- Q = Qi * Qi,
- qo = (qoi, qoì);
~ F= {(q',q")\q' e Fh q" e F2};
- q2), a) = )qi, q2) se e solo se 81(^1, a) = qi , ò2(q2, a) = q2 .
Per dimostrare che A riconosce effettivamente il linguaggio L(A|) n L(A2) è
sufficiente applicare un naturale ragionamento per induzione alla funzione 6*.

Intuitivamente, l’AF che riconosce il linguaggio L(At) n L(A2) è ottenuto
simulando il funzionamento parallelo di Ai e A2, accoppiando i due automi
attraverso il prodotto cartesiano tra gli stati e definendo la funzione di transizione
di A solo dove è definita sia in Ai sia in A2. Il seguente esempio illustra la
costruzione del Teorema 4.8.

Esempio 4.6
Consideriamo l’automa Ai = (0b/b 61,901,-Ai), con Qi = {s0, 5J, I\ = {a,b,c},
qoi = So, F\ = {s$} e 81 definita nel seguente modo bi(s0,a') = s0, 8i(50, Z>) = 5b
61(50,c)=Si, 61(51,a) = Sd, 51(s1,b)=sI, 61(5/,e) = sh rappresentato graficamente in
Figura 4.14(a), e l’automa A2 = )Q2,12, 82, 902, F^, con Q2 = {t0, fj, I2 = {a, b, c},
q02 = io, F2 = {f0} e 82 definita nel seguente modo 62(f0, c) = f0, 62(f0, b) =
62(fb c) = fb 62(/|, a) = f0, rappresentato graficamente in Figura 4.14(b).
I due automi rispettano le ipotesi enunciate nella traccia della dimostrazione
del Teorema 4.8: A = I2 e Q\ e Q2 sono disgiunti. Analizzando gli automi, possiamo
dedurre che Ai riconosce il linguaggio L| = {x e {a, b, c}* | x = ya con
y e {a, b, c} *} e A2 riconosce il linguaggio L2 = {x e {a, b, c} * | x = x1x2...xn con
x; = c * oppure x,- = bc *a}.
Intuitivamente, appartengono all’intersezione quelle stringhe su {a, b, c} che
rispettano i vincoli imposti sull’ordine dei letterali da L2 e che finiscono con ‘a’
come imposto da Lx. Possiamo quindi costruire l’intersezione in modo preciso
secondo lo schema proposto precedentemente, ottenendo l’automa rappresentato in
Figura 4.15.
109
Automi

Figura 4.15 Automa intersezione degli automi riportati in Figura 4.14.

Teorema 4.9
La classe dei linguaggi accettata dagli automi a stati finiti è chiusa rispetto al
complemento.
HO
Informatica teorica

Traccia di dimostrazione
Si consideri l’AF A=(Q,I,8,q^F). Si costruisca dapprima un AF A', aggiungendo
un nuovo stato q ad A in modo tale che la funzione di transizione di A ' conduca a
q ogni volta che è indefinita in A. Inoltre, si imponga che l’automa rimanga in q
per ogni simbolo di ingresso. Con questa operazione si rende totale la funzione di
transizione di A e la corrispondente 8*. Si completa la dimostrazione mostrando
che l’AF che accetta L(A) = I* -L(A) si ottiene da ri', semplicemente scambiando
fra loro stati finali e stati non finali.

Si noti che l’operazione che rende totale la funzione di transizione di un automa
prima di complementarlo, aggiungendo lo stato q, chiamato stato pozzo, è
necessaria. Infatti, l’idea sfruttata dall’operazione di complemento è quella di
invertire gli stati finali con quelli non finali, invertendo cioè l’accettazione con il
rifiuto quando viene letta una stringa. Tale filosofia funziona se la stringa in
ingresso viene letta completamente dall’automa di partenza A. Infatti, in tal caso, se
la lettura termina in uno stato finale di A, terminerebbe in uno stato non finale di A'
e viceversa. Se però durante la lettura della stringa in ingresso A si blocca in
qualche stato, perché la funzione di transizione non è definita per il valore in
ingresso che si sta considerando, sia A sia A ' non riconoscono la stringa. Rendere
totale la funzione di transizione permette quindi di superare questo problema,
poiché in un automa completo qualsiasi stringa in ingresso viene letta fino alla fine.
Illustriamo anche questa costruzione attraverso un esempio.

Esempio 4.7
Si consideri l’automa A = (Q, I, 8, q<j, F), rappresentato graficamente in Figura
4.16. Tale automa riconosce il linguaggio L composto dalle stringhe sull’alfabeto
{0, 1} che contengono esattamente un ‘1’. Qualora ci limitassimo a invertire gli
stati finali e non finali, senza analizzare la totalità della funzione di transizione,
otterremmo un automa identico ad A, con l’unica differenza che risulterebbe
F’= {q0}. L’automa così ottenuto riconosce solamente la stringa vuota e le stringhe
composte da soli ‘0’, cioè solo parzialmente il linguaggio complemento di L. In
particolare tutte le stringhe che portano A a bloccarsi nello stato q\, cioè quelle
composte da più di un ‘1’, non verrebbero riconosciute. Questo è dovuto al fatto
che A non è completo, infatti 8(#b 1) non è definita. Per ottenere una funzione di
transizione totale è sufficiente aggiungere a Q uno stato pozzo q2, in cui si rimane
per qualunque simbolo dell’alfabeto, e definire la transizione da q\ a q2 con H’ in
ingresso, ottenendo l’automa in Figura 4.17. Se si invertono poi gli stati finali e
non in tale automa, si ottiene l’automa che riconosce il complemento di L.
111
Automi

Figura 4.16 Rappresentazione grafica dell’automa dell’Esempio 4.7.

Figura 4.17 AF completo equivalente all’AF in Figura 4.16.

Teorema 4.10
La classe dei linguaggi accettata dagli automi a stati finiti è chiusa rispetto
all’unione.

Dimostrazione

Si considerino due AF ztj e zt2. Allora L = L(Aj) u L(A2) = L(A1) n L(A2). Quindi,
secondo quanto affermato dai Teoremi 4.8 e 4.9, L è accettato da un AF.

ESERCIZI

4.15 Si completi la dimostrazione del Teorema 4.8 mostrando che gli AF definiti
nelle rispettive costruzioni accettano effettivamente L(AA n L(A2) e L(A),
rispettivamente.
4.16 Si fornisca un procedimento per costruire un AF in grado di riconoscere
l’unione di due linguaggi riconosciuti da due AF. Si svolga l’esercizio nei
due seguenti modi:
112
Informatica teorica

1. Adattando la costruzione presentata per l’intersezione;


2. Applicando le leggi di De Morgan secondo quanto indicato dalla
dimostrazione del Teorema 4.10.

Gli automi a stati finiti sono anche chiusi rispetto alla concatenazione e
all’operazione Intuitivamente, per costruire l’automa che riconosce la
concatenazione dei linguaggi Li e L2, riconosciuti rispettivamente dagli automi Ai e
A2, basta costruire un automa A in cui Ax e A2 sono “in cascata”. Per quanto
riguarda la “stella” di Kleene, invece, è sufficiente creare un ciclo sui cammini
dell’AF che portano all’accettazione. Si daranno maggiori dettagli di questa
costruzione nella Sezione 4.4.
Torniamo ora ad analizzare il Pumping Lemma, che aiuta a capire i limiti
espressivi dei linguaggi riconosciuti da AF. Esso, infatti, dà una condizione
necessaria sulla struttura dei linguaggi che possono essere riconosciuti da AF.
Questo suggerisce che esistano molti linguaggi, che, non possedendo quella
struttura, hanno bisogno di un modello diverso che li descriva. Si consideri ad
esempio il linguaggio L = {d'b'"\ n > 0}. Se L fosse riconosciuto da un AFA
dovrebbe rispettare la condizione enunciata dal Pumping Lemma, cioè dovrebbe
esistere una costante k per la quale, se x e L e |x| > k, x può essere scritta come
ywz, dove 1 < |w| < k eyw'z & L, per ogni i > 0.
Cerchiamo quindi di scomporre una generica stringa di L come appena descritto.
Esistono tre possibili scomposizioni
- x = ywz con w = a e r > 0: se così fosse anche a + krbn dovrebbe appartenere a
L Vk>0;
- x = ywz con w = br e r > 0: se così fosse anche anbs + dovrebbe appartenere a
L Vk>0;
- x = ywz con w = abs e r, s > 0: se così fosse anche d'~\dbs)kbn~s dovrebbe
appartenere a L 'dk > 0.
In tutti i casi si ottiene una contraddizione e dunque L non è un linguaggio
riconoscibile da AF. Si noti che questo modo di utilizzare il Pumping Lemma è
molto comune; tuttavia serve solo a dimostrare che un linguaggio non è
riconoscibile da un AF, poiché esistono linguaggi che soddisfano il Pumping
Lemma senza essere riconoscibili con AF.
Si osservi anche che la struttura del linguaggio L considerato precedentemente
suggerisce già intuitivamente che esso non sia riconoscibile da un AF. Infatti L =
{anbn\ n > 0} ha una struttura che richiede di contare e ricordare il numero di a lette
prima di leggere una b. Questo tipo di conteggio richiede una memoria illimitata e
non può quindi essere effettuato con un AF, poiché essi contano usando gli stati
che sono per definizione finiti. Nella sezione successiva introdurremo un nuovo
modello in grado di superare questa limitazione.
113
Automi

4.2 Gli automi a pila


La precedente applicazione del Pumping lemma al linguaggio L = {anbn\ n > 0}
dimostra l’impossibilità di riconoscere alcuni linguaggi con gli AF; in particolare,
gli AF hanno il limite di non poter contare una quantità di simboli non conosciuta a
priori e quindi diventano inutilizzabili in molti casi di interesse pratico. Questo
limite degli AF evidenzia quindi la necessità di introdurre nuovi e più potenti
modelli.
Ad esempio, si consideri nuovamente il robot dell’Esempio 4.3, che assembla
tazze con piatti; se il robot ha già in mano un piatto e ne riceve un altro, esso non è
in grado di accettarlo e si ferma. Tale robot può essere migliorato dotandolo di una
pila (inizialmente vuota). Se il robot ha già in mano un piatto (rispettivamente, una
tazza) e riceve un altro piatto (un’ altra tazza), allora può deporre il nuovo oggetto
sulla cima della pila, aspettando di ricevere una tazza (un piatto). Si supponga di
poter deporre sulla pila una quantità illimitata di oggetti, senza che ciò provochi
problemi. Quando entrambe le mani sono libere e si riceve un piatto (tazza), se la
pila contiene tazze (piatti), allora il robot ne prende una dalla cima della pila e
unisce i due oggetti nel solito modo. Quando il robot si ferma, entrambe le mani
devono essere libere e la pila deve risultare vuota, per assicurare che sia stato
ricevuto il medesimo numero di piatti e di tazze. Si noti che, in ogni istante, la pila
non contiene contemporaneamente tazze e piatti (si veda la Figura 4.18).
Se si suppone che la pila possa contenere un numero illimitato di oggetti,
nessun automa a stati finiti può modellare questo nuovo robot. A causa del loro
numero limitato di stati, gli automi a stati finiti possono distinguere solo fra un
numero finito di configurazioni diverse. D’altra parte, è intuitivamente chiaro che
una configurazione del robot con, ad esempio, 157 piatti sulla pila, è diversa da una
configurazione con 158 piatti sulla pila, poiché nel primo caso il robot si ferma
dopo aver ricevuto esattamente 157 tazze (se le sue mani sono inizialmente libere)
mentre nel secondo no.
L’aggiunta della pila permette quindi di gestire anche dei sistemi che hanno
un numero illimitato di possibili configurazioni e consente di modellare
quest’ultimo robot più perfezionato. Intuitivamente, quindi, si considera un
modello a stati, come gli AF, dotato in più di una memoria “a pila”, che può essere
letta e scritta e che influenza, con il suo contenuto, le transizioni nella macchina a
stati sottostante. Tali automi vengono chiamati automi a pila (AP), dal momento
che si tratta di AF arricchiti di ima memoria ausiliaria strutturata come ima pila. Gli
oggetti possono essere inseriti ed estratti dalla pila secondo una politica last-in
first-out (LIFO), ossia l’ultimo simbolo inserito in pila è il primo che viene tolto.
114
Informatica teorica

Figura 4.18 Un robot che unisce tazze e piatti usando una pila.

Definizione 4.6
Un automa a pila è una 6-upla IQ, I, T, 8, q0, Zo) dove
1. Q è un insieme finito di stati.
2. Z è un insieme finito di simboli di ingresso.
3. T è un insieme finito di simboli, tale che T n I = 0, detti simboli ausiliari (o
simboli della pila).
4. 8 è la funzione di transizione (eventualmente parziale):
8: Q X (I u {s}) X T Q X r*
tale che se per qualche q ed A ò(q, s, A) è definita, allora ò(q, i, A) è indefinita
per ogni i.
5. q0 e 2 è lo stato iniziale.
6. Zo e T è il simbolo iniziale della pila, ossia l’unico simbolo che appare
inizialmente nella pila.

Prima di definire formalmente il comportamento di un AP, cerchiamo di spiegarlo
in modo intuitivo. Si ritorni dapprima al comportamento dell’AF. La lettura della
stringa di ingresso effettuata dall’AF può essere realizzata da un dispositivo ideale
dotato di una testina di lettura. Inizialmente, la testina di lettura è posizionata
all’inizio della stringa di ingresso, che si suppone scritta su di un nastro-, q0 è lo
stato iniziale del dispositivo. Ad ogni passo la testina legge un nuovo carattere e il
dispositivo passa allo stato ò(q, i), dove q è lo stato precedente ed i è il simbolo
letto, cioè passa allo stato definito dalla funzione di transizione a partire dallo stato
115
Automi

x e r

Dispositivo di controllo

Figura 4.19 Descrizione di un AF come macchina di analisi di stringhe.

q, quando in ingresso viene letto il simbolo i. La funzione di transizione non è però


necessariamente totale; perciò, se Ò(q, i) è indefinita, la macchina si ferma; in caso
contrario prosegue e quando (e se) la stringa di ingresso è stata completamente
letta, se essa si trova in uno stato finale, la stringa viene accettata, altrimenti viene
rifiutata. La Figura 4.19 illustra schematicamente il dispositivo idealizzato.

ESERCIZIO

4.17 Si estenda il modello precedente per definire i trasduttori a stati finiti.

Un AP è semplicemente un AF arricchito di una memoria ausiliaria strutturata


come una pila (vedi Figura 4.20). Durante l’evoluzione dell’AP, la testina di lettura
legge il nastro di ingresso da sinistra a destra, come avveniva per gli automi a stati
finiti; il dispositivo di controllo si trova in uno degli stati di Q, mentre la pila
contiene elementi di F. A differenza di quanto visto per gli AF, la mossa (o
transizione) dell’automa non è solo una funzione del simbolo letto in ingresso i e
dello stato presente q, ma dipende anche del simbolo presente in cima alla pila, che
una volta letto viene rimosso dalla pila.
La mossa consiste nel:
- Muovere la testina di lettura verso destra;
- Commutare verso un nuovo stato q'~,
- Scrivere una stringa (eventualmente vuota) di simboli appartenenti all’insieme
F in cima alla pila, al posto del simbolo appena rimosso.
I caratteri vengono impilati e disimpilati secondo una politica LIFO. Inoltre, a
differenza di quanto definito per gli AF, l’automa può anche effettuare una mossa
senza leggere alcun simbolo in ingresso. Formalmente, ciò significa che anche
Ò(q, s, A) può risultare definita. Si noti che non leggere alcun simbolo in ingresso
116
Informatica teorica

Nastro di Ingresso

Pila

Figura 4.20 Una macchina con memoria ausiliaria a pila.

non significa necessariamente che la stringa in ingresso sia stata completamente


letta. Allo scopo di rendere le mosse univocamente determinate, si impone il
vincolo per cui, se ò(q, e, A) è definita, allora ò(q, i, A) deve risultare indefinita per
ogni i. Se non si fosse imposta questa limitazione, l’automa potrebbe scegliere,
qualora si trovasse in uno stato q con i in ingresso e A in cima alla pila e ò(q, e, A) e
ò(q, i, A) fossero entrambe definite, di muoversi o nello stato definito da ò(q, e, A)
senza leggere l’ingresso o in quello definito da ò(q, i, A), leggendo il simbolo i in
ingresso e spostando la testina di lettura di una posizione; cioè l’automa potrebbe
scegliere fra diverse mosse possibili in maniera non deterministica, come mostrato
in Figura 4.21. Si ritornerà sul problema del non determinismo nella Sezione 4.4.
Per il momento si prenderanno in considerazione soltanto modelli formali
deterministici.

Figura 4.21 Una mossa non determinata di un AP.


117
Automi

Per definire formalmente il comportamento di un AP, introduciamo in primo luogo


il concetto di configurazione di AP, una generalizzazione del concetto di stato:
intuitivamente essa è una “fotografia” dell’AP in un determinato istante, che
mostra lo stato dell’organo di controllo, la porzione di stringa in ingresso che deve
essere ancora letta e il contenuto della pila.

Definizione 4.7
La configurazione di un AP è una tripla c = {q, x, fi), dove q e Q, x e /*, y e T. q è
lo stato corrente, x è la porzione non ancora letta della stringa di ingresso, e y è il
contenuto della pila.

Ogni volta che viene eseguita una transizione, l’automa che si trova in una certa
configurazione passa in un’altra configurazione, correlata alla precedente dalla
transizione appena eseguita.

Definizione 4.8
Per un dato AP A, la relazione binaria di transizione '"a nello spazio di tutte le
possibili configurazioni di A è definita da c = {q, x, fi)*~Ac'={q', x , y') se e solo se
vale una delle due condizioni:
1. x = ay, x = y, y = A[Ì, fi = aP e ò(q, a, A) = {q , a)
2. x = x , y = AP, y' = ap e ò(q, s, A) = {q', a)

Si noti che il simbolo A in •"a indica l’automa e non va confuso con un simbolo
della pila. Esso verrà omesso quando il contesto non consentirà equivoci. Si noti
inoltre che la condizione 1. descrive il passaggio da una configurazione all’altra
quando viene letto un simbolo in ingresso, mentre la condizione 2. prende in
considerazione il caso di s-mosse, cioè il caso in cui la testina in ingresso rimane
ferma.
Come si è osservato in precedenza, il punto 4 della Definizione 4.6 garantisce
che l’AP non si trovi in situazioni in cui può evolvere, a parità di condizioni, in
modi diversi, e di conseguenza assicura che, per ogni c, esiste al più una c tale che
c >- c . Si noti inoltre che nessuna mossa è possibile se la pila è vuota.
Analogamente a quanto fatto per la funzione di transizione degli AF, possiamo
introdurre la chiusura transitiva e riflessiva •- di •- ad indicare una sequenza di
transizioni fra configurazioni in corrispondenza a una sequenza di mosse dell’AP.

4.2.1 Automi a pila come riconoscitori di linguaggi


Analogamente a quanto visto per gli AF, gli AP possono essere usati in diversi
modi, ad esempio come riconoscitori di linguaggi. A tale scopo l’accettazione di
ima stringa da parte di un AP può essere definita introducendo la nozione di
118
Informatica teorica

insieme di stati finali, come è stato fatto per gli automi a stati finiti. Intuitivamente,
un AP riconosce una parola quando esiste un “cammino” o sequenza di transizioni
dell’automa che, partendo dallo stato iniziale, legge tutta la parola in ingresso
giungendo in uno stato finale.

Definizione 4.9
Un accettore (o riconoscitore) a pila è una 7-upla A = ‘Q, I, T, 8, q0, Zo, F), dove
Q, 1, T, 8, q(t e Zo sono definiti come nella Definizione 4.6, e F <z Q è l’insieme di
stati di accettazione. La stringa x e I* è accettata dall’automa se e solo se
(q0, x, Zo) •- (q, 8, y) per qualche q e F, y e T*.
Il linguaggio accettato (riconosciuto) da un AP è l’insieme delle stringhe accettate
dall’AP. Se A è un AP, L(A) indica il linguaggio accettato da A.

Intuitivamente, una stringa x è riconosciuta da un accettore a pila se esiste un
cammino coerente con x nell’automa che porta dallo stato iniziale a uno stato finale
leggendo tutta la stringa in ingresso.
Per semplicità, il termine “automa a pila” e il suo acronimo AP verranno usati
anche per riferirsi ad un accettore a pila, ogni volta che il contesto non farà nascere
equivoci.
Analogamente a quanto visto per gli AF, un automa a pila può essere descritto
da un grafo i cui nodi rappresentano gli stati dell’automa, mentre gli archi
rappresentano le transizioni. Se 8C<y0, i, A) = (q', a), allora esiste un lato orientato
che collega q a q' e che è caratterizzato dall’etichetta a, A/a.

Esempio 4.8
Un linguaggio di programmazione di tipo classico, come il Pascal, il C, Java, ...
conferisce ai programmi una struttura ad annidamento. Tale struttura viene
realizzata mediante le seguenti parole chiavi ed i seguenti simboli:
if..fi (per le istruzioni condizionali);
do...od (per i cicli);
begin...end (per racchiudere sequenze di istruzioni);
(...) (per racchiudere espressioni e sottoespressioni).
Il simbolo speciale $ indica la fine del programma.
Desideriamo ora specificare formalmente l’idea intuitiva di corretto
annidamento e bilanciamento delle parole chiave e dei simboli. Per questo scopo
utilizziamo un AP. Quando l’automa incontra, sul nastro di ingresso, un if, do,
begin, o ‘(’> mette sulla pila rispettivamente i caratteri I, D, B o P. Quando invece
esso incontra un fi, od, end oppure ‘)’, allora, per ottenere una corrispondenza
corretta, sulla cima della pila dovranno essere presenti, rispettivamente, i caratteri
119
Automi

I, D, B o P. Se ciò avviene, il simbolo in cima alla pila viene tolto e il processo


continua. Formalmente, si può definire l’AP di cui sopra nel modo seguente.
Q = {qo, qi}, dove q0 è lo stato iniziale.
1= {if, do, begin, fi, od, end, (, ), $}.
T = {I, D, B, P, Zo}, dove Zo è il simbolo iniziale della pila.
F= {q.}-
La funzione 5(q, z, A) è indefinita per q = qi e per ogni valore z e I ed A 6 T; i
valori di S(g0, i, Z) vengono descritti dalla Tabella 4.1 in corrispondenza dei valori
di z e di A. Tale automa è rappresentato graficamente in Figura 4.22.

Tabella 4.1 La funzione di transizione di un AP.

i
A if fi do od Begin end ( ) $
I q0JI qo.£ q^Di q»Bi q0PI
D qo,ID qQ,DD q^ qn.BD qo,PD
B qo,IB qa,DB qo,BB q^ qo,PB
P qo,IP qo,DP qo,BP qo,PP
z0 qo,IZo q^DZo qt>,BZ<} q:i:PZ0 qo,E

if, I\II begin, I\BI


if, D\ID begin, D\BD
if, B\IB begin, B\BB
if, P\IP begin, P\BP
if, Z0\IZ0 begin, Z0\BZ0
fi, 7]e end, B\e
do, I\DI (, I\PI
do, D\DD (, D\PD
$, Z0\s
do, B\DB (, B\PB
do, P\DP (, P\PP
do, Z0\DZ0 (, Z0\PZ0
Od, D\f> ), P\E

Figura 4.22 Un AP che riconosce il corretto annidamento in programmi nello


stile del Pascal.
120
Informatica teorica

Esempio 4.9
Consideriamo il linguaggio L = {anbn\ n > 1}, ossia l’insieme delle stringhe
formate da un qualunque numero intero positivo di a seguito da un ugual numero di
b. Tale linguaggio può essere riconosciuto da un AP, che verifica che nella stringa
letta le a precedano le b e che per ogni a letta sul nastro di ingresso salva il simbolo
Z sulla pila, verificando poi che il numero di b letto sia pari al numero di Z salvato
sulla pila. Formalmente,
A = {{q0, qx, q2}, {a, b}, {Z, Zo}, 8, q0, Zo, {q2})
dove 8 è descritta dal grafo di Figura 4.23.

Per convincersi che un automa riconosce un dato linguaggio si possono effettuare
alcune prove e osservare la sequenza di mosse dell’automa. Se consideriamo
l’automa A dell’Esempio 4.9, possiamo convincerci che riconosce L, considerando
l’elaborazione relativa alla stringa di ingresso aaabbb e alla stringa aaabb, che
dovrebbero essere rispettivamente accettata e rifiutata da A. L’elaborazione relativa
a aaabbb sarà:
aaabbb, Zò) •- aabbb, ZZÒ) l“ abbb, ZZZÒ) •“
(tfo, bbb, ZZZZÒ) >- bb, ZZZÒ) >- (tfi, b, ZZÒ) ■" (tfi, e, Zò) •- (tf0, U ò)
e quindi conferma che la stringa viene accettata dell’automa. La stringa aaabb
viene invece rifiutata, in quanto A si blocca nella configurazione (cp, 8, ZZÒ), che

Figura 4.23 Un AP che riconosce L = {anbn\ n > 1}.


121
Automi

non è una configurazione di accettazione, poiché q\ £ F. Il lettore è invitato a


verificare il comportamento di A in altri casi.
Se la verifica empirica non è considerata sufficiente per convincere che A sia
effettivamente in grado di risolvere il problema stabilito, si può cercare una
dimostrazione formale come suggerito dal seguente Esercizio 4.18.

ESERCIZI
4.18 Con riferimento all’AP di Figura 4.23, si dimostrino le affermazioni seguenti
da 1. a 5.:
1. \/n, (q0, a„, Zo) -* (q0, e, Z”Z0)
2. (q0,b,Z,Z0)^(q1,S,Z’-1Z0)
3. (qh b\ ZZ0) -* (qu e, Zo) e (qu bl, ZZ0) (qìf £, Zo) se i +j
4. lqqx, £, Zo) -* (#2, £, £>
5. La congiunzione di 1., 2., 3. e 4. chiaramente implica che
x, Zq) (q2, £, s) se e solo se x = anbn, n > 1
Suggerimento', si usi l’induzione.
4.19 Si definisca un AP che riconosca L = {anb2n\ n > 0}.
4.20 Si descriva un AP che riconosca il seguente linguaggio parentetico L, di
alfabeto Z:
/={(,), [,],<> )>•}
Le parentesi devono risultare opportunamente annidate, e l’annidamento può
avvenire a qualunque profondità. Il simbolo ‘.‘Rappresenta la fine della
stringa.

b, Zq| BZo
b,A\BA
b,B\BB

\
Figura 4.24 Un AP che accetta {wcwA}.
122
Informatica teorica

4.21 Si mostri che l’AP di Figura 4.24 riconosce il linguaggio


L = {wcwÀ | w e {a, &}*}
dove indica il riflesso o immagine speculare di w, ossia w letta da destra a
sinistra.
4.22 Si costruiscano degli AP che riconoscono i seguenti linguaggi
= {a"b"W| n,m > 1}
L2 = Lfl
L3= {anibn'an>bn> ■■■an*bn* \k>ì,ni> 1 per 1 < z < k}
4.23 * Si scriva un programma Pascal non ricorsivo che riconosca il linguaggio
presentato nell’Esempio 4.8. Si mostri come è possibile scrivere una
soluzione ricorsiva. In particolare, si mostri che in questo caso non c’è
bisogno di impiegare una pila esplicita, ma si può semplicemente ottenere il
medesimo effetto impiegando la pila dei record di attivazione delle
procedure, che viene automaticamente gestita dal processore del linguaggio.

4.2. 2 Gli automi a pila come traduttori di linguaggi


Analogamente al caso degli AF, è possibile estendere gli AP facendo sì che essi
possano produrre una stringa in uscita come risultato della scansione e
riconoscimento di una stringa in ingresso. Gli automi a pila con uscita (che
verranno chiamati trasduttori a pila) sono modelli astratti di trasduttori e, avendo a
disposizione la pila che permette di contare un numero illimitato di simboli,
analogamente a quanto visto per gli accettori a pila rispetto ai riconoscitori a stati
finiti, sono intrinsecamente più potenti dei trasduttori a stati finiti.

Definizione 4.10
Un trasduttore a pila (TP) è una 9-upla T= (Q, I, T, 8, q0, Zo, F, O, T|), dove Q, I, T,
8, z/0, Zo e F sono definiti come nella Definizione 4.9, O è un insieme finito di
simboli di uscita, q è la funzione di uscita r|: Q X (F-u {e}) X F O* definita ogni
volta che è definita 8. L’automa a pila A, ottenuto da un trasduttore a pila T
eliminando O ed q, è detto automa sottostante o soggiacente a T.

Una descrizione grafica di un TP si può facilmente ottenere come estensione della
notazione usata per gli AP. Nel caso dei TP, gli archi vengono etichettati mediante
una 4-upla {a, A, a, z) (scritta a, A/a, z), dove a, A, e a sono definite come nel caso
degli AP, mentre z è la stringa di uscita.

Definizione 4.11
Una configurazione di un TP T è una quadrupla c = {q, x, y, z), dove q e Q, x e /*,
ye l’, z e O*. La relazione di transizione c^c, con c = {q, x, y,z),
Automi 123

c = (q, x, y', z'\ viene definita analogamente a quanto visto per la >-A nella
Definizione 4.8, con le seguenti aggiunte:
z -zz ;
z = q(y, a, A) nel caso (a);
z = r|(<y, e, A) nel caso (b).

Definizione 4.12
Una traduzione r: /* —> O* è associata a Tnel seguente modo:
= z se e solo se {q0, x, Zo, e) >-T* {q, e, y, z), q e F, e,
t(x)
per nessun q', y', z', (q, e, y, z) >- (qr, e, y', z')1

Si osservi che, dato un TP T e una stringa x, la traduzione di x è definita solo se x
viene accettata dall’AP sottostante a T.

Esempio 4.10
Si ritorni al robot di Figura 4.18 che assembla piatti e tazze. La pila illimitata, che
non si poteva modellare mediante la memoria finita di un AF, si può invece
modellare efficacemente mediante la memoria a pila di un AP. Il TP che modella il
robot si può descrivere nel modo seguente.
1. Q = {qG, qi, q2}, dove gli stati hanno il seguente significato intuitivo:
q0: entrambe le mani libere;
qit nella mano sinistra c’è una tazza, la destra è libera;
q2: nella mano sinistra c’è un piatto, la destra è libera.
2. I = {p, t}, dove p sta per piatto e t per tazza.
3. T = {Zo, P, T}. La pila modella la pila del robot. Zo è il simbolo iniziale della
pila. P (rispettivamente, T) rappresenta il piatto (la tazza) quando viene
deposto sulla pila per un successivo assemblaggio.
4. O = {a} dove a indica un oggetto assemblato.
5, 6 ò e q sono definite dalla Figura 4.25.

1 Si assume questa restrizione per rendere univoca la definizione di t. Si osservi però che
essa potrebbe avere anche la conseguenza di non definire la traduzione di una stringa x pur
accettata dall’automa sottostante nel caso il trasduttore possa eseguire una sequenza infinita
di mosse del tipo (q, s, y, z) •- (q', s, y', z'). Approfondiremo questa osservazione nella
Sezione 4.4.3 (Definizione 4.14. e Teorma 4.13).
124
Informatica teorica

p> Zo| Zo, tz JZ, Zo| Z(), E

t, T\ TT, £ t, PI e, a p, P | PP, s

Figura 4.25 Un TP che modella un robot dotato di pila.

Intuitivamente l’automa, ogni volta che si trova in q0, si muove in q\ o in q2 a


seconda del simbolo in ingresso. Da q} (q2), se in ingresso c’è un piatto (tazza),
assembla un nuovo pezzo, scrivendo in uscita una a, se invece in ingresso c’è una
tazza (piatto), la impila insieme a quelle già presenti.
Il lettore è invitato ad esaminare attentamente il grafo di Figura 4.25 e a
simulare il comportamento dell’automa in diversi casi di prova.

Esempio 4.11
Si supponga di voler tradurre una stringa x del tipo wd, w^{a, b}*, in >/, ossia
nella stringa riflessa. Così, ad esempio, x = aabbabad verrebbe tradotta in
ababbaa. Il seguente TP T è in grado di svolgere questo compito:
T= ({q0, qi, q2}, {a, b, d}, {A, B, Zo}, 5, q0, {q2}, {a, b}, T|)
dove 8 e q sono descritti in Figura 4.26.
T legge la stringa di ingresso nello stato q0 e memorizza A (B) nella pila se
riceve una a (b). Leggendo d, T commuta allo stato q2, dove viene prodotta l’uscita
senza ulteriore lettura. Se una A (B) è sulla cima della pila, una a (è) è presente in
uscita. Quando appare Zo sulla cima della pila, T entra nel suo stato finale q2.
125
Automi

d, Zo| Z0; £

b, Zo| BZf} e
b, A | BA £
h RI RR p

Figura 4.26 Un TP che produce la stringa riflessa di quella in ingresso.

Si osservi che, mentre nell’Esempio 4.10 la pila viene usata per riconoscere il
linguaggio da tradurre, nell’Esempio 4.11 la pila non è necessaria per riconoscere il
linguaggio delle stringhe del tipo wd, w <= {a, b}*, ma serve invece per poter
generare la traduzione.

ESERCIZI
4.24 Si dimostri formalmente che il TP dell’Esempio 4.11 compie effettivamente
la traduzione richiesta.
Suggerimento', si usi l’induzione per dimostrare che:
1. {q0, w, Zo, e) >-T* (q0, £, Zo, dove W indica il risultato della
sostituzione delle lettere minuscole in w con le corrispondenti lettere
maiuscole, e indica il riflesso di W.
2. (qa, d, XZ0, e) ^T^qi, £, XZo, s) per ogni X& {A,B}*.
3. (</b £, XZ0, e) |-r* {qi, e, Zo, x), dove x indica il risultato della
sostituzione delle lettere maiuscole in X con le corrispondenti
minuscole.
4. (<ji, £, Zo, x) e, x) per ognix e {a, b}*.
4.25 Si costruisca un TP che realizza la traduzione
x(a”bmcm) = cb'”e\ per m,n > 1
(Non importa quale sia l’uscita se la stringa di ingresso non è del tipo
precedente).
126
Informatica teorica

4.2.3 Proprietà degli automi a pila


Nella Sezione 4.1.3 e nell’introduzione della Sezione 4.2 sono state fomite
motivazioni informali come sostegno all’affermazione che gli automi a pila
risultano più potenti degli automi a stati finiti. Per “più potenti” si intende che la
classe di problemi che si possono risolvere per mezzo di un automa a pila è più
ampia della classe di problemi risolubili mediante gli automi a stati finiti. In
generale si considerino due formalismi FI e F2, che definiscono accettori di
linguaggi. FI è più potente di F2 se l’insieme dei linguaggi accettati dai membri di
FI comprende l’insieme dei linguaggi accettati dai membri di F2.
Nel seguito si dimostrerà formalmente che gli automi a pila sono degli
accettori di linguaggi più potenti degli automi a stati finiti. Per farlo dimostreremo
che gli AP possono sempre simulare gli AF semplicemente non utilizzando la pila,
cioè che tutti i linguaggi riconoscibili da AF sono anche riconoscibili da AP, e che
esiste almeno un linguaggio riconoscibile da AP e non da AF. Questi due passi
vengono formalizzati dai due teoremi seguenti.

Teorema 4.11
Ogni linguaggio accettato da un AF è accettato anche da un AP. Quindi gli AP
sono almeno potenti quanto gli AF.

Dimostrazione
Per un dato AF A = {Q, I, 5, q0, F) è immediato costruire un AP
A = {q ,I,T,8 ,q0,Z0,F^ tale che L(A) = L(A). Si definisca semplicemente

Q =Q,I=I,T = {Àq}, F = F, 8 (q, i, Zo) = (8(q, i), Zo) per ogni q ed i.

Teorema 4.12
Il linguaggio L = {anbn | n > 1} può essere accettato da un AP ma non può essere
accettato da un AF. Quindi gli AP sono strettamente più potenti degli AF.

Dimostrazione
È noto dall’Esempio 4.9 che L può essere accettato da un AP, inoltre alla fine della
Sezione 4.1.3 abbiamo mostrato attraverso il Pumping Lemma che L non è
riconoscibile da alcun AF.
127
Automi

ESERCIZIO
4.26 Si dimostri che i TP sono trasduttori di linguaggi più potenti dei TF.

Analizziamo ora un’altra differenza fra gli AF e gli AP. Ad ogni mossa, gli AF
sono obbligati a leggere un simbolo. Ciò significa che un AF A con in ingresso una
stringa x composta da n simboli fa al più n mosse. Infatti A deve continuare a
leggere tutta la stringa, finché o la stringa è stata completamente letta o entra in uno
stato per cui la funzione di transizione non è definita per il corrispondente simbolo
di ingresso. Si noti che, come osservato in precedenza (nella Sezione 4.1), preso un
qualsiasi AF è sempre possibile trasformare la sua funzione di transizione in modo
da renderla totale. Si può dunque supporre che un AF legga sempre tutta la stringa
in ingresso fino all’ultimo carattere.
Lo stesso risultato non vale, in generale, per gli AP, anche se si aggiunge un
opportuno stato di errore, come avveniva per gli AF. Infatti, poiché gli AP non
devono necessariamente leggere un simbolo ad ogni mossa, può accadere che un
AP, in qualche configurazione, entri in una sequenza infinita di E-mosse (ossia
mosse che non fanno avanzare la testina sul nastro di ingresso). Ad esempio, se
l’AP fosse nella configurazione c = {q, x, AZ(j/ e <ì(q, e, A) = {q, AA\ esso
entrerebbe nella sequenza infinita di configurazioni {q, x, AZò) (q, x, AAZg) •-
(q, x, AAAZ0) Fortunatamente, questo comportamento spiacevole per gli AP
può essere eliminato, se necessario, mediante la seguente operazione.

Definizione 4.13
Si consideri un AP A.
{q, x, a) (q',y, fi) indica che
(q, x, a) l~*A {q',y, P) e, per p = ZP', 8(g', e, Z) è indefinito.

Intuitivamente, ,-*d è una sequenza di mosse che porta a una configurazione da cui
non è possibile procedere con un E-mossa, cioè per potere “evolvere” dalla
configurazione raggiunta, è necessario leggere un simbolo di ingresso.

Definizione 4.14
Un AP A è aciclico se e solo se per ogni xe/*, (g0, x, Zo) »-*d (q, e, y) per qualche
?ey.

Quindi un AP aciclico legge sempre tutta la stringa di ingresso (senza
necessariamente riconoscerla), qualunque sia, per poi fermarsi dopo un numero
finito di mosse.
128
Informatica teorica

Teorema 4.13
Per ogni AP A esiste un AP aciclico AL ad esso equivalente.

Schema della dimostrazione


Si noti dapprima che ogni AP può essere accresciuto in modo tale che, in ogni
configurazione del tipo (q, ix, Zq), ò(q, i, Z) oppure ò(q, s, Z) sia sempre definita.
Una tale modifica può, ovviamente, essere effettuata senza alterare il linguaggio
accettato dall’automa e senza violare il vincolo che garantisce l’univocità della
prossima mossa in ogni configurazione (detereminismo).
In secondo luogo, si osservi che un AP di questo tipo può non comportarsi in
modo che (q0, x, Zo) |-*d (q, a, y) solo se, per qualche stato q , e y = z E, ha la
possibilità di effettuare una infinità di a-mosse senza cancellare z , ossia se esiste
uno stato p tale che^<7,8, y) >-* {p, a, ay) >-* (p, a, 0ay^ 1-* (p, s, p" ay^ per

qualche a,/3 e r’ (ossia A entra in un ciclo). In tal caso, A può essere modificato
in un equivalente AL imponendo
8(q ,s,z ) = {qE,z
se durante (q, e,z) (p, a, pay^ non si entra mai in uno stato finale; altrimenti

si impone

8(q,a,Z ) = (qF ,z), ò(qF ,a,Z ) = (qE,Z )

dove qF è un nuovo stato finale. In entrambi i casi qE è uno stato di errore adatto a
consumare tutta la stringa di ingresso restante. I dettagli della costruzione sono
lasciati al lettore per esercizio.

L’importanza del Teorema 4.13 appare evidente quando si analizza la chiusura dei
linguaggi riconosciuti da AP rispetto al complemento. Infatti, la costruzione
proposta per gli AF, consistente semplicemente nello scambio tra stati finali e stati
non finali, non può essere estesa ad AP non aciclici. Infatti, una parola che
determina una sequenza infinita di s-mosse non sarà riconosciuta né dall’automa
originale né dall’automa “a stati invertiti”. Potendo invece trasformare l’automa in
modo da evitare cicli, rimane possibile utilizzare una costruzione simile a quella
proposta per gli AF, mostrando così che i linguaggi riconosciuti da AP sono chiusi
rispetto al complemento. I dettagli di tale dimostrazione sono lasciati al lettore nel
seguente Esercizio 4.27.
129
Automi

ESERCIZIO

4.27 * Si dimostri che la classe di linguaggi accettata dagli AP è chiusa rispetto al


complemento.
Suggerimento: si consideri, senza perdita di generalità, un AP aciclico A che
accetta un linguaggio L assegnato. Ciò garantisce che tutte le stringhe di
ingresso siano lette completamente. Allo scopo di costruire un AP A che
accetti L , si spezzi dapprima ogni stato q dell’automa in tre stati (q, 1),
(q, 2), (q, 3) e si faccia in modo che tutti gli stati del tipo (q, 3) siano di
accettazione. Lo scopo di questa operazione è di registrare se A sia entrato o
meno in uno stato di accettazione durante una sequenza di e-mosse. Nel
primo caso A raggiunge gli stati del tipo (q, 1). Nel secondo, A raggiunge
gli stati del tipo (q, 2) dai quali i corrispondenti stati (q, 3) sono raggiungibili
mediante opportune e-mosse.

Per analizzare la chiusura dei linguaggi riconosciuti dagli AP rispetto alle altre
usuali operazioni insiemistiche, consideriamo il seguente teorema, che mostra una
limitazione importante del potere riconoscitivo degli AP dovuto ai limiti (la
strategia LIFO) relativi alla gestione della memoria a pila.Vedremo poi come
questo risultato influenza la chiusura dei linguaggi riconosciuti dagli AP rispetto
all’unione (e di conseguenza, rispetto all’intersezione e alla differenza).

Teorema 4.14
Nessun AP è in grado di riconoscere il linguaggio L = {anb™\ m = 2n oppure m = n,
n > 1}.

Il teorema è dimostrato in termini intuitivi, poiché la dimostrazione formale
richiede molti meccanismi tecnici che sono propri di uno stadio più avanzato della
teoria dei linguaggi formali.
L’Esempio 4.9 e gli Esercizi 4.19 e 4.21 hanno mostrato che un AP può
“contare” un numero illimitato di a solo per mezzo della sua pila, e può
“confrontare” il valore conteggiato con un altro intero solo disimpilando gli
elementi dalla pila, il che implica la distruzione dell’informazione immagazzinata
nella pila relativa al valore di n. Quindi, nel caso dell’Esempio 4.9, dopo aver letto
a”bn, un AP perderà l’informazione su n. Ciò non implica necessariamente che la
pila debba essere svuotata, ma piuttosto che il suo contenuto non può più avere
alcuna relazione con l’intero n.
D’altra parte, per ogni AP, se
{qa, anbn, Zo/ •-* (q, s, y) allora (q0, anbn+k, Zo} •-* {q, bk, y)

Al fine di accettare L, il comportamento dell’automa deve essere tale per cui


130
Informatica teorica

{q, bk, y) {q, s, y'\ q e F solo se k = n

Ciò è tuttavia impossibile poiché l’AP ha ormai perso l’informazione su n.


Intuitivamente quindi il linguaggio L del Teorema 4.14, poiché il numero di b può
essere o uguale al numero di a o il doppio del numero di a e non vi è modo di
sapere in quale dei due casi ci si trovi fino alla completa lettura di n b, non è
riconoscibile da un AP. Infatti se decidiamo di disimpilare un simbolo della pila
per ogni b letta e, alla fine, abbiamo ancora delle b in ingresso, non vi è modo di
verificare che ne siano rimaste in ingresso esattamente n; se, invece, scegliamo di
disimpilare un simbolo dalla pila ogni coppia di b lette e quando abbiamo finito di
leggere l’ingresso ci sono ancora dei simboli sulla pila, non vi è modo di verificare
che essi siano n/2.

ESERCIZIO
4.28 Si mostri che nessun AP può riconoscere il linguaggio L = {cfbnan\ n>l},
usando un ragionamento informale simile a quello impiegato per giustificare
il Teorema 4.14.

Il risultato del Teorema 4.14 è legato al problema della chiusura dei linguaggi
riconosciuti dagli AP rispetto all’unione. Infatti il linguaggio L = {abm\ m = 2n
oppure m = n, n > 1} può essere visto come l’unione dei linguaggi {a"b"l n > 1} e
{anb2n\ n > 1} entrambi riconoscibili da AP. Questo significa quindi che gli AP non
sono chiusi rispetto all’unione, in quanto un linguaggio ottenuto per unione di due
linguaggi riconoscibili da AP non è riconoscibile da AP.
Come conseguenza del risultato appena ottenuto, possiamo affermare che i
linguaggi riconoscibili da AP non sono neanche chiusi rispetto all’intersezione e
alla differenza. Infatti, se fossero chiusi rispetto all’intersezione, visto che sono
chiusi rispetto al complemento e che l’unione di due insiemi A e B si può esprimere
come (A n B), dovrebbero essere chiusi anche rispetto all’unione. Con un
ragionamento analogo si può mostrare che i linguaggi riconoscibili da AP non sono
chiusi neanche rispetto alla differenza.

4.3 Le macchine di Turing


Gli AP sono più potenti degli AF, nel senso che possono risolvere tutti i problemi
risolubili dagli AF ed anche alcuni problemi che gli AF non riescono a risolvere.
Anch’essi hanno però i loro limiti. Tali limiti sono essenzialmente dovuti alla
rigida politica LIFO con la quale viene gestita la memoria a pila. Il Teorema 4.14
aveva già evidenziato uno di questi limiti: la pila è una memoria distruttiva in cui
leggere significa eliminare elementi dalla pila. Inoltre la politica LIFO permette di
accedere solo all’ultimo elemento inserito e quindi, qualora ci sia la necessità di
131
Automi

leggere un simbolo che si trova in mezzo alla pila, tale politica di accesso obbliga a
eliminare tutti i simboli impilati sopra ad esso.
Come ulteriore esempio, si consideri il robot che impila piattini e tazze,
menzionato precedentemente., La politica LIFO della pila forza il robot ad
accoppiare ima nuova tazza con il piatto più recentemente ricevuto, che si trova
sulla cima della pila. Si supponga ora che il robot debba assemblare insiemi
costituiti da un piatto grande, un piattino ed una tazza, in cui la tazza deve essere
posta sopra un piattino che, a sua volta, deve essere posto su di un piatto grande. È
facile comprendere che una memoria a pila basata su politica LIFO è inutile in
questo caso. Si indichino i piatti grandi, i piattini e le tazze con i simboli g, p e t,
rispettivamente. Se il robot immagazzina tutti gli oggetti in arrivo in una memoria
sequenziale e se segue una politica LIFO, esso non sarà mai in grado di maneggiare
sequenze di oggetti del tipo gggpppttt. Non è infatti disponibile alcun insieme
completo fino a che non vengono ricevute le tazze. Quindi tutti gli oggetti ricevuti
prima delle tazze devono essere messi da parte. Si noti che alcuni di essi
potrebbero essere tenuti nelle mani del robot, solo in numero finito, però; quindi
per sequenze del tipo gnpnf con n sufficientemente grande, gli oggetti dovrebbero
alla fine essere accantonati in una memoria secondaria. Se poi la memoria
secondaria è una memoria a pila, dopo aver ricevuto la prima tazza il robot può
prendere il piattino, ma non il piatto grande, a meno che non elimini tutti gli altri
piattini ricevuti, perdendo però la possibilità di recuperarli.
Questo esempio conferma in modo intuitivo i limiti della memoria gestita a
pila e fornisce una motivazione per cercare dispositivi ancor più generali e potenti.
Introduciamo quindi un altro modello classico dell’informatica e,
precisamente, le macchine di Turing (MT), che prendono il nome da Alan Turing,
un pioniere della teoria della computazione che per primo propose e studiò questo
modello. Seguendo lo stile consueto, viene dapprima presentato in modo informale
il modello, poi vengono fomite le definizioni formali ed esempi chiarificatori ed
infine si studiano le sue proprietà principali.
Informalmente, una macchina di Turing (MT) consiste di:
- Un nastro di ingresso T/.
- Un nastro di uscita To.
- k nastri di memoria Tb ..., T^.
Ciascun nastro è una sequenza di celle ed è infinito a destra. Ciascuna cella
contiene un unico simbolo scelto all’interno di un insieme finito di simboli del
nastro, contenente fra gli altri anche il cosiddetto spazio vuoto (blank),
indicato da b. Ciascun nastro viene letto da una testina che può leggere,
scrivere, muoversi a destra o a sinistra di una posizione, o rimanere ferma. La
testina di T/non può scrivere. La testina di To non può né leggere né muoversi
verso sinistra.
- Un dispositivo di controllo dotato di un numero finito di stati.
132
Informatica teorica

La Figura 4.27 fornisce una descrizione schematica di una MT. Tale macchina è
solitamente chiamata MT multinastro o a k-nastri.

Figura 4.27 Una macchina di Turing.

Un passo di computazione di una MT consiste nelle seguenti operazioni. In


dipendenza dallo stato corrente del controllo e dai simboli che si trovano sotto
ciascuna testina di lettura (ossia del nastro di ingresso e dei nastri di memoria), la
macchina effettua le seguenti operazioni.
1. Cambia lo stato del dispositivo di controllo;
2. Stampa nuovi simboli - sovrascrivendo i simboli esistenti - nelle celle
sottostanti alle testine dei nastri di memoria;
3. Muove una o tutte le testine, indipendentemente l’una dall’altra, di una cella a
destra (7?) o a sinistra (£) o le lascia ferme (5), con l’eccezione della testina del
nastro d’uscita To che non può muoversi a sinistra.
In alternativa, la macchina non effettua alcuna operazione e si ferma
definitivamente.
Una MT si può descrivere mediante un grafo; come al solito, gli stati
dell’organo di controllo sono indicati dai nodi del grafo e i passi computazionali, o
transizioni, sono rappresentati da archi etichettati. Ad esempio, la Figura 4.28
rappresenta il fatto che, partendo dallo stato qx e leggendo i da T/, 5b ..., sk da Tb
..., Tj, la macchina compie una transizione passando allo stato q2, scrive o su To,
sostituisce 5i, ..., sk con 5/, ..., sk, e muove le sue k + 2 testine secondo la Uupla
(Àf0, ..., Mk +1). Ciascun può essere R,LoS. Mo indica la mossa della testina di
Tg Mk+1 indica la mossa della testina di To e deve essere diversa da L.
133
Automi

*><«!> • • • .s*> /o.csj'............ Sjk'>, <Af0............ Mk + 1>

Una configurazione di una MT a k nastri, cioè una fotografia della macchina dopo
un passo di computazione, è composta dallo stato dell’unità di controllo, dal
contenuto dei k + 2 nastri e dalla posizione delle k + 2 testine. La configurazione
iniziale corrisponde allo stato della macchina prima di iniziare la computazione ed
è composta dallo stato iniziale dell’unità di controllo, dal nastro di ingresso
contenente un numero finito di simboli non vuoti, dai nastri di memoria che
contengono solamente un simbolo speciale non vuoto Zo nella loro prima cella e
nessun altro simbolo non vuoto e dal nastro di uscita vuoto. Tutte le testine sono
posizionate sulla prima cella dei rispettivi nastri. Ad ogni passo, quindi, poiché una
transizione può scrivere solo un simbolo per nastro, solo un numero finito di
simboli non vuoti si troverà su uno qualunque dei nastri.

Esempio 4.12 •
Un robot che metta insieme una sequenza di piatti grandi (g), piattini (p) e tazze (?)
può funzionare nel modo seguente:
- Riceve tutti gli oggetti da un nastro in movimento e pone ciascun oggetto in
cima a tre pile diverse, una per le g, una per le p ed una per le i;
- Dopo aver ricevuto tutti gli oggetti, estrae una g, una p, ed una t, da ciascuna
pila e produce un insieme assemblato (a) (si veda la Figura 4.29);
- Alla fine delle sue operazioni verifica che tutte le pile siano vuote. Ciò
garantisce che sia stato ricevuto un ugual numero di oggetti dei tre tipi.
La descrizione di ima MT M che modella il precedente robot è riportata qui di
seguito. Questa macchina ha cinque stati, q0, qR, qw, qA, qE e tre nastri di memora
Ti, T2 e T3.
- Inizialmente M è in qn. Essa passa allo stato qR (uno stato di lettura), lascia
inalterati i simboli speciali Zo sui nastri Tb T2, T3 e muove le corrispondenti
testine a destra, non curandosi di quale simbolo venga letto da T/ (cioè esegue
la mossa a prescindere dal simbolo in ingresso). Entrambe le testine T/ e To
non vengono mosse (si veda la Figura 4.30a).
134
Informatica teorica

Figura 4.29 Un robot che assembla piatti grandi, piattini e tazze.

- Quando M è in qR e legge g,p oppure t dal nastro Tj, essa rimane in qR e scrive
i simboli di ingresso in I), dove j = 1 se i = g, j = 2 se i = p ej = 3 se i = t. Le
testine di Tz e di T, vengono mosse verso destra, mentre le altre testine restano
ferme (si veda la Figura 4.30b). Il nastro Ti memorizza quindi il numero di
piatti grandi, il nastro T2 il numero di piattini e il nastro T3 il numero di tazze.
- Quando M è nello stato qR e legge un simbolo vuoto da T/, ciò significa che
tutti gli oggetti sono stati letti. D’ora in poi, la testina di Tz non si muoverà
più. M commuta a <7;y(uno stato di scrittura), sposta a sinistra le testine di Tb
t2,t3e non scrive nulla su To (si veda la Figura 4.30c).
- Quando M è in qw e legge i simboli g, p e t da Tb T2, T3 rimane in qw, scrive
un simbolo vuoto al posto del simbolo letto e sposta le testine a sinistra.
Inoltre scrive una a su To e muove la corrispondente testina verso destra (si
veda la Figura 4.30d). In questo modo per ogni tripla di oggetti viene scritta
una a sul nastro in uscita.
- Quando M è in qw e legge Zo da tutti i nastri Tb T2, T3 significa che è stato
ricevuto un ugual numero di g, p e t e che tutti sono stati assemblati. La
macchina allora passa nello stato qA, uno stato di accettazione, scrive uno
spazio vuoto al posto di Zo, scrive il carattere Y (per indicare il successo) su To
e si ferma (si veda la Figura 4.30e).
- In tutti gli altri casi, cioè se in almeno uno dei tre nastri di memoria non si è
giunti a Zo, M entra nello stato di errore qE, e quindi scrive il carattere N (per
indicare il fallimento) su To e si ferma (si veda la Figura 4.301).
135
Automi
ì<Zo,2q,Zq>/«. <Zb.Zo,Zo>, <s,JtR,R.S>

c. < 8,0 >/«, < 0,0. <r,&s,ns>


fbj
».<O.8>f«,<O.8>.<S,L.L,L,S> >,<SkS,X&S>

«, <l,s,c>tu, < 0,4 > r<S,l>,L,LrR>

^,<Zo,s,c>iN,<^,g,e >,<s,s,s,s,s>

^,<S,S,»>/K<e,g,e><sfs,s,s,s>
(d) (H

Figura 4.30 Una MT che modella un robot, i indica un qualsiasi carattere tra g,
p ot. (a) Inizializzazione. (b) Lettura, (c) Commutazione verso imo
stato di scrittura, (d) Scrittura, (e) Accettazione, (f) Rifiuto.

La Figura 4.31 mostra cinque configurazioni notevoli di M quando opera sulla


stringa di ingresso gggpgtpt. La Figura 4.3la rappresenta la configurazione
iniziale. Le Figure 4.3Ib e 4.3le mostrano rispettivamente la configurazione
raggiunta dopo aver letto tutto l’ingresso e l’inizio della fase di scrittura, cioè la
configurazione che si ha subito dopo che M si è spostata da qR a qw. La Figura
4.3 Id rappresenta la configurazione raggiunta quando si sono già letti tutti i
simboli sui nastri T, e Tj (il numero di piatti grandi in ingresso è infatti maggiore
del numero di piattini e tazze) e la Figura 4.31e mostra la configurazione finale,
dopo che è stato raggiunto lo stato di errore qE.
136 Informatica teorica
138
Informatica teorica

Figura 4.31 Configurazione di esempio della MT di Figura 4.30. (a)


Configurazione iniziale, (b) Fine della lettura, (c) Inizio della
scrittura, (d) Scrittura, (e) Individuazione del fallimento.

ESERCIZI
4.29 Ispirandosi all’Esempio 4.12, si costruisca una MT che modelli un robot che
compone insiemi formati da un piatto, una tazza, due forchette, due coltelli e
un cucchiaio.
4.30 Si costruisca una MT in grado di rappresentare un robot che mette insieme
piatti grandi, piattini e tazze, come nell’Esempio 4.12, con la differenza che
un oggetto assemblato deve essere emesso non appena ciascuna delle tre pile
contiene almeno un elemento.
4.31 Si costruisca una MT che risolva il problema dell’Esempio 4.12 usando un
solo nastro di memoria.

Passiamo ora ad una descrizione e ad un’analisi più formale delle MT.


139
Automi

Definizione 4.15
Una MT a k nastri è una 9-upla M = {Q, I, T, 0,5, r|, qo, Zo, F) dove
- Q è un insieme finito di stati-,
- Z è un alfabeto di ingresso finito;
- T è un alfabeto di memoria finito;
- O è un alfabeto di uscita finito;
- F c Q è l’insieme degli statifinali-,
- qoe. g è lo stato iniziale',
- Zq g T è il simbolo iniziale dell’alfabeto di memoria;
- I, T e O contengono un simbolo speciale, detto simbolo vuoto, indicato con H> ; '
- S è la funzione, eventualmente parziale, di transizione-,
Ò:(Q-F)X IX r* -> Qx r*X {Rfi,S}k+1;
- p è la funzione, eventualmente parziale, di uscita
ipCg-Z^X Ix T4 —> Ox {R, 5'}, definita là dove è definita 5.

Quando non si specifica il valore di k, la MT viene chiamata semplicemente MT


multinastro.

Si noti che la funzione di transizione è definita in modo tale che non ci siano
transizioni uscenti da uno stato finale.

Definizione 4.16
Una configurazione c di una MT a k nastri Mè una (k + 3)-pla
c = {q, xliy, cxUAPa, ulo)
dove q e Q, x e y e I*, i e I ar e /f e T*, AreT, per 1 <r<k, u e O*, o g O,
T é/uTuO.

q indica lo stato corrente di M, xiy, arAfir, uo, indicano rispettivamente il contenuto
del nastro di ingresso, dei nastri di memoria e del nastro di uscita, in modo tale che
le restanti porzioni siano tutte vuote. Le testine di ciascun nastro sono posizionate
sulla cella che memorizza il primo simbolo della stringa che segue il simbolo T.

Definizione 4.17
Una configurazione iniziale Cq di M è del tipo
Cq = 1(1q, 7iy, 7Z0, ...,TZo,T«)

ossia, x = a, Ar = Zq, ar = pr = e, u = e, o = V> .


140
Informatica teorica

Bisogna ora formalizzare la transizione da una configurazione c a una


configurazione c in una MT M. Intuitivamente, le due configurazioni saranno
“collegate” se, applicando a c i cambiamenti indotti dalla transizione abilitata da c
stessa, si ottiene c .

Definizione 4.18
La relazione di transizione *-M (detta anche mossa o passo computazionale) fra due
configurazioni c e c di M è definita nel modo seguente.
Siac = (p, xTiy, afMiPi, ..., aklAkfik, u\o) con
x= xi ,y = jy , ar= ar Ar ,flr = Brf3r ;
Sia c = Ip , xAi'y, a/TA/p/, ..., a/TA/P/, zz'To').
Sia ò(p, z, Ai, .Ak) = (p,Ci, Ck, N, Nj, ..., Nk) con
p e Q,Ne {R,L,S}, Cr e T,Nr e {/?,£,£} per
1 < r < k',
Sia r|(#, i, Ai, ...,Ak) = (y, M) con v e O, Me {R, S}. Allora c>-Mc’ se e solo se
vale innanzitutto la:
1. p = q.
Deve poi valere una delle seguenti condizioni mutuamente esclusive, numerate 2.1,
2.2 e 2.3:
2.1 x = x, i = i, y = y' e N= S;
2.2 x' =xi,i' = j ,y’ = y eN= R
(se y = e, allora i" = V> e y’ = s);
2.3 x = x , z" = z, y’ = iy e N=L.
Inoltre, per 1 < r < k, deve valere anche uno dei seguenti casi mutuamente
esclusivi, numerati 3.1, 3.2 e 3.3:

3.1 ar = ar, Ar' = Cr, P/ = pr e Nr = S;


3.2 a/ = arCr, Ar' = Br, p/ = pr eNr = R
(se pr = s, allora Ar' = b e pr' = s);
3.3 a/ = ar, A, = Ar, P/ = Crpr e Nr = L.

Infine deve verificarsi anche una delle seguenti condizioni, mutuamente esclusive,
numerate 4.1 e 4.2:
4.1 u = u e o' = v, M = S;
4.2 u = uv e o' = b , M = R.

Se, nei casi 2.3 e 3.3 x = s oppure ex = s (ossia se la testina di lettura è posizionata
sulla cella più a sinistra del nastro) non esiste un c tale che c '-Mc e M si ferma.
141
Automi

La stessa cosa avviene seSet] non sono definite in (q, i,A\, ...,At). In tali casi c
viene chiamata configurazione di arresto.
. ■
Esaminiamo il significato delle diverse condizioni alla luce del comportamento del
modello informalmente definito in precedenza. Se ò(q, i,A^, =
(p, Q, Ck,N,Ni, ...,Nk), la condizione 1. vincola lo stato di c a essere quello
di arrivo della transizione.
Le condizioni di tipo 2. definiscono l’evoluzione del nastro di ingresso nel
passaggio da c a c \ se la testina rimane ferma (2.1), le tre parti in cui la testina
divide il nastro (parte sinistra, simbolo corrente e parte destra) saranno identiche in
c e c . Se la testina si muove a destra (2.2), la parte a sinistra della testina in c
conterrà anche il simbolo corrente di c, il simbolo corrente di c sarà il primo
simbolo della parte destra in c e la rimanente parte destra di c sarà la parte destra di
c . Infine, se la testina si muove a sinistra (2.3), la parte a sinistra della testina in c
conterrà tutti i simboli della parte sinistra in c, tranne l’ultimo che diverrà il
simbolo corrente di c e la parte destra di c sarà composta dal simbolo corrente in
c seguito dalla sua parte destra.
Analogamente le condizioni di tipo 3. specificano l’evoluzione dei nastri di
memoria e le condizioni 4. quella del nastro di uscita. Si noti che nel caso delle
condizioni 3. e 4. bisogna anche considerare il simbolo che viene scritto nella
posizione corrente da 8, e che nella condizione 4. ci sono solo due possibili mosse.
Si osservi ché, come al solito, per ogni configurazione c, la relazione di
transizione vale al più per un c . Inoltre, se lo stato q di una configurazione c
appartiene a F, allora c è ima configurazione di arresto, ossia M si ferma sempre
quando raggiunge uno stato finale, anche se non è vero necessariamente l’opposto.
Come di consueto, il simbolo M verrà eliminato da *~M quando non vi saranno
rischi di ambiguità.

4.3.1 Le macchine di Turing come accettori di linguaggi


Quando le MT vengono usate per definire linguaggi e come dispositivi
riconoscitori, il nastro di uscita non viene preso in considerazione, come accade per
gli AF e gli AP. Le MT verranno quindi considerate, per il momento, come delle
7-uple ottenute eliminando O e q dalla Definizione 4.15. La definizione di
configurazione di una MT viene modificata di conseguenza, cioè diverrà c =
(q, x3iy, «iT^iPì, ..., omettendo la rappresentazione del nastro di uscita.
Informalmente, una MT M accetta una stringa x, scritta sul suo nastro di
ingresso, se raggiunge una configurazione di arresto il cui stato corrispondente sia
uno stato finale. Per definire formalmente l’accettazione dobbiamo, analogamente
a quanto fatto per AP e AF, considerare la chiusura riflessiva e transitiva *~*M di
che rappresenta la sequenza di configurazioni in corrispondenza a una
sequenza di mosse nella MT.
142
Informatica teorica

Definizione 4.19
Sia Muna MT multinastro. Una stringa xe/* è accettata da M se e solo se
Co = (?o, Tx, TZ0, TZ0) '~*M{q,xVy, ■■■, afL4ApA)conq e F.
Il linguaggio accettato da M è definito come L(M) = {x\x e I* e x è accettato da
M}.

Intuitivamente quindi, il linguaggio riconosciuto da una MT M è composto da tutte
e sole le stringhe che, coerentemente alla funzione di transizione, permettono di
andare dallo stato iniziale a uno stato finale. Si noti che per le MT, poiché nel
nastro in ingresso è possibile muoversi in entrambe le direzioni, non è richiesto che
al termine della computazione la testina si trovi al termine della stringa di ingresso;
in alcuni casi particolari la stringa potrebbe anche essere accettate pur senza essere
stata letta completamente. Si osservi, inoltre, che una volta che M ragiunge uno
stato finale, per come è definita la funzione di transizione, non lo può più lasciare e
termina la sua computazione.

Esempio 4.13
Si consideri il linguaggio L = {a>1bncn\ n> 1}. E’ facile comprendere che la MT M
ad un solo nastro schematizzata qui sotto è in grado di riconoscerlo.
Intuitivamente, M, che inizialmente si trova nello stato q0, opera nel modo
seguente.
Passo 1: Leggendo a, M passa allo stato q\ e muove la testina del suo nastro di
memoria verso destra, stando ferma in ingresso, allo scopo di spostarsi
nella prima cella libera sul nastro di memoria, senza però dimenticarsi di
contare la a sul nastro in ingresso.
Passo 2\ M legge tutte le a fino a che non viene raggiunga la prima b (se esiste).
Nel fare ciò, M scrive tanti * alla destra di Zo nel suo nastro di memoria
quante sono le a del nastro di ingresso.
Passo 3: Alla prima b, Mpassa allo stato q2, tenendo ferma la testina nel nastro in
ingresso e muovendosi a sinistra nel nastro di memoria.
Passo 4\ M legge tutte le b. Nel fare ciò, muove verso sinistra la testina del nastro
di memoria di tante posizioni quante sono le b del nastro di ingresso. Nel
suo passaggio la testina lascia inalterati i simboli * contenuti nelle cella,
ossia li riscrive identici. In questo modo, dopo aver letto tutte le b, la
testina è posizionata su Zo se e solo se il numero di b eguaglia il numero
delle a. Inoltre il numero di a (e di b) letti è rimasto memorizzato nel
nastro di memoria grazie alla lettura non distruttiva che ne è stata fatta.
Passo 5: Alla prima c, Mpassa allo stato g3, tenendo ferma la testina nel nastro in
ingresso e muovendosi a destra nel nastro di memoria, usando lo stesso
accorgimento del Passo 1.
143
Automi

Passo 6: M legge tutte le c (se ve ne sono). Nel fare ciò, muove la testina del suo
nastro di memoria verso destra di tante posizioni quante sono le c lette in
ingresso. In questo modo, quando si raggiunge il primo b a destra della c
posta più a sinistra, la testina del nastro di memoria raggiunge il primo b
alla destra dei * se e solo se il numero delle c uguaglia il numero delle b.
In tale caso M entra in uno stato finale e si ferma.
In tutti gli altri casi M si ferma in imo stato non finale.
Il diagramma della Figura 4.32 fornisce una descrizione più dettagliata di M.
Come al solito, gli stati finali sono indicati con un doppio cerchio e la mancanza di
un arco uscente significa che 8 non è definita per quei valori.
Si fornisce qui sotto la sequenza di computazione completa di M in
corrispondenza della stringa di ingresso aabbcc. Il lettore è invitato a simulare il
comportamento di M sia con altre stringhe del tipo anbncn che con stringhe non
appartenenti aL, verificando se Meffettivamente accetta/,.
(q0, ^aabbcc, TZ0) •- (qJr ^aabbcc, Zg\ b( (q}, a^abbcc, Z0*l b) •-
(q}, aa\bbcc, Z0**L b}1- (fa, aa\bbcc, Zo*1* b)1- (q2, aabTbcc, Zgt** b) .
(q2, aabblcc, 7Z0** b)1- (q3, aabb\cc, ZoV** b) >- (fa, aabbc\c, Zo*^* b) >~
(q3, aabbcc\ b , ZgV** b)1- (q4, aabbcc\ b , Z0**T b )

a,»h,<R,R>

Figura 4.32 Una MT che accetta il linguaggio {anbncn\ n> 1}.


144
Informatica teorica

Esempio 4.14
Si consideri il linguaggio L = {ab"\ n > 1} u {a"Z>2"| n > 1}. La MT a 2 nastri M
schematizzata qui sotto è in grado di accettare L.
Dopo un banale passo di inizializzazione, equivalente al Passo 1 dell’Esempio
4.13,
Passo P. M legge tutte le a e scrive tanti * a destra di Zo su Ti quante sono le a
presenti sul nastro di ingresso. Contemporaneamente, in modo analogo,
scrive coppie di * su T2.
Passo 2: Mentre sta leggendo le b, muove di un passo a sinistra sia le testine di Ti
che di T2.
Passo 3: Se sono state lette tutte le b e se la testina di Ti è posizionata su Zo, M
entra in uno stato finale e si ferma.
Passo 4\ Altrimenti, M legge tutte le restanti b e nel fare ciò continua a muovere la
testina di T2 di un passo a sinistra per ogni b letta.
Passo 5: Se tutte le b sono state lette e la testina di T2 è posizionata su Zo, M entra
in uno stato finale e si ferma.
In tutti gli altri casi, la stringa di ingresso viene rifiutata.
Come esercizio, il lettore è invitato a fornire una descrizione dettagliata di M,
mediante un diagramma di stato, e a verificare che M effettivamente accetti L.

ESERCIZI
4.32 Si costruiscano MT multinastro che accettino i seguenti linguaggi
Li = {wcw| w e {a, b}+}
L2 = {wcwA| w g {a, b}+}2
L3= {anbnl |m> 1}
L4 = {x g {a, b, c}* tale che il numero di occorrenze di a in x sia uguale al
numero di occorrenze di b o al numero delle c}.
4.33 Si costruisca una MT a 1 nastro che accetti L ={anbn} u {anb2n}.

Bisogna ora fare un’importante annotazione. Tutte le MT usate finora come


riconoscitori di linguaggi hanno la proprietà di raggiungere sempre una
configurazione di arresto per ogni ingresso dato. In tale configurazione, se lo stato
è uno stato finale esse accettano la stringa di ingresso, altrimenti la rifiutano. Si
osservi, tuttavia, che questa condizione non è strettamente richiesta dalla
Definizione 4.19. In base ad essa, infatti, quando x g L, la MT si ferma in una
configurazione finale, ma, se x <£ L, M può anche non raggiungere mai una
configurazione di arresto, ossia può continuare a funzionare indefmitivamente.

2 Come al solito, v/ indica la stringa riflessa di w.


145
Automi

ctVM.<S,R>

Figura 4.33 Una modifica della MT della Figura 4.32.

Per esempio, si potrebbe modificare la MT dell’Esempio 4.13 aggiungendo lo stato


q5 e le transizioni di Figura 4.33 (il resto del grafo rimane inalterato). Se la
macchina modificata M' ricevesse in ingresso una stringa del tipo d‘bncn+\ non
entrerebbe mai nello stato g4, ma si muoverebbe in q5, effettuando il ciclo
all’infinito. Si noti però che, secondo la Definizione 4.19, M'accetta ugualmente
L = {anbV\n> 1}.
Il lettore osserverà che una situazione analoga si era verificata con gli automi a
pila, che potevano entrare in un ciclo infinito di £-mosse. Si era però riusciti a
dimostrare (Teorema 4.13) che questo inconveniente poteva sempre essere
eliminato, grazie alla costruzione di AP aciclici. Nel Capitolo 8 si mostrerà che
questo risultato non vale per le MT e si analizzeranno le importantissime
conseguenze di questo fatto.
Per il momento, sottolineamo soltanto al lettore che V accettazione, come
definita nella Definizione 4.19, è un concetto più debole della decisione, in quanto
il secondo termine implica la capacità di stabilire entrambe le relazioni x e L e
x <£ L. Una MT che accetta L è in grado di stabilire se x e L quando ciò avviene,
ma non necessariamente l’opposto, poiché potrebbe non fermarsi mai per un dato
ingresso. Come si è già detto, la differenza concettuale fra i due termini verrà
analizzata dettagliatamente nel Capitolo 8.

4.3.2 Le macchine di Turing come traduttori di linguaggi


Quando si definiscono le MT con il nastro di uscita, esse divengono dei trasduttori,
ossia possono essere usate per tradurre stringhe di I* in stringhe di O*. In questo
caso le MT vengono viste come le 9-uple della Definizione 4.15.
146
Informatica teorica

Definizione 4.20
Sia M una MT multinastro. M definisce una traduzione rM: I* O* secondo la
regola seguente:
TmCQ =y se e s°l° se Tx, TZ0, TZ0, T# )
(q, xAiy, afiAfii, con q e F.

Come al solito, M verrà omesso in >-M e quando non vi sarà rischio di
ambiguità.
Intuitivamente, una stringa x viene tradotta in una stringa y da una MT M, se
esiste un cammino che parte da una configurazione iniziale con x sul nastro di
ingresso e termina in una configurazione finale con y sul nastro di uscita.
Si noti che, in generale, una MT M definisce una traduzione parziale, ossia
una funzione parziale zM'-1*^0*. Infatti è indefinita sia se M raggiunge una
configurazione di arresto il cui stato non appartiene a F sia se M non si ferma mai
quando opera su x. Ciò è illustrato dai seguenti esempi.

Esempio 4.15
La MT ad un nastro schematizzata qui sotto duplica ogni stringa di ingresso di
alfabeto {a, b}+. Precisamente, = xx per ogni xe {a, b}+.
Passo 1: M legge x da sinistra a destra, copiandola, carattere per carattere, sia in
TycheinTo. -
Passo 2\ Quando si raggiunge la fine di x in Tj, M smette di scrivere e muove
all'indietro la testina di T; fino a che non viene raggiunto Zo. In questa
fase la testina di To rimane ferma e non scrive nulla.
Passo 3: A questo punto, M ripercorre T/ copiandone il contenuto carattere per
carattere su To. In questo modo una nuova copia di x viene concatenata a
To.
Passo 4\ Quando si raggiunge il primo spazio vuoto in Tj, M entra in uno stato
finale e si ferma.
Il diagramma di Figura 4.34 fornisce una descrizione dettagliata di M. Il lettore è
invitato a simulare manualmente il comportamento di M in corrispondenza della
stringa di ingresso abb.
147
Automi

Figura 4.34 Una MT che duplica le stringhe di ingresso. Una doppia etichetta
per un singolo lato è un’abbreviazione per indicare due lati
paralleli etichettati rispettivamente con le due etichette.

Esempio 4.16
La MT a 2 nastri M schematizzata qui sotto effettua la seguente traduzione:
T^anbn-) = *” ^anb2n-) = *2n n> 1
r.v(x) è indefinita se xg {anbn\ n > 1} u {anb2n\ n > 1}

Passo 1: M legge tutte le a. Per ciascuna a, scrive un singolo * in Ti ed una coppia


di * in T2.
Passo 2'. M legge tutte le b e muove verso sinistra le testine di Ti e di T2 per
ciascun ingresso b.
Passo 3: Se tutte le b sono state lette e la testina di Tz è posizionata su Zo, M
emette tanti * in To quanti sono i * contenuti in T/, poi entra in uno stato
finale e si ferma.
Passo 4’. Altrimenti, se restano ancora delle b da leggere, M le legge e muove
verso sinistra la testina di T2 per ogni b letta.
148
Informatica teorica

Passo 5: Se tutte le b sono state lette e la testina di T2 è posizionata su Zo, M


emette tanti * in To quanti ve ne sono in T2 ed entra in uno stato finale,
fermandosi.
In tutti gli altri casi M entra in uno stato di errore qE F e si ferma.

Esempio 4.17
Il nastro di ingresso T, di una MT M contiene una sequenza di parole inglesi w0,
wi, ..., wm organizzate nel modo seguente.
Wo$Wib w2b ...wn*
I simboli $, b e * fungono da delimitatori e non possono apparire aH’intemo delle
parole. M cancella dalla sequenza w0$uq lb w2 b ... w„* tutte le occorrenze di stringhe
uguali a u’o e stampa la sequenza risultante sul nastro di uscita To.
Mè una MT a 2 nastri ed opera nel modo seguente.
Passo 1 : u’o viene copiata su Tp
Passo 2\ Mlegge il testo, parola per parola (la fine della lista è marcata con *). Per
ciascuna parola w, si effettuano le seguenti operazioni:
2.1 Wt è dapprima copiata su T2. Nel fare ciò, M confronta u’, con w0
carattere per carattere. La fine della parola è indicata da b o da *.
2.2 M cambia stato se e w0 risultano diverse, continuando a copiare
wt su T2.
2.3 II contenuto di T2 viene copiato su To se /Wha cambiato stato in 2.2.
2.4 Lo stato viene azzerato se c’è già stato un cambiamento di stato in
2.2 e le testine di Tj e di T2 vengono posizionate sulla prima cella
del nastro.

ESERCIZI
4.34 Si dia una descrizione formale dettagliata della MT deH’Esempio 4.16.
4.35 Si costruisca una MT ad 1 nastro che effettui la medesima traduzione della
MT dell’Esempio 4.16.
4.36 Si costruiscano le MT multinastro che effettuano le seguenti traduzioni:
- T/(x) = XX/<
- t2(x) = xx se il primo e l’ultimo carattere di x a, xxR altrimenti
- Tj(x)= x1*1
2 3
- T4(an)= b" se nè pari, bn se nè dispari.
4.37 Si dia una descrizione dettagliata della MT schematizzata nell’Esempio 4.17.
4.38 Si costruisca ima MT che sostituisce ogni occorrenza di ima parola data in un
testo inglese con un’altra parola data.
Suggerimento: si tragga ispirazione dall’Esempio 4.17.
149
Automi

4.39 Si costruisca una MT che riceve in ingresso un testo inglese contenente un


numero arbitrario di spazi fra ciascuna coppia di parole ed emette un testo
usando un unico spazio come delimitatore.
4.40 Si costruisca una MT che riceve in ingresso una sequenza di parole inglesi
separate da H ed emette la sequenza di ingresso senza parole ripetute.

4.3. 3 Le macchine di Turing come valutatori di funzioni


Finora le MT e gli altri automi sono stati considerati principalmente come
elaboratori di linguaggi. Si osservi, tuttavia, che le stringhe sono anche un mezzo
per rappresentare i numeri. Ad esempio 101 indica “centouno” se interpretato come
numero decimale e “cinque” se interpretato come un numero binario. Gli automi,
quindi, e le MT in particolare, se usati per tradurre stringhe di cifre in altre stringhe
di cifre, si possono anche considerare come calcolatori di funzioni con dominio e
codominio numerico. Ovviamente, le funzioni calcolate dai diversi automi visti
dipendono dalla potenza di ciascun modello.
Sfruttando questa idea, ad esempio, si potrebbe costruire una MT che riceve in
ingresso una stringa di cifre decimali, che codifica il numero n e produce come
uscita la stringa di cifre decimali che codifica n2, implementando così la funzione
“quadrato” per mezzo di una MT.
Si noti che può accadere che la funzione fM: D -> R calcolata da qualche MT M
sia parziale. Infatti fM è indefinita per qualche x e D, se la computazione di M.
sull’ingresso che codifica x, si ferma in uno stato non finale o, ancora più
importante, Mnon si ferma mai.
Presentiamo ora alcuni esempi di MT usate come valutatori di funzioni.

Esempio 4.18
La MT a 2 nastri schematizzata qui sotto riceve in ingresso un numero naturale n
codificato con la notazione decimale e produce il suo successore n + 1.
Passo 1: M copia tutte le cifre di n su Tb alla destra di Zo. Nel fare ciò, muove la
testina di T2 dello stesso numero di posizioni, senza però scrivere nulla.
Si noti che l’ingresso contiene il numero n dalla cifra più significativa a
quella meno significativa, per cui la cifra più vicina a Zo sarà quella più
significativa.
Passo 2\ M legge le cifre immagazzinate in Ti da destra a sinistra, cioè dalla meno
significativa alla più significativa.
Per ciascuna cifra d
2.1 Finché d è 9, Mscrive 0 nella corrispondente cella di T2.
2.2 Alla prima cifra d diversa da 9, M scrive la cifra rappresentante
d+ 1 nella corrispondente cella di T2. Se il simbolo letto non è una
cifra (ossia è Zo), allora M scrive 1 nella prima cella del nastro To.
150
Informatica teorica

2.3 Dopo la prima cifra d, diversa da 9, tutte le restanti cifre di Ti (se ve


ne sono) vengono copiate identicamente in T2.
Passo 3: La stringa di cifre che è stata prodotta sul nastro T2 viene copiata sul,
nastro To.
La Figura 4.35 fornisce una descrizione dettagliata di M.

ESERCIZIO
4.41 Si simuli il comportamento della MT dell’Esempio 4.18 con gli ingressi 99,
12899 e 312.

Figura 4.35 Una MT che calcola il successore di un numero decimale. □ indica


ima qualunque cifra decimale. Quando due o più □ occorrono
nella medesima etichetta di un arco, esse indicano la stessa cifra. 13
indica una qualunque cifra diversa da 9. △ indica il successore
della cifra indicata da E.
151
Automi

ESERCIZIO

4.42 Si simuli il comportamento della MT dell’Esempio 4.18 con gli ingressi 99,
12899 e 312.

Esempio 4.19
Costruiamo una MT M in grado di sommare due numeri naturali codificati in
binario. I due numeri n e m sono scritti sul nastro di ingresso, dalla cifra più
significativa a quella meno significativa, separati dal simbolo $. M ha tre nastri di
memoria Tb T2 e T3, e funziona nel modo seguente.
Passo 1: Copia n su T] alla destra di Zo. Nel fare ciò, le testine di T2 e di T3 non
vengono spostate.
Passo 2\ Dopo aver letto il simbolo $ che separa n e m, M copia m su T2
mantenendo ferme le testine di Tj e di T3.
Passo 3: Dopo aver letto m, le testine di T; e diT2 vengono posizionate sui
caratteri più a destra di n e di m, cioè i meno significativi. Così, M
comincia a sommare n e m, cifra per cifra, secondo le regole descritte
dalla Tabella 4.2, in cui e a, indicano rispettivamente la cifra
z-esima di n, m ed a = n + m. rt rappresenta il riporto zesimo; inizialmente
(ossia per i = 1) r, = 0.
Le cifre di a vengono scritte su T3 da sinistra a destra (ossia a-, è
memorizzato nella cella alla destra di Zo), quindi dalla cifra meno
significativa a quella più significativa. In ciascun passo, dati i valori di n,,

Legenda
152
Informatica teorica

nii e rh la Tabella 4.2 mostra come valutare ai e ri+i. Quest’ultima viene


usata, per mezzo della moria finita dell’organo di controllo, nel passo
successivo, insieme a n(+1 e tmì+1, per valutare ai+ b
Passo 4: Quando la testina di T] (T2) legge Zo, la computazione continua come se
le restanti cifre in T] (T2) fossero tutte 0, fino a che anche la testina di T]
(T2) raggiunge Zo.
Passo 5; Quando entrambe le testine di e di T2 raggiungono Zo, se r, + 1 vale 1,
un 1 viene aggiunto a T3.
Passo 6: Infine, la stringa a viene copiata su To in ordine inverso; ossia la stringa
a viene copiata da destra a sinistra, quindi dalla cifra più significativa a
quella meno significativa.
La Figura 4.36 mostra una descrizione dettagliata di M.

,<Z0,Z0.Z0>/«, <Zo.Zo,Zo>. <SAR.RS>

<«.».» >,<S.LMS,8> >1».<«.i ...» ><H.S.R.S.S>

<13

fa/

H, <0.O,H >/t. <0,O,O> <S.L.L.fLS>

H < 1,1.* >/«, <l,l,0>

(0 = 0)
8 <0,0,« > /» < 0,0.1 > <8,1,, L.R. S >

t. <0.1.8 >/* <0,1,1 ><S.L.L.R.S> t> <0,1.8 >,'» <0,I.0> ,<S.AL,ftS>


» < 1.0.» >lb < 1,0.1 >< S,L.L/t.S> » <1,0,8 < 1,0,0> <S,£,L.HS':
(b)
1W
Automi

e,<Zo,l,O>,<S,S,L,R,S> K,<l,Zo.O>,<S.4S.B,S>
b,<O,Zo,b>l
»,<Zo,O,l>,<S,S,L,R,S> b,<Ò,Zo,l> ,<S,L,S,R,S>

98

e,<Zo,i,ii >i
b,<Zo,l,0>,<S.S,L,R,S>

b,<O,Zo,b>l
«,<Zo,O,l> ,<S,S,L,R,S> »,<O,Zo,l>,<S,L,S,R,S>

b,<Z0,Z0,b>l ' b, <Z0,Z0,b >/


b.<Zo,Zo,l>,<S,S,S,S,S> *, <Zo,Zo.l >, <S,S£S,S>
j>,<Z0,Z0,«>/
b. <Z0,Z0,l >, <s,s,s,s,s>

(d)
154
Informatica teorica

fe)

Figura 4.36 Una MT che effettua l’addizione binaria, (a) Fase di lettura,
corrispondente ai passi 1,2. (b) Passo 3. (c) Passo 4, quando r, = 0.
(d) Passi 4 e 5, con rt■ = 1. (e) Passo 6. □ indica 0 o 1. Diverse
occorrenze di □ nell’etichetta del medesimo arco indicano la
stessa cifra. Le etichette multiple su di un singolo arco sono
un’abbreviazione per indicare diversi archi etichettati dalle
rispettive etichette. Quando necessario, i nodi racchiudono un
breve commento sul significato dello stato.

ESERCIZIO
4.43 Si simuli a mano il comportamento della M dell’Esempio 4.19 su qualche
esempio.
4.44 Si mostri che q5, q7, q$ e q9 nell’Esempio 4.19 potrebbero essere eliminati
nella costruzione di M, ossia che si può costruire una MT equivalente senza
questi stati.
4.45 Si costruisca (o perlomeno si schematizzi) una MT che effettua la somma di
due numeri decimali.
4.46 Si schematizzi una MT che esegue la moltiplicazione di due numeri
codificati in una base qualunque.
4.47 Si schematizzi una MT che stabilisce se un numero dato è primo o meno. La
macchina si deve fermare per ogni ingresso con una risposta affermativa o
negativa.

4.3. 4 Proprietà delle macchine di Turing


In questa sezione si discutono brevemente alcune importanti proprietà delle MT,
allo scopo di familiarizzare il lettore con questo modello che verrà usato
estensivamente nel corso del testo. In primo luogo si dimostra che le MT sono più
potenti degli AP e, di conseguenza, degli AF.
Automi 155

Teorema 4.15
La classe dei linguaggi riconosciuti dalle MT include strettamente la classe dei
linguaggi riconosciuti dagli AP.

Dimostrazione
Per dimostrare l’inclusione debole basta mostrare che, per ogni AP A, si può
sempre costruire una MT ad 1 nastro M che agisca esattamente come A, usando il
nastro di memoria esattamente come la memoria a pila di A. Si lascia al lettore
l’esercizio di mostrarlo formalmente.
Per provare l’inclusione in senso stretto, si osservi che una MT può
riconoscere il linguaggio {anbn} o {anb2n}, come mostrato nell’Esempio 4.14,
mentre un AP non può fare altrettanto, come dimostra il Teorema 4.14.

Lo stesso risultato vale per le MT usate come traduttori di linguaggio rispetto ai
TP, come enunciato dal seguente teorema, la cui dimostrazione viene lasciata al
lettore per esercizio.

Teorema 4.16
Le MT sono traduttori più potenti dei TP, ossia tutte le traduzioni effettuate da un
TP possono essere effettuate anche da una MT, ma non viceversa.

ESERCIZIO

4.48 Si dimostri il Teorema 4.16. Suggerimento', la traduzione definita


nell’Esempio 4.16 richiede la capacità di riconoscere L = {anbn} o {anb2n};
tuttavia L non può essere riconosciuto da alcun AP (Teorema 4.14). Questa
osservazione può essere formalizzata, dimostrando che l’esistenza di un TP
in grado di effettuare una simile traduzione implicherebbe l’esistenza di un
AP capace di riconoscere il linguaggio precedente.

La Definizione 4.15 presenta solo una delle molte versioni, leggermente differenti
fra loro, del modello di macchina che ha preso il nome da Alan Turing. In seguito
viene ora mostrato un campione di tale varietà. Si anticipa fin d’ora che tutte le
versioni presentate hanno la medesima potenza di calcolo del modello precedente.
Consideriamo, dapprima, il modello di Turing originale; esso è leggermente
diverso da quello esaminato in precedenza. Informalmente, la differenza consiste
nell’avere, al posto di nastri distinti usati rispettivamente come ingresso, memoria e
uscita, un solo nastro, che viene usato contemporaneamente come dispositivo di
ingresso, uscita e memoria (si veda la Figura 4.37).
156
Informatica teorica

Dispositivo
di controllo

Figura 4.37 La macchina di Turing originale.

Un’altra differenza rispetto alla Definizione 4.15 è che l’unico nastro del modello
originale è infinito in entrambe le direzioni. Come al solito, l’unità di controllo può
effettuare diverse mosse in dipendenza dal simbolo sotto la testina e dallo stato
interno. Una mossa consiste in:
- Cambiamento dello stato interno;
- Riscrittura del simbolo sotto la testina;
- Spostamento della testina a sinistra (L) o a destra (R) o in nessuna direzione
(5).
Convenzionalmente, l’ingresso della macchina è il contenuto del nastro all’inizio
della computazione. La testina è inizialmente posizionata sul simbolo non vuoto
più a sinistra e lo stato dell’unità di controllo viene posto pari a q0. Quando la
macchina si ferma, qualora ciò avvenga, un’opportuna convenzione definisce quale
porzione del nastro vada considerata come uscita della macchina. Formalmente,
una macchina di Turing a nastro singolo può essere definita nel modo seguente.

Definizione 4.21
Una MT a nastro singolo è una 5-upla {Q, A, 8, q$, F) in cui
- Q è un insieme finito di stati;
- A è un alfabeto finito caratteristico del nastro, comprendente un simbolo
speciale vuoto b ;
- q0 e Q è lo stato iniziale;
- F è l’insieme degli stati finali;
- 8 è la funzione, eventualmente parziale, di transizione
bfiQ-F) X A^Q 'X AX {R,L,S}.
157
Automi

ESERCIZIO
4.49 Si formalizzino le nozioni di configurazione, configurazione iniziale,
transizione, riconoscimento di un linguaggio e traduzione di un linguaggio
per le MT a nastro singolo.

Si noti che le MT ad 1 nastro sono un modello diverso dalle MT a nastro singolo,


poiché nel primo caso Tz e To sono distinti dal nastro di memoria, mentre, nel
secondo caso, l’unico nastro funziona sia da memoria che da ingresso e uscita. Allo
scopo di evitare confusione, il termine “MT ad 1 nastro” indicherà un MT
multinastro con un solo nastro di memoria, mentre MT a nastro singolo è un nome
riservato al modello della Definizione 4.21. La sigla MT viene indistintamente
impiegata per qualunque versione del formalismo di Turing.

Teorema 4.17
Le MT multinastro e le MT a nastro singolo sono formalismi equivalenti, ossia
accettano la stessa classe di linguaggi e realizzano la stessa classe di traduzioni.

Schema della dimostrazione
Ovviamente, la simulazione di una MT a nastro singolo mediante una MT
multinastro non è un problema. È sufficiente infatti aggiungere in modo molto
semplice alcuni passi, per copiare il contenuto dell’ingresso su di un nastro di
memoria all’inizio della computazione e dal nastro di memoria al nastro di uscita
alla fine della computazione.
Si consideri brevemente, invece, il caso opposto. Si supponga che una generica
configurazione di una MT a k nastri Msia del tipo di quella di Figura 4.38. Si può
rappresentare tale configurazione mediante una configurazione di una MT a nastro
singolo M' nel modo indicato dalla Figura 4.39. Il contenuto dei diversi nastri di M
può essere rappresentato in M’ sull’unico nastro, mettendo prima il nastro di
ingresso, poi i nastri di memoria e infine il nastro in uscita. Il contenuto dei diversi
nastri è memorizzato, segnando con il simbolo speciale * il punto in cui si trova la
testina in M. In M', poi, il contenuto dei diversi nastri è separato dal simbolo
speciale $, che segna anche l’inizio della stringa sul nastro di ingresso. La fine
della sequenza è invece indicata con il simbolo speciale q .
158
Informatica teorica

Dispositivo
di controllo

Figura 4.38 Configurazione della MT a k nastri M. c(T) indica la porzione non


vuota del contenuto del nastro T.

Una singola transizione di Mè simulata da un’opportuna sequenza di transizioni di


M'. Più precisamente, M' deve “visitare” q e i caratteri alla destra di tutte le *,
fatta esclusione per il * più a destra, allo scopo di ottenere l’informazione
necessaria per scegliere la mossa. Successivamente M’ simula la mossa di M
modificando opportunamente i caratteri alla destra degli * ed eventualmente
cambiando le posizioni degli *.
Se qualche nastro di M, Tx, richiede una cella supplementare, questa viene
ottenuta facendo scorrere opportunamente tutti i caratteri alla destra del simbolo $,

e « $ c(Tfi' $ £07)' $ c(Ti)' a c(Ti)'" c(To)’ - <TOr 9

Dispositivo
di controllo

Figura 4.39 Configurazione della MT a nastro singolo M'. c(Tx)' rappresenta il


contenuto non vuoto del nastro Tx di M alla sinistra della testina di
Tx; c(Tx)" rappresenta il contenuto dello stesso nastro alla destra
della testina, compreso il carattere sotto la testina. $ e * sono
simboli che non appartengono a luTu O e vengono usati per
marcare i limiti fra il contenuto dei diversi nastri e posizioni della
testina, rispettivamente, q è un’opportuna codifica dello stato di M.
Automi 159

che marca la fine di c(Tx). Ad esempio, si supponga che c(Tx)" contenga un solo
carattere e che la testina debba muoversi a destra. M' dapprima fa scorrere di una
posizione tutti i caratteri a destra di c(Tx)”, compreso $, poi scambia c(Tx)" con *
e scrive un b nel “buco” restante.
I dettagli della costruzione sono lasciati al lettore per esercizio.

Sia le MT multinastro che quelle a nastro singolo si possono dotare di nastri
multidimensionali: il modello risultante è la MT con nastro multidimensionale. Un
nastro ^-dimensionale è realizzato in modo tale che ciascuna cella sia
univocamente determinata da una £-upla di interi positivi, analogamente a quanto
avviene per le matrici nella programmazione. La Figura 4.40 illustra il caso di un
nastro bidimensionale (un nastro planare). Una MT con nastro bidimensionale può
muovere le sue testine (o la sua testina) in quattro direzioni: sopra, sotto, sinistra,
destra o rimanere ferma.
Anche questo modello è equivalente agli altri e l’equivalenza con il modello
originale può essere facilmente dimostrata una volta che si stabilisca una
conveniente corrispondenza biiettiva fra N - {0} e (N - {0})\ che indicano
rispettivamente l’insieme delle posizioni della testina per i nastri lineari e l’insieme
delle posizioni della testina per i nastri multidimensionali. Ad esempio, è possibile
enumerare le celle di un nastro bidimensionale seguendo un metodo diagonale,
come mostrato in Figura 4.41, dove, per ciascuna coppia {x,y),
Informatica teorica

Una volta che si è stabilita una corrispondenza fra le posizioni del nastro, è facile
stabilire una corrispondenza fra le configurazioni delle macchine e le relative
sequenze di mosse.

ESERCIZI

4.50 Si dia una definizione formale di MT con nastro multidimensionale e si


dimostri la sua equivalenza rispetto al modello proposto inizialmente in
questo capitolo.
4.51 Una MT multitestina è definita come una MT multinastro, con la sola
differenza che essa può essere dotata di più testine su ciascun nastro e le
mosse possono dipendere dai simboli che vengono letti da tutte le testine. Si
definisca formalmente questo dispositivo e si dimostri la sua equivalenza
con le MT multinastro.

Esistono altre varietà di MT, ma non è negli obiettivi di questo testo riportarli in
modo esaustivo.
Analizziamo ora il problema dell’esistenza di forme minime per le MT. Infatti,
quando si ha a che fare con modelli formali, si affronta spesso il problema della
trasformazione di un modello formale in un'altra forma equivalente più concisa, o
anche “minima”. Ciò avviene anche con i programmi per calcolatori, che si
possono trasformare in versioni equivalenti che riducono la quantità di memoria
161
Automi

impiegata o il tempo di esecuzione. Il problema è stato discusso anche nel caso


degli AF: dato un AF A che riconosce un linguaggio prestabilito, si è mostrato
come sia possibile ridurre il numero di stati fino al punto in cui si raggiunge il
numero minimo (Teorema 4.4). Un simile risultato vale per le MT? Il teorema
seguente fornisce una risposta in proposito.

Teorema 4.18
Ogni MT è equivalente ad un’opportuna MT dotata solo di due stati non finali e di
uno stato finale (ed eventualmente di un numero accresciuto di simboli).

La dimostrazione del Teorema 4.18 è altamente tecnica e può essere saltata dalla
maggior parte dei lettori. Il lettore interessato può cercare di ottenerla come
esercizio un po’ pesante nei dettagli tecnici da prendere in considerazione, ma
concettualmente non insormontabile, almeno facendo ricorso al “piccolo aiuto”
fornito. Una soluzione schematizzata è anche proposta alla fine del capitolo.

ESERCIZIO
4.51* Si dimostri il Teorema 4.18.
Suggerimento', senza alcuna perdita di generalità, si può considerare una MT
a nastro singolo M ed una singola mossa di M, consistente ad esempio nella
transizione:
(#7, ...<75T<73Ui3...) '“A/ (#3, .. .<75<78T<713...)

Sì costruisca una MT M a nastro singolo con due soli stati non finali, qa,
che simuli la mossa di Mattraverso la seguente sequenza di mosse.
XX, ...a5T(<77, a3,-,*)a13 ...)3 >- (q$, ...a5(q3, a*, +, 7?)Ta13 ...)>-
...T5T(g3, a8, +, R}{q0, a^, -,R)...)-
(<7p, ...as(q2, as, +, 7?XXo, «b,
«8, +, 7?Xtfl, «13, -, L')---')'-
{q?, ...a5{qx, a^, +, 7?XXi, «13, -, L')...')-
<?«,••• «5 X^l, «8, +, X%2, «13, -, L')...')
Xp, •••«5X0, «8, +, ^X<#3, «13, ”, L')---') *-
•••«s'KtfO, «8, +, ^X?3, «13, ”, ?,)•••) -

((/a,--. a5 aS X?3, «13, ,

3 * indica L o R in dipendenza dalla precedente mossa di m , ma non influenza il


comportamento successivo.
162
Informatica teorica

Si è riusciti precedentemente a costruire MT che calcolano la somma di due numeri


naturali, sia per la codifica binaria che per la codifica decimale. Il problema
generale che bisogna affrontare è se la potenza computazionale delle MT sia
influenzata o meno dal modo in cui il problema viene codificato. Il teorema
seguente risponde a questa domanda.

Teorema 4.19
Ogni MT è equivalente ad un’opportuna MT avente un alfabeto con due soli
simboli (eventualmente dotata di un numero maggiore di stati).

Il Teorema 4.19 afferma quindi che la codifica di un problema non influenza la
capacità di una MT di risolverlo. Si noti però che i Teoremi 4.18 e 4.19 impongono
una scelta: possiamo ridurre gli stati “agendo” sull’alfabeto o viceversa, ma
difficilmente, in generale, non possiamo ottenere entrambi gli scopi
contemporaneamente: una tipica situazione in cui si viene a trovare quasi ogni
ingegnere nelle sue scelte di progetto. Nel corso di questo testo troveremo diversi
altri esempi di questo genere.
Anche la dimostrazione del Teorema 4.19 è lasciata come esercizio per il lettore,
dopo aver notato che due simboli sono sufficienti per codificare qualunque alfabeto
finito. Ad esempio, siano dati i due alfabeti A = {$ , ai, ..., am}, A = {b,a}. Si può
allora rappresentare la stringa ... b b a3b ... come
... bbaabaaaaababbaaabb ...
Il simbolo a, viene cioè codificato da i occorrenze di a , mentre b viene usato
come separatore fra la codifica di ciascuna a, ed un doppio b codifica il simbolo b
originale.
Diversamente dalle MT, gli AF possono essere sensibili alla scelta della
codifica. Ad esempio, un TF può facilmente calcolare la funzione successore di un
numero naturale, se codificata in base unaria (in cui cioè n è rappresentato dalla
sequenza di n simboli identici, ad esempio a ). Il TF semplicemente copia il suo
ingresso sul nastro di uscita aggiungendovi preliminarmente un ulteriore a . La
stessa computazione non può essere effettuata da un TF se n è scritto in qualunque
altra base, come afferma il seguente teorema.

Teorema 4.20
Nessun TF è in grado di calcolare il successore di un numero naturale codificato in
una qualunque base k > 1.

Dimostrazione
La dimostrazione è data per k = 10, ma chiaramente vale allettante per ogni k > 1.
163
Automi

Si supponga, per assurdo, che esista un TF T che calcoli il successore di un


qualunque numero naturale codificato come stringa di cifre decimali. Si
considerino i numeri rappresentati da stringhe del tipo 9*, ossia n = 99...9 (9 è
ripetuto x volte).
Sia 9X) = z (z^£ per ipotesi). Allora q*(^0, 9*-9) = z-y^,
r|*(t7o, 9' • 8) = z -y2. Tuttavia il successore di 9X • 9 è 1 • 0x+1, mentre il successore
di 9X • 8 è 9X + \ di cui z. non è un prefìsso proprio.

Rimangono infine da analizzare le proprietà di chiusura dei linguaggi riconoscibili
da MT. Come anticipato, a differenza di quanto fatto per gli AP, per le MT non è
possibile eliminare eventuali cicli infiniti. Questo rende il meccanismo fino ad ora
utilizzato per creare l’automa che riconosce il complemento di un linguaggio L, a
partire dall’automa che riconosce L totalmente inefficace per tutte quelle stringhe
che portano la MT in computazioni che non terminano.
Da questa osservazione possiamo intuire che le MT non sono chiuse rispetto al
complemento, ma rimandiamo la discussione formale al Capitolo 8. La famiglia di
linguaggi riconosciuti da MT è invece chiusa rispetto all’unione e all’intersezione;
è infatti facile immaginare MT che simulino il comportamento due altre MT
“lavorando in parallelo (o in serie)” sulla stringa in ingresso; ad esempio una MT
“parallela” potrebbe abbastanza naturalmente simularne altre due, rispettivamente a
k\ e k2 nastri, facendo uso di k\ + k2 nastri e di un insieme di stati ricavato sulla
base del prodotto cartesiano dei due insiemi originari. Anche in questo caso i
dettagli della dimostrazione vengono lasciati al lettore (Esercizio 4.56).

ESERCIZI
4.52 Si dimostri la seguente versione, più forte, del Teorema 4.20. Nessun TF può
calcolare la traduzione t: Z*$ —> (9*$ tale che t(x$) = y$, dove y è la
codifica del successore di un dato numero naturale codificato da x.
4.53 Si mostri che esiste un TP che calcola la stringa riflessa di una codifica
decimale del successore di un dato numero naturale codificato in base
decimale.
4.54 Si definiscano formalmente le seguenti variazioni delle MT e si dimostri la
loro equivalenza con il modello multinastro:
1. MT non stazionaria', ad ogni passo le testine dei nastri devono
necessariamente muoversi a destra o a sinistra e non possono restare
ferme.
2. La macchina può effettuare una delle seguenti azioni:
a. Riscrivere, senza muovere la testina.
b. Muovere la testina senza scrivere alcun simbolo.
3. MT con nastro singolo semi-infinito: il nastro della macchina è
infinito solo a destra.
164
Informatica teorica

4.55 Si dimostri che l’insieme dei linguaggi accettati dalle MT è chiuso rispetto
all’unione e all’intersezione.

4.4 Automi non deterministici


Tutti i modelli evolutivi considerati finora sono deterministici, nel senso che, una
volta fissato lo stato e l’ingresso, la loro evoluzione è univocamente determinata; in
altri termini, la relazione di transizione è ad un solo valore. Spesso si verifica però
che il sistema che si deve modellare non può essere descritto in modo così
deterministico, in quanto l’osservatore possiede una conoscenza del suo
comportamento che non è sufficientemente accurata da consentirgli di prevederne
l’esatta evoluzione, come conseguenza dello stato presente e dell’ingresso dato.
Si consideri, ad esempio, un gioco definito dalle seguenti regole. Un giocatore
può sostare in un luogo, scelto all’interno di un insieme finito di altri luoghi,
ciascuno dei quali è caratterizzato da un suo nome ben preciso. Ad ogni mossa può
venirgli assegnata, da un altro giocatore, una lettera dell’alfabeto. In
corrispondenza di ciò il primo giocatore deve portarsi in un luogo il cui nome
comincia con la lettera assegnata. Se più di un luogo ha un nome che comincia con
quella lettera, il primo giocatore è libero di compiere una scelta arbitraria fra i
diversi luoghi possibili.
Un gioco di questo tipo può essere descritto da un grafo analogo a quello della
Figura 4.42, in cui sono rappresentati tre possibili nodi, “Giardino”, “Garage” e
“Casa”; da qualunque luogo con la lettera c, il giocatore si può solamente spostare

Figura 4.42 Una rappresentazione grafica di un gioco.


165
Automi

in “Casa”, mentre con la lettera g si può spostare sia in “Giardino” che in


“Garage”. Il grafo in figura, quindi, , pur assomigliando ad un AF, presenta più di
un arco uscente dai vari nodi etichettato con la lettera g. Questo rende ovviamente
il modello non deterministico e non coerente con la Definizione 4.1.
In altri casi una modellizzazione non deterministica viene preferita a una
deterministica in quanto in grado di fornire una descrizione più “astratta” di un
certo fenomeno reale. Ad esempio, la notazione (che non necessita di spiegazioni):
a > b => max := a
a < b => max := b
definisce il massimo fra due numeri a e b. Tale notazione si potrebbe considerare
come una specifica non deterministica del modo in cui una macchina può valutare
il massimo fra due numeri. Il non determinismo compare quando a = b, in quanto è
possibile applicare l’una o l’altra delle due regole, fri questo caso il non
determinismo favorisce l’astrazione delle specifiche, in quanto ciascuna delle due
interpretazioni è possibile.
Per ciascuno dei modelli evolutivi finora descritti esiste un modello non
deterministico corrispondente, come verrà illustrato nelle pagine seguenti; nella
sezione successiva, verrà poi introdotto un nuovo formalismo, le reti di Petri, e si
concluderà l’argomento nella Sezione 4.6 con qualche considerazione generale a
proposito del non determinismo.

4.4.1 Gli automi non deterministici a stati finiti


La seguente definizione riformula tutte le definizioni fondamentali relative agli AF
adattandole al caso non deterministico.

Definizione 4.22 '


Un automa non deterministico a stati finiti (AFN) è definito come nella
Definizione 4.1, con la sola differenza che la funzione di transizione è data da:
5: Q X I -» p(g)4

Sia 8 la funzione di transizione di un AFN. Allora 8*: Q X /* —> p(0 è definita


da

3*(<j, a) = {<?} per ogni <7 8*(<j, xz)= |J £(#',/)

Nel caso di accettori, x e /* è accettata da un AFN {Q, I, 8, q0, F) se e solo se


8*(<7o, x) n F 0.

4 Si ricorda che p(0 rappresenta l’insieme delle parti di Q, i cui elementi sono quindi
insiemi di stati.
166
Informatica teorica

Intuitivamente, la definizione precedente afferma che un AFN può presentare


diverse sequenze di transizioni per ogni dato stato e per ogni data sequenza di
ingresso: poiché la funzione di transizione di un AFN, dato uno stato e un ingresso,
porta, invece che in un solo stato, in un insieme di stati, la chiusura riflessiva e
transitiva di questa nuova funzione di transizione non rappresenta più un singolo
cammino coerente con la stringa scandita dall’automa, ma un insieme di possibili
cammini.
Il fatto che a una stringa corrispondano diverse sequenze nell’automa porta a
dover modifificare anche la condizione di accettazione. In particolare, una stringa
di ingresso è accettata se e solo se almeno una delle possibili sequenze di
transizioni conduce ad uno stato finale.
Ciò significa che se si “simula” il comportamento dell’accettore e si riesce a
trovare almeno una sequenza di mosse, tra quelle possibili, che conduce ad uno
stato di accettazione, allora si può concludere che la sequenza di ingresso è
accettata. Non si può tuttavia ottenere la conclusione opposta fino a che non si è
verificato che tutte le sequenze possibili non conducono ad uno stato appartenente
aF.

S S
(b)

Figura 4.43 Un AFN (a) e l’albero delle scelte (b) realizzate durante il
riconoscimento. S indica accettazione. □ indica uno stato da cui la
computazione non può procedere. L’albero rappresenta tutti i
cammini attraversati durante il riconoscimento di abaaa.
167
Automi

ESERCIZIO
4.56 Si scriva un programma per calcolatore che simuli un AFN. Il programma
riceve in ingresso una descrizione deH’accettore ed una stringa da analizzare
e fornisce in uscita un messaggio di accettazione o di rifiuto della stringa.
Suggerimento', si può costruire una struttura ad albero per rappresentare tutti
i possibili cammini attraversati dall’accettore durante il riconoscimento
(Figura 4.43). Quando un cammino non può essere percorso ulteriormente, il
programma deve ritornare indietro e tentare un nuovo cammino,
scegliendolo fra quelli uscenti dallo stato più recentemente incontrato ed
ancora in grado di offrire transizioni di uscita inesplorate.

Gli automi non deterministici a stati finiti non sono più potenti dei loro
corrispondenti deterministici finché si rimane nel campo del riconoscimento di
linguaggi. Questa proprietà viene dimostrata dal seguènte teorema.

Teorema 4.21
Per ogni AFN A, può essere costruito un AF AD che accetti il medesimo linguaggio.

Dimostrazione
Sia A = (g, I, 8, q0, F), 5: QXl
Si definisca AD come (QD, I, òD, q0D, Fa') con

- 2n = P(e);
- &o(qD, i) = q D= U S (q, i) ;

- Fd — {qo\ F n qo & 0};


- Qod = {qo} ■

Allo scopo di dimostrare che L(Ad) =L(A), bisogna mostrare che 8//(g0/j, x) g Fd
se e solo se 8*(g0, x) n F 0. Questa è una conseguenza immediata del fatto che
8d*(<?od, x) = 8*(g0, x) per ogni x.
Quest’ultima affermazione a sua volta può essere dimostrata per mezzo di una
semplice induzione. Infatti,

Òd*(<]od, s) = §*(?o, £) poiché q0D = {qG}


8r>(Sn*(?oo,y), 0 = |J 8(<y, z) = |J 8(<y, z) per l’ipotesi di induzione.
168
Informatica teorica

Più intuitivamente, dato un AFN, si costruisce un AF equivalente che ha come stati


insiemi formati da stati dell’AFN; la funzione di transizione è poi costruita in modo
che se un insieme di stati è raggiungibile a partire da uno stato dell’AFN, allora tale
relazione deve essere presente anche nell’AF sfruttando la costruzione degli stati
come insiemi di stati dell’AFN. Ad esempio se nell’AFN 5(^i, z) = {q2, q^} e
8{?i,0={?2,?4}, allora nell’AF 8D({#i}, z) = {q2, q3}, SD({q2}, i)= {q2, q4} e
8d({?i, qi}, Ì)= {qi, q3, qt}- Si osservi che nell’AF {^}, {q2}, {qr, q2}, {q2,q3},
{?2, qi} e{^2, ?3, qi} sono stati e non insiemi di stati. Inoltre, poiché uno stato
dell’AF rappresenta un insieme di stati dell’AFN e nell’AFN una stringa è
riconosciuta se almeno uno dei suoi possibili cammini coerenti con l’automa porta
dallo stato iniziale a uno degli stati finali, tutti gli insiemi di stati contenenti almeno
uno stato finale dell’AFN sono finali nell’AF.
Gli AFN, quando vengono impiegati in qualità di riconoscitori di linguaggi,
non sono dunque più potenti degli AF, anche se sono molto spesso più convenienti
da usare (la costruzione del Teorema 4.21 infatti produce un insieme di stati di
cardinalità 2", se n è il numero di stati dell’AFN di partenza). In molti casi, la
formalizzazione più naturale di un problema è quella descritta mediante un AFN.
Inoltre, molte dimostrazioni matematiche appaiono più immediate nel caso degli
AFN. Gli esempi e gli esercizi seguenti illustrano meglio questi punti.

Esempio 4.20
L’AFN di Figura 4.44 riconosce alcune unità lessicali di un linguaggio di
programmazione molto semplificato: numeri, identificatori, operatori aritmetici e
commenti. Le unità lessicali vengono separate da uno o più lb. I commenti
cominciano con /* e terminano con */. L’accettore di Figura 4.44 è non
deterministico. Ad esempio, dopo aver letto una / nello stato qt), l’accettore può
entrare sia in q3 che in q4. Analogamente, dopo aver letto una * nello stato q3, può
entrare in q(> oppure rimanere in q5.
169
Automi

Figura 4.44 Un AFN che riconosce le unità lessicali di un linguaggio di


programmazione immaginario. ìb = spazio vuoto; d = cifra; l =
lettera;, o = operatore (+,—, *, /); c = un qualunque carattere.

ESERCIZI

4.57 Si trasformi l’AFN di Figura 4.43 in un equivalente AF deterministico


usando il procedimento illustrato nel Teorema 4.21.
4.58 Si trasformi l’AFN di Figura 4.44 in un equivalente AF deterministico
usando il procedimento illustrato nel Teorema 4.21.
4.59 Si dimostri che la classe di linguaggi accettata dagli AF è chiusa rispetto
all’unione. (Questo risultato può essere dimostrato, in base al Teorema 4.8,
come conseguenza della chiusura rispetto al complemento e all’intersezione.
Si noti come sia più facile trovare una dimostrazione diretta dell’enunciato
per gli AFN che per gli AF).
170
Informatica teorica

È interessante a questo punto presentare e discutere brevemente i trasduttori non


deterministici a stati finiti. Come si vedrà, l’introduzione del nondeterminismo ha
qui un effetto profondo sul comportamento del dispositivo formale.

Definizione 4.23
Un trasduttore non deterministico a stati finiti (TFN) è definito come nella
Definizione 4.4, con l’unica differenza che le funzioni 5 e r| costituiscono una
coppia
(5, n): Q X I fpfiQ x O*)
dove pXG X O*) denota l’insieme dei sottoinsiemi finiti di Q X O*. 5 e rj sono
la proiezione di (5, r|) su Q e su O* rispettivamente:
8(g, z) = fi' | fi’, -w) e (5, T|)(g, z) per qualche w e O*}
r\fi,i)={w | fi', -w) e (5, rp(<T z) per qualche q e Q}
fi, T])*: Q X /* -> X (?*) è definito come al solito induttivamente da
fi, fi)* fi, e) = fi, e)
(5, T])*(<7, xz) = {fi’,fi) | 3q",u,v tale chey = uv e
fi",ù) e fi,fi)* fi, x) e fi',v) e <S,T]>(X', z)}

Un TFN definisce una traduzione r: I* —> pX^*) nel modo seguente:


r(x)={y \ 3q e F tale che (g, fi) e (5, q)*(^0,x)}.

Si osservi che i TF e i TFN sono modelli molto diversi e non ha quindi molto senso
paragonare le loro potenze di traduzione. Infatti i TF definiscono traduzioni ad un
solo valore r: I* —> O* mentre i TFN definiscono traduzioni a più valori r:
I* pXO*)-

ESERCIZIO
4.60 Si scriva un programma che simuli un TFN. Si noti che, per calcolare r(x), la
simulazione richiede la costruzione di tutte le possibili sequenze di mosse
che conducono agli stati finali.

4.4. 2 Automi a pila non deterministici e macchine di Turing non deterministiche


Le definizioni di automa a pila non deterministico e di macchina di Turing non
deterministica derivano in modo naturale dalle corrispondenti definizioni
deterministiche. Si considerino dapprima gli automi a pila non deterministici in
qualità di accettori di linguaggi.
171
Automi

Definizione 4.24
Un accettare a pila non deterministico (APN) è una 7-upla {Q, I, T, 8, q0, Zo, F)
in cui tutti i simboli hanno il medesimo significato dèlia Definizione 4.8, escluso 8,
che è definito nel modo seguente:
8: Q x (/ u {e}) x r —> x T*)
(dove X T*) indica i sottoinsiemi finiti di Q X T*). La relazione su
Q X I* X T* è definita da {q, x, y) (q', x , y') se e solo se
1. x = ay, x =y, y = A(Ì, y' = a0, (q ,a) e 8(q, a, A)
oppure
2. x = x', y = A(Ì, y' = a0, tq',a) e ò(q, e, A)
x e I* è accettato dall’automa se e solo se
{q0, x, Zo) {q, e, y), q e F, y e T*

Si noti che gli automi a pila sono modelli intrinsecamente non deterministici:
infatti, nella Definizione 4.8 si era dovuto aggiungere il vincolo che, se per
determinate condizioni è definita una e-mossa, allora non è definita nessuna altra
transizione con quelle condizioni. Questo vincolo viene ora rimosso.
Il non determinismo non aumenta la potenza di calcolo degli automi a stati
finiti quando vengono usati come riconoscitori di linguaggi. Lo stesso risultato non
vale nel caso degli automi a pila, come formalizzato nel seguente teorema.

Teorema 4.22
Il linguaggio L = {anbn\ n > 1} u {anb2n\ n > 1} è accettato da un APN ma non
da un AP deterministico.

Dimostrazione
La seconda parte del teorema coincide con il Teorema 4.14. Bisogna dunque solo
determinare un APN A in grado di accettare L. Tale automa è illustrato nella Figura
4.45. L’APN in figura salva sulla pila due A per ogni a in ingresso, poi, in modo
non deterministico, si sposta in qi o in q2, dove verifica rispettivamente se il
numero di b coincide con il numero di a o è il doppio del numero di a. Se almeno
una possibile computazione porta in uno stato di accettazione leggendo tutta la
stringa in ingresso, questa fa parte del linguaggio.
172
Informatica teorica

Figura 4.45 Un APN che accetta L = {anbn\ n > 1} u {anb2n\ n > 1}.

ESERCIZIO
4.61 Si progetti un APN che riconosce il linguaggio
L= w e {a, b}*}

Poiché gli APN sono più potenti degli AP, la classe di linguaggi riconosciuti dai
primi è più ampia di quella riconosciuta dai secondi e di conseguenza non è detto
che valgano le stesse proprietà di chiusura. Analizziamo brevemente cosa cambia.
Avevamo visto che i linguaggi riconoscibili da AP sono chiusi rispetto al
complemento, ma non rispetto all’unione e all’intersezione. Nel caso degli APN le
proprietà di chiusura cambiano. Infatti, gli APN sono chiusi rispetto all’unione.
Intuitivamente, presa una qualsiasi coppia di linguaggi riconoscibili da APN, è
sempre possibile costruire un APN che riconosce l’unione, aggiungendo uno stato
iniziale che viene collegato tramite due e-mosse agli stati iniziali degli automi di
partenza, ottenendo così l’APN che riconosce l’unione.
Gli APN rimangono invece non chiusi rispetto all’intersezione, infatti il non
determinismo non permette di superare alcuni limiti della memoria a pila, che
rimane una memoria distruttiva. In particolare gli APN non possono riconoscere il
linguaggio {anbncn\ n > 1} che può essere ottenuto come intersezione di due
linguaggi riconoscibili da APN ({a*è"c"| n > l}e{a*bncn\n > 1}).
Infine, essendo chiusi rispetto all’unione, ma non rispetto all’intersezione, non
possono essere chiusi rispetto al complemento (ricordiamo infatti che l’intersezione
di due insiemi può essere riscritta in funzione di unione e complemento).
Passiamo ora a considerare l’effetto del non deterinismo sulle macchine di
Turing.
173
Automi

Definizione 4.25
Le macchine di Turing non deterministiche (MTN) vengono definite come le loro
corrispondenti deterministiche, con la solita differenza che, per una MT
multinastro, le funzioni di transizione e di uscita sono date da:
<5,r|):(e-F) X / X FÀ A p(0 X T* X {R,L,S}k+ì X {R,S})

mentre, per una MT a nastro singolo, 6 è definita come


ò:(Q-F) X A $>(Q X A X {R,L,S}~).

Si osservi che a differenza di quanto avvenuto per gli APN, nella definizione della
funzione di transizione delle MTN non è stato necessario specificare che l’insieme
delle parti sia finito. Questo è dovuto al fatto che l’insieme
Q X T* X {R,L,S}k +1 X {R, 5} è già finito, poiché viene costruito dal prodotto
cartesiano di insiemi finiti.

ESERCIZIO
4.62 Si formalizzi il comportamento delle MTN quando vengono impiegate in
qualità di accettori di linguaggi, trasduttori di linguaggi e valutatori di
funzioni. Come nel caso degli altri automi, una MTN accetta una stringa se
esiste almeno una sequenza di mosse che conduce ad uno stato finale. La
MTN definisce quindi una funzione a più valori che associa la stringa di
ingresso all’insieme di tutti i possibili valori di uscita ottenuti mediante una
sequenza consentita di mosse.

Analogamente a quanto avvenuto per gli AFN rispetto agli AF, il non
determinismo non aggiunge potenza alle MT, come formalizzato dal seguente
teorema.

Teorema 4.23
Le macchine di Turing non deterministiche non sono più potenti delle macchine di
Turing deterministiche, se usate in qualità di riconoscitori di linguaggi.

Dimostrazione informale
Per dimostrare che le MTN non sono più potenti delle MT è necessario mostrare
che, data una qualsiasi MTN M, è sempre possibile costruire una MT M’ che
riconosce lo stesso linguaggio di M. Supponiamo, senza nessuna perdita di
generalità, che Msia una MTN a nastro singolo.
174
Informatica teorica

Poiché la funzione di transizione di M è definita da (Q - F) X A a


p(£> X A X {R, L, S}), se si considera una computazione di M su una stringa in
ingresso, essa è ben descritta da un albero di configurazioni, in cui è inserita ogni
configurazione raggiungibile dalla configurazione iniziale, in manera simile a
quanto avviene anche per gli altri tipi di automi (si vedano l’Esercizio 4.56 e la
correlata Figura 4.43). Una parola viene accettata se esiste almeno un cammino che
termina in una configurazione fìnale.Un esempio di albero di configurazioni per
una MTN è mostrato in Figura 4.46, dove i cerchi indicano configurazioni di
accettazione, i rettangoli configurazioni di halt ma non di accettazione e le linee
tratteggiate computazioni che non terminano.
M’ potrà perciò simulare M, ricostruendo in maniera sequenziale tutte le
possibili computazioni di questo risultato si può ottenere in modo classico
mediante un opportuno algoritmo di “visita” dell’albero delle computazioni di Afe
codificando l’algoritmo mediante la macchina di Turing (esercizio noiso ma
sicuramente fattibile).
Si noti tuttavia che, poiché, quando una parola non viene accettata, ima MT
può entrare in un ciclo infinito e, quindi, non terminare mai la sua computazione,
alcuni rami dell’albero di computazione potrebbero essere infiniti. La MT M’, che
simula M, deve quindi evitare di visitare l’albero delle computazioni “in
profondità” (o depth first), ossia seguendo un percorso fino al suo termine, prima di
passare eventualmente ad un altro mediante backtrack', piuttosto conviene che essa
lo visiti in ampiezza (o breadth first). In tal modo, se esiste un cammino dell’albero
di M che porti ad accettare la stringa, M’prima o poi lo troverà e si

Figura 4.46 Albero delle configurazioni di una MTN.


Automi 17i>

fermerà accettando la stringa; altrimenti, se tutti i cammini di M terminano in stati


non finali, M’ pure terminerà la sua computazione senza aver trovato neanche un
modo di accettare la stringa e la rifiuterà esplicitamente; se invece qualche
cammino di M’ non termina e nessun cammino porta in uno stato di accettazione,
M’ pure, continuando a simulare tutti i cammini di M, non terminerà la sua
computazione e la stringa non sarà dunque accettata.
M’ rappresenta una sequenza di mosse di M nel modo seguente: un numero
intero positivo viene associato a ogni possibile mossa di M in modo da identificarla
univocamente; l’albero delle computazioni di M, per ogni stringa di ingresso, viene
etichettato associando ad ogni nodo il numero della mossa utilizzata per giungere
alla corrispondente configurazione; convenzionalmente, per ogni ramificazione
dovuta ad ima scelta nondeterministica, i figli vengono etichettati in ordine
crescente da sinistra a destra. In questo modo, la sequenza delle etichette sui nodi
del cammino, partendo dalla radice, identifica univocamente il cammino
sull’albero.
M’ sarà quindi una MT a 2 nastri, che chiameremo rispettivamente nastro di
lavoro e nastro guida, che opera nel seguente modo per simulare M\
Passo 1 : La stringa in ingresso viene copiata sul nastro di lavoro e il nastro
guida viene inizializzato a 1, ad indicare la prima possibile
sequenza.
Passo 2: Viene simulata la sequenza di mosse contenuta nel nastro guida. Se
il numero correntemente in lettura non corrisponde a una possibile
scelta, M’abbandona l’esecuzione della sequenza di mosse.
Passo 3: Al termine della sequenza di mosse memorizzata sul nastro guida o
in caso di abbandono prematuro, se lo stato della MTN M simulata
da M’ è di accettazione, M’ si ferma e accetta la stringa, altrimenti,
il nastro di lavoro viene cancellato e le testine riportate sulla prima
cella e M’ genera la sequenza di mosse successiva sul nastro guida
e copia nuovamente l’ingresso sul nastro di lavoro per cominciare
nuovamente dal Passo 2.
Si noti che le possibili sequenze vengono generate algoritmicamente da M’ in
modo univoco come sequenze sull’alfabeto D={1, 2, ..., r}, dove
r = max {| ò(q,a) |} , ad esempio, seguendo una numerazione per lunghezza
qe.Q ,ae.A
crescente e per ordine numerico delle etichette delle mosse di M'. 1,2, ..., r, 11, 12,
...Ir, 21, 22, ... . In questo modo si ottiene, appunto, una visita dell’albero in
ampiezza.
M’ è quindi una MT a 2 nastri che simula M, eseguendo tutte le possibili
sequenze dell’esecuzione di M nell’ordine dettato dalle sequenze sul nastro guida.
M’ usa il nastro di lavoro per la simulazione di ogni sequenza e conserva la stringa
sul nastro di ingresso per poterla ricopiare e avviare l’eventuale esecuzione
successiva. M’ si ferma e accetta se e solo se M si ferma e accetta, in caso contrario
176
Informatica teorica

non termina o termina senza accettare (se tutte le ramificazioni dell’albero di M


terminano a loro volta).
■'
ESERCIZIO
4.63* Si mostri che le MT deterministiche possono simulare le MTN come
trasduttori di linguaggi, quindi anche come valutatori di funzioni, a
condizione di codificare opportunamente l’insieme t(x). Ad esempio sia
t(x)={«i, ..., un} c O*. Si può allora definire una traduzione
rD(x) = ••$«», con $ £ O. Va tuttavia precisato che la simulazione
funziona propriamente solo se tutti i calcoli della MTN originale terminano
(ossia raggiungono una configurazione di arresto). In caso contrario,
l’insieme r(x) potrebbe anche essere infinito e quindi Td(x) potrebbe risultare
indefinito. Il lettore è invitato a ritornare su questo esercizio dopo aver letto i
Capitoli 8 e 9.

4.5 Un modello evolutivo intrinsecamente non deterministico: le reti di Petri


I modelli evolutivi non deterministici discussi nella sezione precedente sono stati
definiti come un’estensione della versione deterministica originale. In questa
sezione viene presentato un altro modello evolutivo, caratterizzato da un non­
determinismo intrinseco: le reti di Petri. Le reti di Petri sono da considerare alla
stregua degli altri modelli “storici e fondazionali”, come i modelli discussi finora;
esse vengono tuttavia presentate qui per le seguenti ragioni:
- Le reti di Petri vengono impiegate sempre di più in casi pratici per modellare
sistemi complessi, ad esempio sistemi di calcolo, sistemi di monitoraggio e
controllo di impianti industriali, e perfino organizzazioni umane. Esse sono un
utile strumento di specifica formale impiegabile nella fase della raccolta dei
requisiti e in generale in tutto il corso dello sviluppo dei sistemi.
- Le reti di Petri sono state ampiamente studiate, sia nelle scienze pure che in
quelle applicate, come modello per la rappresentazione di fenomeni
concorrenti. Esse uniscono al rigore dei modelli formalmente definiti una
rappresentazione grafica di grande e immediata espressività, ciò che le ha rese
molto popolari anche in ambienti non strettamente accademici.
Informalmente, una rete di Petri è composta da un insieme finito di posti,
rappresentati graficamente come i nodi di un grafo, un insieme finito di transizioni,
rappresentate come delle barre e un insieme di archi che collegano posti a
transizioni e transizioni a posti. Per capire il significato di questi elementi e come
sia possibile combinarli, consideriamo la rete in Figura 4.46.
La rete è composta da sette posti ..., P7) e sei transizioni (Zj, ..., Z6). Da un
posto possono uscire uno o più archi, ciascuno dei quali collega il posto con una
177
Automi

transizione, e in tal caso si dice che il posto è in entrata per la transizione, e,


analogamente, da una transizione possono uscire uno o più archi, ciascuno dei quali
collega la transizione con un posto e in tal caso si dice che il posto è in uscita per la
transizione. Non esistono archi che collegano in modo diretto elementi dello stesso
tipo. Un caratteritica di questo tipo è tipica dei cosiddetti grafi bipartiti.
I pallini neri all’interno dei posti P3, 7J4 e P5 si chiamano token, o gettoni o marche',
un token può posizionarsi solo nei posti e un posto può contenere zero o più token.
La descrizione della posizione dei token nella rete prende il nome di marcatura e la
posizione che assumono nel momento di definizione della rete si chiama marcatura
iniziale. Una marcatura della rete rappresenta uno stato del sistema, di cui un posto
è solo un’informazione parziale. I token servono per descrivere il comportamento
della rete, che descrive un sistema in termine di flusso di token da un posto
all’altro. Un token passa da un posto all’altro attraverso la transizione che li
collega, facendo scattare la transizione. Le uniche transizioni che possono scattare
sono quelle abilitate, cioè quelle che hanno in tutti i posti in ingresso almeno un
token. Nell’esempio in Figura 4.47 le transizioni Z3 e t4 sono abilitate poiché P3, P4
e P5 contengono un token. Tutte le transizioni abilitate possono scattare e la scelta
tra esse è totalmente non deterministica.
Quando una transizione scatta viene consumato un token per ciascun posto in
ingresso, cioè il numero di token nei posti in ingresso viene decrementato di uno e
viene aggiunto un token a ciascun posto in uscita. Lo scatto di una transizione porta
il sistema da una marcatura Mf a una marcatura M. (non necessariamente diversa da
M) e rappresenta quindi una transizione tra stati del sistema. Considerando quindi
l’esempio in Figura 4.47 e supponendo che scatti t4, alla fine si otterrà la seguente
situazione: P4 e P7 contengono un token e tutti gli altri posti sono vuoti. La nuova
situazione è tale per cui t6 è l’unica transizione abilitata.
Le possibili sequenze di scatto di una rete descrivono il comportamento, cioè
l’evoluzione, del sistema sotto analisi. Nelle seguenti definizioni formalizziamo,
generalizzandoli leggermente, i concetti che abbiamo appena introdotto
intuitivamente.

Definizione 4.26
Una rete di Petri (RP) è una 4-upla (P, T, IF, OF) in cui
- P = {pi, .. ,,pn} è un insieme finito di posti',
- T= {h, ..., tm} è un insieme finito di transizioni',
- IF: PxT —> N èiafunzione di transizione - ingresso;
— OF: TXP —> N è la funzione di transizione - uscita.
178 Informatica teorica

Figura 4.47 Esempio di rete di Petri.

Come abbiamo precedentemente visto attraverso l’esempio, una rete di Petri si può
descrivere graficamente per mezzo di un grafo bipartito (più propriamente un
multigrafo) con due insiemi di nodi: uno rappresentato in modo convenzionale da
cerchi e l’altro attraverso delle barre, cioè il primo è associato a P e l’altro,
disgiunto dal precedente (P n T = 0), è associato a T. Rispetto alla precedente
introduzione informale, la funzione IF(p,t) definisce, per ogni coppia (posto,
transizione) (p, t), il numero di archi che vanno dal posto p alla transizione t. Se
IF(p,t) è diverso da zero, allora p è un posto in ingresso (o in entrata) per t.
Simmetricamente, per ogni coppia (transizione, posto) (t, p\ OF(t,p) definisce il
numero di archi che vanno dalla transizione t al posto p. Se OF(pJ) è diverso da
zero, allora p è un posto in uscita per t.

Esempio 4.21
Consideriamo la rete di Petri (P, T, IF, OF) in cui
- P={P1,...,P5};
- t= {c,
- IF è definita dalla Tabella 4.3;
- OF è definita dalla Tabella 4.4.
179
Automi

La rete ha cinque posti e cinque transizioni e, considerate le tabelle, sappiamo ad


esempio che c’è un arco che esce da e arriva a tI? due archi che vanno da P3 a
e così via. La rete complessiva è descritta graficamente in Figura 4.48.

Tabella 4.3. Funzione transizione - ingresso IF.

\ p
T \ Pi P2 P3 P4 PS
h 1 0 2 0 0
h 110 0 0
t} 0 0 0 1 0
t4 0 0 0 0 0
Ì5 0 0 0 0 1

Tabella 4.4. Funzione transizione - uscita.

T
P Ti t2 Ts L h
Pi 0 0 0 1 0
p2 0 0 2 0 0
p3 0 0 0 0 1
P4 0 2 0 0 0
P5 1 0 0 0 0

Figura 4.48 Una rete di Petri.


180
Informatica teorica

Formalizziamo ora, il concetto di marcatura di una rete, di transizione abilitata e


scatto di una transizione.

Definizione 4.27

Una marcatura di una rete di Petri è una funzione M-.P-* N.



Graficamente una marcatura viene rappresentata da un numero M(p) di token
(indicati da punti) collocati nel nodo che rappresenta p. Come precedentemente
accennato, la marcatura M di una rete è il corrispettivo, per le reti di Petri, del
concetto generale di stato.

Definizione 4.28
Data una marcatura M di una rete di Petri, si dice che una transizione t g Tè
abilitata da M se e solo se, per ogni posto p, M(p) > IF(p, t).

Definizione 4.29
La relazione di transizione •- è definita sullo spazio delle possibili marcature, ossia
suH’insieme di funzioni {M\ M: P—> }: M <- M', se e solo se esiste una t
abilitata da Me, per ognip, M'(p) = M(p) - IF(p, t) + OF(t, p).

La relazione di transizione >- definisce quindi l’evoluzione della rete mettendo in
relazione due marcature, solo quando da una si può passare all’altra a causa dello
scatto di una transizione abilitata. Qualora si voglia rendere esplicito che il
passaggio da una marcatura ad un’altra avviene attraverso lo scatto della
transizione t, si utilizza il simbolo •—t.
Analogamente a quanto fatto con la funzione di transizione degli automi visti fino
ad ora, possiamo definire la chiusura riflessiva e transitiva, >-*, della relazione di
tranzione. M •-* M' mette in relazione due marcature se esiste una sequenza di
scatti che porta da Afa M'.

Esempio 4.22
La marcatura M mostrata in Figura 4.49a abilita due transizioni, e f2, che possono
quindi entrambe scattare. Dalla marcatura M la rete di Petri può raggiungere quindi
sia la marcatura M\, mostrata in Figura 4.49b (nel caso in cui scattasse q), che la
M2, mostrata in Figura 4.49c (nel caso in cui scattasse t2)- M2 non è in relazione con
nessun’altra marcatura, non avendo alcuna transizione abilitata, mentre M2, a sua
volta, può produrre M2, dove Mfzò ) = Àf3(p3) = Àf3(p5) = 1, Àf3(p2) = 0, Àf3(p6) = 2,
Automi 181

sparando t3, e Àf4, dove M4(pi) = MAp^) = M4(p5) = MApp = 1, M4(p2) = 0,


sparando t4.
Si noti che M >- M\ e M ■- M2, ma né Mi *-* M2 né M2 •-* Mi. Mi e M2 possono
entrambe essere raggiunte da M scegliendo di sparare ti o t2, in modo non
deterministico. Tuttavia, effettuata la scelta, il conseguente comportamento del
sistema corre su binari diversi. Le evoluzioni della rete che portano rispettivamente
a Mi e M2 sono quindi mutuamente esclusive.

Figura 4.49 Una rete di Petri (a) e le sue possibili evoluzioni (b e c).
182
Informatica teorica

Esempio 4.23
Si consideri il problema di valutare una espressione aritmetica, ad esempio (a +
ò)*(c + d). Tale problema può essere rappresentato in modo intuitivo attraverso le
RP: i posti indicano gli operandi (e quindi anche i risultati parziali ottenuti nel
calcolo dell’espressione), le transizioni indicano gli operatori, mentre la presenza di
un token in un posto indica la disponibilità del valore dell’operando
corrispondente.
La Figura 4.50 riporta la RP che rappresenta l’espressione (a + b) * (c + d).
Qualora i valori di a, b, c e d fossero disponibili, ci sarebbe un token nei quattro
posti corrispondenti e entrambe le transizioni indicate con il simbolo + sarebbero
abilitate e quindi possono scattare in qualunque ordine. La transizione marcata con
*, invece, risulta abilitata solo dopo lo scatto delle due transizioni etichettate con +,
cioè quando i risultati parziali ((a + è) e (c + d)) sono pronti.
Come si può notare dall’esempio appena considerato, le RP esprimono bene
l'ordine parziale delle operazioni e sottolineano come alcune sottoespressioni di
una espressione data possano essere parallelizzate.

Figura 4.50 Una RP che rappresenta il calcolo di un’espressione aritmetica.


Automi 183

ESERCIZI
4.64 Per la RP di Figura 4.48 si trovino tutte le marcature M raggiungibili dalle
marcature iniziali Mo sotto elencate, ossia tutte le Mtali che Mo >-* M.
1. Àf0(P4) = 1, M0(pi) = 0 per i * 4.
2. Mo(pì) = 1, = 0 per z #5.
3. À/o(p5) = 2, Mfe) = 1, = 0 per z #2,5.
4.65 Analogamente a quanto fatto nell’Esempio 4.23 si descriva una RP per
ciascuna delle seguenti espressioni:
1. ((a + * (c + </)) + (e -f *g))
2. (a + b * (c + d)) + a
3. a-(b + c* (b + (d-c* b)))

Le reti di Petri sono dunque un modello evolutivo non deterministico, poiché può
accadere che da ima marcatura M, dove più di una transizione è abilitata, la rete
evolva in modi diversi a seconda della transizione che scatta, cioè, M '-f M' e

Può inoltre succedere che M |-;1 Mi '-a A6 e M M2 |-,i M3, nel qual caso ri e
t2 si dicono concorrenti, per M, poiché entrambe possono verificarsi, partendo da
M, in qualunque ordine ottenendo la stessa marcatura, cioè l’ordine in cui scattano
non influenza la marcatura che viene raggiunta. Se consideriamo nuovamente
l’esempio in Figura 4.47 possiamo osservare che ri e t2 sono concorrenti; infatti,.se
in una marcatura in cui sono entrambe abilitate, una delle due scatta, l’altra rimane
abilitata.
Quando, invece, a partire da una configurazione M in cui ci sono due
transizioni abilitate, lo scatto di una porta in una marcatura dove l’altra transizione
non è abilitata, tali transizioni si dicono in conflitto, in quanto la scelta di ima di
loro impedirebbe all’altra di sparare. Le transizioni t3 e t4 della rete di Petri in
Figura 4.47 sono in conflitto.
Esistono diverse caratteristiche che una rete di Petri può avere rispetto a una
data marcatura M; tra queste risulta particolarmente interessante il numero
massimo di token che si possono generare a partire da M.

Definizione 4.30
Una rete di Petri si dice k-limitata rispetto alla marcatura M se e solo se, per ogni
marcatura M'tale che M >-* M' si ha che il numero di token nei posti della rete è al
massimo k, cioè Vp e P, M'(p) < k.
Una rete di Petri è limitata se esiste un k tale che la rete è ^-limitata.
184
Informatica teorica

4.5 .1 Le reti di Petri come riconoscitori di linguaggi


Nelle prossime sezioni vedremo diversi esempi di applicazione delle reti di Petri
per modellare ed analizzare sistemi concorrenti di varia natura. Seguendo tuttavia
lo schema generale adottato in questo capitolo, iniziamo mostrando l’uso della
nuova macchina a stati come riconoscitore di linguaggi formali. Vi sono diversi
modi per utilizzarle a tal fine: il più classico, ed anche il più naturale, è di
considerarle come riconoscitori di un insieme di sequenze di sparo delle
transizioni.

Definizione 4.31
Un accettare a rete di Petri (ARP) è una 6-upla (fi, T, IF, OF, S, L, Mo, F) dove P,
T, IF, OF sono definite come nella Definizione 4.26, Mo è la marcatura iniziale, F
è un insieme finito di marcature finali, S è l’alfabeto di ingresso e L: f-> Su {s}
è una funzione che etichetta ogni transizione della rete con un simbolo dell’alfabeto
di ingresso oppure con a, nel caso di etichetta vuota.

Un ARP, quindi, non è altro che un rete di Petri per cui viene definita una
marcatura di partenza e un insieme di marcature finali e in cui le transizioni
possono essere etichettate con simboli dell’alfabeto di ingresso.

Definizione 4.32
Una sequenza di sparo (o sequenza di scatto) t, per un dato ARP R, è un elemento
di T*-t =ti1 ti2 ■■■t. e la stringa s, definita come
b zi
s =
l2 L(t.)L(t.
ìn )---L(t. ), è
accettata da R se e solo se esistono n - 1 marcature Mi, ..., M„.\ di R tali che:
Ma'-, Mt*- M2...Mn.!'~t‘ M„ conMn F
‘ '1 * '2 ‘ hi

Intuitivamente, il linguaggio riconosciuto da un APR è formato dalle stringhe che
si possono ottenere da tutte le sequenze di sparo che dalla marcatura iniziale
portano in una marcatura finale. Formalmente, dato un ARP R, il linguaggio
riconosciuto da7? è definito come L(R) = {s| s e X* ed5 è accettato da/?}.

Esempio 4.24
Si consideri la rete di Petri di Figura 4.51 e si assuma come Mo la marcatura in
figura. L’insieme delle marcature finali F è composto dalla sola marcatura MF, tale
che MfiPl) = MfiPfi = 0 e MF(P2) = 1. Alcuni esempi di sequenze di sparo che
portano dalla marcatura iniziale alla marcatura finale sono t2, tq2h, tddzhh,
tdd\ht-ìhh, ..., a cui corrispondono le stringhe c, acb, aacbb, aaacbbb, ....
La marcatura iniziale abilita sia fi che t2, ma, mentre lo scatto di fi, oltre a
aggiungere un token nel posto P2, riporta il token consumato nel posto Pi, lo scatto
185
Automi

Figura 4.51 ARP che riconosce il linguaggio L = {ancbn | n > 0}.

di t2 consuma il token in P, senza ripristinarlo, aggiungendo un token in P3. La


transizione t3 è abilitata solo quando sia P2 che P3 contengono almeno un token e,
quando scatta, consuma un token in P2 e consuma, ma subito riaggiunge, un token
in F3.
Si può quindi considerare P2 come lo stato che conta il numero di a che
compongono il prefisso della stringa. Dopo le a, per poter eliminare il token in Pr
ci deve essere una c, data dallo scatto di t3. Solo a quel punto t3 è abilitata e dovrà
scattare tante volte quanti sono i token in P2 per raggiungere la marcatura finale,
cioè il numero di b deve essere pari al numero di a.
Generalizzando si può dedurre che il linguaggio riconosciuto dalla rete di Petri in
Figura 4.51 è L = {ancbn | n > 0}. ■

ESERCIZI

4.66 Si costruiscano ARP che accettano i seguenti linguaggi.


Li = {ancbnden\ n > 0}
L2 = {ancb2n\ n > 0}
L3 = {a,b}* ■ {cnden\ n > 0}
4.67 Si determini il linguaggio accettato dalla rete di Petri di Figura 4.52 con
M)(Li) = 1, Mdp.) = 0 per tutti gli altri posti e F={M0}. Si determinino
inoltre il linguaggio accettato nel caso in cui M0(p\) = 2, M^Pi) = 0 per tutti
gli altri posti.
186
Informatica teorica

Figura 4.52 Una rete di Petri utilizzata come accettore di linguaggio.

Cerchiamo ora di capire in che relazione si trovano gli ARP con gli altri modelli
visti fino ad ora. Essi sono strettamente più potenti degli automi a stati finiti. Infatti
un AF A non può riconoscere il linguaggio L = {ancbn | n > 0}, che è invece, come
visto nell’Esempio 4.24, riconoscibile con un ARP, mentre può essere banalmente
trasformato in una rete di Petri R equivalente nel seguente modo:
- per ogni stato q, di A viene definito un posto Pi in R;
- se 8(</„ 5) = qj, si definisce una transizione 4, con L(tk) = s con il solo P, in
ingresso e il solo Pj in uscita;
- la marcatura iniziale è definita mettendo un token nel posto Pq,
- l’insieme delle marcature finali è composta da tutte le marcature che hanno un
unico token in un costo PF corrispondente a imo stato finale di A.
Qualora si considerassero però ARP limitati, essendo finito e fissato a priori il
numero massimo di token per ciascun posto, gli ARP perderebbero la capacità di
“contare” un numero non noto di simboli, come è necessario ad esempio per L =
{ancbn | n > 0}, e avrebbero la stessa potenza espressiva degli AF.
Confrontiamo ora la capacità di riconoscimento di linguaggi degli ARP con
quella degli APN.

Esempio 4.25
Si consideri il linguaggio L = {d'bncn | n > 0}, che come già visto in precedenza
non può essere riconosciuto da un APN. Tale linguaggio può essere invece
riconosciuto da un ARP, ad esempio quello riportato in Figura 4.53, con la
marcatura iniziale riportata in figura e F = {MF} tale che M^Pp) = 1 e MAPj = 0,
Vi <5.
187
Automi

Figura 4.53 ARP che riconosce il linguaggio L = {anbncn | n > 0}.

Il funzionamento dell’ARP di Figura 4.53 è il seguente: le transizioni t\ e t2 sono


inizialmente abilitate, ma, mentre lo scatto di th oltre a portare un token in P2 e P3,
riporta anche un token in Pi, lasciando così ancora t3 e t2 abilitate, lo scatto di t2
disabilita t\ e t2, portando un token in P4. Intuitivamente t\ scatta n volte, portando
n token in P2 e P3. Lo scatto di t2, sene maggiore di 0, abilita t3, che per “svuotare”
P2 dovrà scattare n volte, verificando quindi che il numero di b sia pari a n. Lo
scatto di t5 abilita f4, che, analogamente a t3, dovrà scattare n volte per “svuotare”
P3 e portarsi alla marcatura finale, controllando così il numero di c.

Gli ARP sono dunque in grado di riconoscere dei linguaggi non riconoscibili da
APN; viceversa, però, ci sono dei linguaggi riconoscibili da APN che non sono
riconoscibili da ARP.
Si consideri ad esempio il linguaggio L = {wcwR |w e {a, b}*}, che come ben
noto è riconoscibile da AP. Tale linguaggio non è riconoscibile però da un ARP.
Infatti gli ARP usano i token per contare i letterali, ma essi non hanno una
“identità”, che permetta di distinguere se un gettone è stato creato da un simbolo
piuttosto che da un altro. Una dimostrazione rigorosa del precedente ragionamento
intuitivo richiede però approfondimenti non banali ed esula dagli scopi di questo
testo.
Concludendo, gli ARP sono quindi più potenti degli AF, non confrontabili con
APD e APN e quindi meno potenti delle MT.
I linguaggi riconoscibili da ARP sono chiusi rispetto alle operazioni di unione
e intersezione, mentre non sono chiusi rispetto al complemento. Non essendo
un’analisi approfondita di questo argomento parte degli scopi di questo libro, si
188
Informatica teorica

rimanda il lettore interessato alla letteratura specializzata proposta nelle note


bibliografiche, lasciandogli in esercizio la dimostrazione della più semplice di
queste proprietà.

ESERCIZIO
4.68 Si mostri che i linguaggi riconoscibili con ARP sono chiusi rispetto all’
unione. Si suggerisce di procedere in modo analogo a quanto fatto nella
dimostrazione del Teorema 4.10

Esistono in letteratura diverse variazioni delle reti di Petri introdotte nella


Definizione 4.26, ciascuna delle quali può cambiare in modo significativo il potere
espressivo del formalismo originale.
Fra le varie alternative proposte in letteratura, analizziamo ora le reti con
l’aggiunta di archi inibitori. Intuitivamente, un arco inibitore è un tipo speciale di
arco che esce da un posto verso una transizione. Una transizione che ha in ingresso
un arco inibitore è abilitata solo se il posto a cui è connessa tramite tale arco è
vuoto. Graficamente un arco inibitore è rappresentato come un arco che termina
con un piccolo cerchio al posto della convenzionale freccia, come mostrato in
Figura 4.54, in cui la transizione t è abilitata solo se P, è vuoto.
Gli archi inibitori sono molto utili, ad esempio, per modellizzare il
comportamento di sistemi chimici e biologici, in cui la presenza di certe sostanze
chimiche o proteine impedisce a qualche reazione di avvenire. Essi risultano molto
utili anche per rappresentare le priorità fra diverse richieste, ma analizzeremo
questi aspetti più in dettaglio nella prossima sezione.
Formalmente le reti di Petri con archi inibitori sono definite come segue.

Definizione 4.33
Una rete di Petri con archi inibitori è una 5-upla (P, T, IF, OF, 1) in cui
- P, T, IF e OF hanno lo stesso significato della Definizione 4.25;
- I: P X T è la relazione di inibizione.

Figura 4.54 Rappresentazione grafica di un arco inibitore.


189
Automi

L’introduzione degli archi inibitori nelle reti di Petti aumenta il potere espressivo
del formalismo originale, rendendo le reti di Petti equivalenti alle macchine di
Turing. Anche in questo caso ci limitiamo ad illustrare l’affermazione attraverso un
esempio, rimandando alla letteratura specializzata per una dimostrazione rigorosa
dell’asserto.

ESERCIZIO

4.69 Si formalizzino i concetti di transizione abilitata e di scatto per le reti di Petti


con archi inibitori, presentate nella Definizione 4.33.

Esempio 4.26
Si consideri il linguaggio L = | w e {a, b}*} che, come visto
precedentemente in modo intuitivo, non può essere riconosciuto da un ARP. Esso
però può essere riconosciuto con l’aggiunta di archi inibitori. Il problema della
“mancanza di identità” dei token può essere infatti parzialmente superato
sfruttando la seguente idea.
Ciascun elemento dell’alfabeto viene codificato con una cifra, nel nostro caso
ogni ‘a’ viene codificato con un 1 e ogni b con uno 0, a partire dalla stringa vuota
che viene rappresentata con 1. .Ogni stringa viene poi interpretata come fosse un
numero rappresentato in binario; una stringa w corrispondeperciò in modo univoco
a un numero naturale. Ad esempio la stringa vuota viene codificata con 1 e
corrisponde quindi al numero 1, la stringa aab viene codificata con 1110 e
corrisponde quindi al numero 14. L’idea è quindi di rappresentare la stringa w con
il numero di token corrispondente alla sua codifica, per cui l’aggiunta di una ‘6’
corrisponde a raddoppiare la codifica della stringa precedentemente ottenuta,
mentre l’aggiunta di una ‘a’ corrisponde a raddoppiare tale codifica e a sommarvi
1. Con l’aiuto degli archi inibitori si colleziona quindi il numero “giusto” di token
per codificare la stringa w e, simmetricamente, sottraendo e dividendo la codifica
ottenuta, sempre con l’aiuto degli archi inibitori, si verifica che w sia seguito da
L’ARP completo è riportato in Figura 4.55, come composizione della parte (a)
e (b), che corrispondono rispettivamente alla parte che tiene traccia di w e alla parte
che riconosce Si noti che nomi uguali nelle due parti corrispondono allo stesso
posto. Il posto Pe nella Figura 4.55(q) mantiene il numero di token corrispondente a
w. Ad ogni scatto della transizione viene consumato un token di Pc e vengono
generati due token in Ptemp- Quando tutti i token in Pc sono stati consumati può
scattare t4, che è abilitata solo da una a in ingresso. Alla fine Ptemp conterrà il
numero di token che corrisponde alla stringa letta fino a quel momento. Con lo
scatto di t6 questi token verranno spostati in Pc e lo scatto di t1 riporterà il token in
Po per leggere un nuovo carattere. Un token in Po abilita anche la transizione /8 che
porta un token in Pb La parte (b) della Figura 4.55 è preposta al controllo di wR,
cioè verifica che la nuova stringa corrisponda al riflesso della stringa letta in
190
Informatica teorica

precedenza. In particolare lo scatto di fu consuma due token in Pc e ne genera uno


in P'temp (simula cioè la divisione) e f12 sottrae un elemento a Pc in caso in ingresso
ci sia una a.

(a)
191
Automi
192
Informatica teorica

Figura 4.56 Confronto del potere espressivo degli ARP, ARP con archi
inibitori, AF, AP, APN e MT.

La marcatura iniziale è quella riportata in figura mentre F = {MF}, con M^Pp = 1,


M/4Pi) = 1 e MpPi) = 0 per tutti i posti diversi da Pi e Pc.

La Figura 4.56 riassume la relazione tra il potere espressivo dei diversi accettori di
linguaggi visti fino ad ora.

4.5.2 Le reti di Petri per descrivere sistemi


Nella sezione precedente le reti di Petri sono state usate come riconoscitori di
linguaggi. Tale uso non è quello tradizionale del formalismo, che viene più spesso
utilizzato per descrivere sistemi concorrenti, senza introdurre la nozione di
marcatura finale. Cominciamo ad analizzare le potenzialità di questo strumento con
il seguente esempio.

Esempio 4.27
Si supponga che un calcolatore sia composto da una CPU e da due video-terminali
(FTi e VTi). Ciascun terminale invia al sistema un task alla volta. La CPU può
eseguire un solo task alla volta, indipendentemente dal terminale che lo ha inviato.
Se non sono noti i criteri in base ai quali il sistema operativo assegna la CPU
ai vari task, il comportamento esterno del sistema può essere descritto dalla RP di
Figura 4.57. Chiaramente, una RP siffatta consente al sistema di scegliere sempre
ti, lasciando così VT~, nella situazione di blocco individuale. Ciò potrebbe
verificarsi, ad esempio, se il sistema operativo assegnasse una priorità più alta ai
task generati da VT\.
193
Automi

Viene elaborato
il task
proveniente
da VT

Figura 4.57 Una RP che modella un calcolatore.

Si supponga invece di sapere che il sistema operativo serve in modo alternato i due
terminali FTi e VT2. Per semplificare la discussione, si supponga anche che vi sia
sempre qualche utente seduto al terminale, pronto a generare nuovi task non
appena possibile. In questo caso il sistema può essere modellato dalla rete di Figura
4.58, che impone a t\ e t2 di alternarsi. Si noti inoltre che, in questo caso, il
comportamento del sistema è deterministico.

ESERCIZI

4.70 Facendo riferimento all’Esempio 4.27, si rappresenti un sistema composto da


una CPU, due terminali T\ e T2, due banchi di memoria B e e una
stampante con il seguente comportamento. I task da attivare sono generati da
e Fe una volta generato un task può essere eseguito solo se:

CPU

Figura 4.58 Un altro modello a RP per il calcolatore di Figura 4.57.


194 Informatica teorica

1. La CPU è disponibile.
2. È disponibile un banco di memoria (Si supponga Bi sia usato
esclusivamente da T\ e B2 da T2).
Dopo l’esecuzione, i task richiedono la stampante per stampare i risultati.
4.71 Si rappresenti una variazione multiprocessore dell’Esercizio 4.70 in cui sono
presenti due diverse CPU che possono essere utilizzate sia da task generati
da Ti sia da task generati da T2 e in cui la memoria è ancora divisa in due
banchi, ciascuno però utilizzabile indistintamente da tutti i task.

Esempio 4.28
Un esempio ben noto in letteratura per mostrare le caratteristiche e le
problematiche tipiche dei processi concorrenti è il problema dei filosofi a cena
(meglio noto con il nome inglese dining philosophers). Il problema è stato
originariamente introdotto da E. W. Dijkstra nel 1965 come problema di
sincronizzazione ed è stato poi rivisitato da Tony Hoare.
Immaginiamo di avere cinque filosofi seduti intorno a un tavolo rotondo, come
rappresentato in Figura 4.59. Ciascun filosofo può svolgere una delle due seguenti
attività: pensare o mangiare (o meglio, provare a mangiare). Per mangiare un
filosofo ha bisogno di due forchette e al tavolo ci sono solo cinque forchette,
ciascuna posizionata tra due filosofi adiacenti. Quando un filosofo vuole mangiare,
deve prendere prima le due forchette che ha di lato, una a destra e l’altra a sinistra,
e questo è ovviamente possibile solo se le forchette non sono già in mano ai suoi
vicini.

Figura 4.59 Configurazione dei filosofi a tavola.


195
Automi

Il problema dei filosofi a cena è particolarmente interessante perché permette di


descrivere, in modo semplice e intuitivo, molti problemi fondamentali e
caratteristiche ricorrenti dei sistemi concorrenti. Se si assume, ad esempio, che
ciascun filosofo adotta la strategia di prendere prima la forchetta alla sua destra,
allora al momento di acquisire quella alla sua sinistra, se tutti i filosofi provano a
mangiare contemporaneamente, resterà bloccato con in mano la forchetta a destra e
impossibilitato a prendere la forchetta alla sua sinistra, che è già in mano ad un
altro filosofo (e così avviene per tutti gli altri filosofi). I filosofi si trovano quindi in
ima situazione di deadlock.
Un altro scenario di interesse è il seguente: Platone e Socrate prendono le loro
forchette, cioè le forchette 1, 2, 3, e 4 e mangiano per un certo periodo; poi,
rilasciano le loro forchette e pensano per un po’. Mentre Platone e Socrate stanno
pensando, Confucio e Voltaire fanno la stessa sequenza di azioni: acquisiscono le
loro forchette, cioè le forchette 2, 3, 4, e 5, mangiano, rilasciano le forchette e
pensano. Dopo che Confucio e Voltaire hanno finito di mangiare, la sequenza viene
ripetuta nuovamente da Platone e Socrate, che aveva già precedentemente preso la
forchetta 1, prima che Cartesio avesse la possibilità di farlo. In questo ciclico
susseguirsi di eventi, Cartesio non ha mai la possibilità di mangiare e finisce per
morire di fame (in inglese, to starve). Il termine starvation indica l’impossibilità di
un processo di andare in esecuzione per mancanza di risorse, perchè, ad esempio,
lo scheduler garantisce sempre le risorse a processi con priorità più alta.
Le reti di Petri offrono un formalismo semplice e intuitivo per formalizzare e
analizzare problemi del tipo dei filosofi a cena: la Figura 4.60 ne mostra ima
possible formalizzazione. Inizialmente tutti i posti che rappresentano un filosofo
(chiamati in figura F;) contengono un token, ad indicare che il filosofo sta
pensando, così come tutti i posti che rappresentano una forchetta (chiamati in
figura/), ad indicare che la forchetta z-esima è sul tavolo.
Quando il filosofo z-esimo si impossessa della forchetta alla sua sinistra,
supponiamo sia la /-esima forchetta, allora scatta la transizione consumando i
token in fi e Ff, quest’ultimo viene subito rimesso in posizione, e un nuovo token
viene prodotto nel posto Fsx_,i ad indicare che il filosofo z-esimo possiede la
forchetta sinistra. Ovviamente, essendo stato consumato il token nel posto fi, se il
filosofo, che ha la forchetta /-esima alla sua destra, la volesse acquisire, sarebbe
impossibilitato a farlo. Quando il filosofo /-esimo ha acquisito entrambe le
forchette, i posti F, Fxx , e F^i conterranno un token, permettendo alla transizione
tmi di scattare. Tale transizione consumerà tutti e tre i token e aggiungerà un token
in Fm,i, ad indicare che il filosofo z-esimo sta mangiando. Questo token abilita la
transizione z/z che, quando scatta, lo consuma e aggiunge un token in Ff,fi e fk (dove
la /(-esima forchetta è quella alla destra del filosofo z-esimo), ad indicare che il
filosofo z-esimo è tornato a pensare e le forchette sono nuovamente sul tavolo.
196
Informatica teorica

Fi

Figura 4.60 RP che rappresenta il problema dei filosofi a cena per cinque
filosofi.
Torniamo ora ad analizzare le reti di Petri arricchite con gli archi inibitori. Come
anticipato nella Sezione 4.5.1, tale estensione delle RP è particolarmente utile
quando si voglia entrare in maggiori dettagli sulle politiche di gestione di risorse
condivise in sistemi concorrenti, ad esempio mediante l’uso di priorità.
Supponiamo infatti di rappresentare una richiesta da parte di un processo i come un
token nel posto Ph la presenza della risorsa richiesta come un token nel posto Pr e
l’accoglimento di tale richiesta come una transizione ti>r, che ha in entrata Pt e Pre
quando scatta consuma i token in P, e Pr. Intuitivamente, se diversi processi hanno
diverse priorità, ad esempio il processo j ha priorità più alta del processo i rispetto
alla risorsa r, sarà sufficiente aggiungere un arco inibitore da Pj a tir, ad indicare
che se il processo j ha fatto una richiesta per la risorsa r, il processo i non può avere
tale risorsa. Una rappresentazione grafica di tale schema è rappresentato in Figura
4.61.
197
Automi

Figura 4.61: Parte di RP che gestisce le priorità tra richieste.

4.5.3 Le reti di Petri temporizzate

Introduciamo ora un altro tipo di estensione delle reti di Petri con lo scopo di
modellare esplicitamente gli aspetti temporali dell’evoluzione dei sistemi
concorrenti; questa estensione del modello risulta perciò particolarmente indicata
per le applicazioni “in tempo reale”. Anche aH’intemo di questa categoria di reti di
Petri esistono diversi possibili formalismi. In seguito ci riferiremo all’estensione
proposta da Merlin e Farber, che è probabilmente la più intuitiva e meglio
conosciuta estensione di RP in tale direzione.
Intuitivamente ima rete di Petri temporizzata (RPT) è una rete di Petri in cui a
ciascuna transizione viene assegnata una coppia di valori non negativi, che
rappresentano rispettivamente il minimo e il massimo tempo di scatto della
transizione dal momento in cui essa è abilitata. Una transizione abilitata dalla
presenza dei token nei suoi posti in ingresso scatterà quindi all’interno
dell’intervallo relativo indicato. Se uno dei posti in ingresso viene svuotato prima
che la transizione scatti, essa viene disabilitata e al momento in cui ritornerà
abilitata, si considererà come se non fosse stata mai stata abilitata precedentemente,
cioè si riconsidererà da 0 il tempo di attesa per il suo scatto rispetto all’intervallo
ad essa associato. Formalizziamo ora tali idee intuitive attraverso la seguente
definizione.

Definizione 4.34
Una rete di Petri temporizzata (RPT) è una 6-upla (P, T JF ,OF, eft, Ift) in cui
- P, T, IF, OF hanno lo stesso significato introdotto nella Definizione 4.26;
- eff. T . F+ è una funzione che assegna ad ogni transizione un valore che
rappresenta il minimo tempo di scatto da quando viene abilitata;
Informatica teorica

- Ift: T 2T + è una funzioneche assegna ad ogni transizione un valore che


rappresenta il massimo tempo di scatto da quando viene abilitata,
dove 2T+ ■= [0, co), indica, a seconda dei casi, l’insieme dei numeri naturali, dei
razionali non negativi o dei reali non negativi, arricchiti con il valore ‘+oo’.
Si richiede inoltre che \/t e T eft(t) <

Graficamente, i valori assegnati da eft e Ift a ciascuna transizione vengono annotati
a lato della transizione stessa sotto forma di intervallo. Si noti che per ottenere lo
stesso risultato di una rete di Petri classica è sufficiente associare ad ogni
transizione l’intervallo [0, co).

Esempio 4.29

Riprendiamo ora il problema dei filosofi a cena, introdotto nella sezione precedente
e risolto nell’Esempio 4.28. Si assuma che la sessione di “pensare” per ciascun
filosofo duri almeno dp e al più Dp unità di tempo, dopo le quali il filosofo è
affamato e cerca di prendere le forchette, in qualsiasi ordine, e di mangiare. Come
visto precedentemente una sessione di “mangiare” inizia quando il filosofo ha
entrambe le forchette, ma ora dura almeno dm e al più Dm unità di tempo.
La versione temporizzata dei filosofi a cena è formalizzata in Figura 4.62,
dove per semplicità si è rappresentato un solo filosofo, in quanto gli altri risultano
tutti identici a quello mostrato.
La scelta dei tempi da assegnare a ciascuna attività dei filosofi può essere fatta
in modo da costruire un sistema che non vada mai in deadlock. Ad esempio, se dp =
Dp = 4 dm = 4-Dm e la rete viene modificata in modo che i filosofi inizino a
mangiare la prima volta in modo sequenziale, ogni sessione successiva di
“mangiare” avverrà esattamente dopo dm unità di tempo. Questa scelta rende il
sistema completamente deterministico.

ESERCIZIO
4.72 Considerando l’Esempio 4.29, si suggerisca un insieme di vincoli per gli
intervalli di scatto, in modo da permettere un po’ di parallelismo, ma
evitando possibilità di deadlock e starvation.

La Definizione 4.34 lascia aperte alcune domande sull’interpretazione degli


intervalli di scatto. Una transizione può essere abilitata per tutto il suo intervallo di
scatto e non scattare? Una transizione può essere “forzata” a scattare o no?
199
Automi

Figura 4.62 Parte di RPT che gestisce un filosofo nella formalizzazione del
problema dei filosofi a cena.

In letteratura sono state analizzate entrambe le possibilità ed esistono sostenitori di


una e dell’altra risposta. L’assunzione più comune è che una transizione che rimane
abilitata per tutto il suo intervallo di scatto debba alla fine scattare. Tale assunzione
prende comunemente il nome di semantica forte ed è quella che abbiamo assunto
implicitamente nell’Esempio 4.29. Esistono casi in cui però l’altra assunzione,
chiamata semantica debole, viene preferita in quanto viene considerata più
consistente con la semantica delle RP originali, in cui una transizione non viene
mai forzata a scattare.
Si immagini, ad esempio, uno studente che dovrebbe consegnare la sua tesi:
una volta stampata la tesi e ottenuta l’approvazione dal relatore, esiste una finestra
di tempo in cui lo studente può consegnare la tesi, ma non è ovviamente forzato a
farlo. Tale scenario può essere rappresentato in modo naturale utilizzando RPT con
semantica debole. Si noti però che un potenziale effetto negativo della semantica
debole nelle RPT è la generazione di token “morti”, che non possono essere usati
per far scattare nessuna transizione poiché Tintervallo di scatto è scaduto.
Una definizione precisa e completa del funzionamento delle RPT comporta
l’analisi di diversi altri dettagli della semantica del modello e viene lasciata alla
letteratura più specializzata.
Qui ci accontentiamo di completare una presentazione “semiformale” del
modello e della sua semantica attraverso un ulteriore esempio largamente usato in
zvv
Informatica teorica

letteratura come “benchmark” per valutare i relativi vantaggi e svantaggi di vari


modelli e metodi formali: la modellizzazione e l’analisi di un passaggio a livello.

Esempio 4.30
Tra le varie versioni del problema del passaggio a livello presenti in letteratura,
consideriamo una versione molto semplificata in cui il sistema consiste di un solo
binario unidirezionale e di un solo treno. Uno schizzo dell’attraversamento è
rappresentato nella Figura 4.63.

Figura 4.63 Sezione critica neH’attaversamento in corrispondenza di un


passaggio a livello.

Il comportamento del sistema è il seguente:


1. La velocità del treno varia nell’intervallo [V^, Kmax].
2. La sbarra è posta di fronte a una regione chiamata “regione critica” e indicata
in figura con I. La regione critica è lunga h metri.
3. La regione critica è adiacente a una “zona di monitoraggio”, indicata con R,
che precede I ed è lunga d metri.
4. Un sensore è posizionato all’inizio di di R per segnalare l’arrivo del treno.
5. La sbarra impiega esattamente y secondi per spostarsi dalla posizione di
chiusura a quella di apertura e viceversa.
6. Quando il sensore è attivato dal treno, il controllore manda un comando per
chiudere la sbarra.
La RPT che rappresenta il comportamento di tale sistema è rappresentata in Figura
4.64, dove
dlVmax, dM d lVmin, hm hi Emax^ h IVmin.
Il sistema di controllo della sbarra funziona correttamente se quando il treno è nella
regione critica la sbarra è chiusa e se la sbarra non è chiusa quando non è
necessario, ossia quando si è sicuri che il treno non sia nella regione critica.
Automi ZU1

Figura 4.64 RPT che rappresenta il comportamento di un semplice passaggio a


livello.

Il comportamento del sistema può essere analizzato sfruttando il modello di RPT


per determinare le condizioni sotto cui i requisiti del sistema vengono
rispettati. Si può ad esempio notare che la proprietà che la sbarra sia chiusa quando
il treno è nella regione critica vale se dm > y o equivalentemente se d > Vmax'7- Il
vincolo è necessario, infatti, se dm < y, un treno può entrare nella regione critica
prima che la sbarra sia completamente chiusa. D’altra parte, se dm è
significativamente maggiore di y, la sbarra sarà chiusa molto prima che il treno
entri nella regione critica.

4.6 Alcune annotazioni sul confronto fra modelli non deterministici e quelli
stocastici
I modelli non deterministici non vanno confusi con i modelli probabilistici o
stocastici. Usando un’espressione poco rigorosa, si può affermare che, nell’ambito
dei formalismi evolutivi, i sistemi stocastici sono caratterizzati dal fatto che le
transizioni da uno stato ad un altro avvengano con una probabilità prefissata.
Esistono in letteratura molti modelli stocastici che vengono applicati sia all’interno
che all’esterno del campo dell’informatica. Alcuni di essi sono derivati dai
formalismi deterministici in modo molto simile a quelli non deterministici.
Un tipico esempio è fornito dalle catene di Markov, che sono automi a stati finiti in
cui, a ciascuna transizione, è associata una data distribuzione di probabilità.
zuz
Informatica teorica

Figura 4.65 Una catena di Markov.

In tale contesto, il semplice automa di Figura 4.65 stabilisce che il sistema, qualora
raggiunga lo stato e riceva in ingresso una a, effettuerà una transizione verso qk o
q, con probabilità, rispettivamente, del 60% e del 40%; se invece l’ingresso è b,
certamente raggiungerà qt.
In un certo senso, i modelli non deterministici descrivono un grado maggiore
di “incertezza” rispetto ai modelli probabilistici. Infatti la disponibilità di una
distribuzione probabilistica sulle transizioni corrisponde ad una maggiore
conoscenza del comportamento del sistema.
I modelli stocastici si prestano bene a rispondere a domande del tipo “Qual è
lo stato più probabile in cui si verrà a trovare il sistema se si fornisce una data
sequenza di ingresso?” oppure “Qual è il tempo medio di servizio di un sistema che
riceve clienti con una data distribuzione probabilistica?”.
Sebbene i modelli stocastici siano usati estensivamente in alcune aree
dell’informatica, come la valutazione delle prestazioni, si è deciso di non dedicarvi
ulteriore attenzione, in quanto il loro impiego è più specialistico rispetto a quello
degli altri modelli presentati in questo testo.

RIASSUNTO DEL CAPITOLO


Questo capitolo è stato dedicato all’introduzione ed alla discussione di diversi
modelli evolutivi classici deH’informatica. A questo punto, ci si aspetta che il
lettore sia in grado non solo di comprendere, scegliere ed usare i modelli presentati
finora, ma anche di estenderli o adattarli qualora ve ne fosse la necessità.
In particolare abbiamo descritto gli automi a stati finiti, gli automi a pila, le
macchine di Turing e le reti di Petri.
Si è mostrato come un modello possa essere d’aiuto nella formalizzazione e
risoluzione di problemi pratici. Un problema da risolvere si può formalizzare
(codificare) come riconoscimento di un linguaggio, come traduzione fra linguaggi
o come calcolo di una funzione.
Si è posta inoltre l’attenzione sull’utilità dei modelli non deterministici,
estendendo al campo non deterministico i modelli deterministici (gli automi), ed è
203
Automi

stato anche presentato un modello intrinsecamente non deterministico (le reti di


Petti).
Tutti i modelli sono stati confrontati fra loro dal punto di vista della potenza di
calcolo, ossia della loro capacità di risolvere una classe più o meno ampia di
problemi. Si è compreso che le MT sono formalismi massimi, in quanto
consentono di definire qualunque linguaggio definito attraverso gli altri modelli.
Molti degli esercizi proposti, l’appendice e le note bibliografiche, sono fonti
ulteriori di ispirazione e spingono il lettore ad un’ulteriore analisi verso argomenti
più avanzati.

ALTRI ESERCIZI
4.73 Si costruisca un AF che modelli l’algebra degli interi modulo k, dotato delle
operazioni Succ: [N]ì->[N]ì definita da Succ([n]k) = [(« + 1)]*, Preck
[N]£—>[N],t definita da Predi [n]k) = [(« - I)]*.
4.74 Qual è il linguaggio accettato dall’AF mostrato in Figura 4.66? Si trasformi
l’automa in un equivalente AF deterministico.
4.75 Si dimostri che l’insieme dei linguaggi riconoscibili da APN è chiuso
rispetto all’unione, alla concatenazione e alla stella di Kleene.
4.76 * Si dimostri che l’intersezione di un linguaggio riconoscibile da APN con un
linguaggio riconoscibile da AF è un linguaggio riconoscibile da APN.
4.77 Si formalizzino gli automi a pila doppia. Essi sono come i consueti AP,
tuttavia sono dotati di due pile invece che di ima. Una singola mossa dipende
dal valore contenuto sulla cima delle due pile, oltre che dallo stato corrente e
dal simbolo di ingresso. L’automa opera inoltre su entrambe le pile nello
stesso tempo. Si dia una versione deterministica e non deterministica di tale
automa.
4.78 Si dimostri che gli automi a pila doppia, sia deterministici che non
deterministici, sono equivalenti alle MT.

Figura 4.66 Un AFN.


204
Informatica teorica

Soluzioni schematiche di esercizi scelti


4.11 II TF minimo equivalente al TF di Figura 4.7 è dato in Figura 4.67.
4.14 L’automa finito soggiacente è il minimo AF equivalente all’AF soggiacente
al TF di Figura 4.7. Si noti che ciò non vale in generale.

Figura 4.67 Un TF.

4.51 Si supponga cheMabbiaci = {<7i, ..., am}, Q= {<71, ..., qn},F c, Q.


M è definita da
i- Q = {q«, q$, qrì
ii. I consistente in
1. {aj, ..., am\ ak e A}
2. {(q, a, x, y)| q e Q, a^A, x & {+, — }, y e {R, L}}
iii. 5 è definita da
1. per ò(q, a) = {q', a, S)
si definisca
8 (qa, {q, a, —,/?» = (qa,{q, a',-, R), S)
5 (q«, (q, a, -, L)) = {qa,{q, a, -, £), S)
2. per ò(q, a) = (q', a , R)
si definisca, per * = R e * = L
5 (q«, (q, a, -, *)) = (qp, (q, a , +, R), R)
3. per ò(q, a) = (q', a , F)
si definisca, per * = R e * = L
5 {qm (q, a, -, *)) = (qp, (q, a , +, L\ L)
4.a 8 (qa, a ) = (qa, (q0, a, -, R), R\ per ogni a e A
4.b 8 (#p, a ) = (qa, (q0, a, -, L\ L), per ogni a e A
4.c per □ = qa e q^, e * = R e L:
8 (□, (qj, a, +, *)) = (qp, {q}-_ b a, +, *), *)
per ogni a g A,j = 1, ..., n.
4 .d 8 (qp, (qj, a, -, *)) = {qa, (qj + l, a, -, *), *)
205
Automi

per ogni a g A,j = 1, ..., n - 1.


4 .e 8 (□, (q0, a, +, *)) = {qa, a , *)
per ogni a g A.
5 . 8 (qa, (q, a, -,*)) = {q¥, a , S)
per ogni q g F, a g A.
4.56 Sia Mj una MT a nastri che accetta F e sia M2 una MT a k2 nastri che
accetta L2. Una MT a (k\ + k2) nastri M che accetta F o L2 (rispettivamente,
Z] n L2) può essere facilmente costruita secondo le linee seguenti. L’insieme
Q degli stati di M è dato da Q. X Q2 più un unico stato finale qF. Dato un
ingresso x, M simula le mosse di Mi per mezzo dei suoi primi F nastri e della
prima componente dei suoi stati; M2 è simulata in modo analogo. Se M
raggiunge lo stato (qu q2) con e Fi o (rispettivamente, e) q2 g F2 essa
entra in qF e si ferma.
206
Informatica teorica

APPENDICE 4.A

L’equivalenza fra le MT e le funzioni ricorsive parziali


Le macchine di Turing sono “formalismi massimi”, fra quelli esaminati finora, in
quanto nessun altro modello ha la capacità di risolvere problemi che non si possano
risolvere mediante MT. Il numero di formalismi massimi, dal punto di vista della
potenza computazionale, è molto grande. È certamente inutile qui, e forse
impossibile, essere esaustivi nell’elencare tali formalismi e nell’esposizione delle
relative dimostrazioni di equivalenza. Nel Capitolo 5 ne verrà presentato un altro e
se ne mostrerà l’equivalenza con le MT. Questo dovrebbe essere sufficiente a
fornire al lettore un’idea intuitiva delle proprietà essenziali dei formalismi massimi,
nonché la capacità di ricavare, da solo, le relative dimostrazioni di equivalenza,
almeno nei casi più semplici. Vale però la pena di aggiungere un altro elemento a
questo quadro generale.
Le funzioni ricorsive parziali, come definite nella Sezione 2.4.2, sono un altro
classico formalismo massimo, generalmente correlato al dominio dei numeri
naturali. Si illustrerà ora schematicamente come le funzioni ricorsive parziali
possano definire ogni funzione computabile dalle MT. Il vice-versa è più facile da
dimostrare, quindi la sua dimostrazione è lasciata al lettore per esercizio.
Per motivi di semplicità si faranno le seguenti supposizioni, che non inficiano
la generalità dell’enunciato.
- Si considerano MT a nastro singolo.
- L’alfabeto A della MT ha solo due elementi, A = {H , 1}, qui indicati con {0,
1} (si veda il Teorema 4.19).
- La macchina si ferma se e solo se entra in uno specifico stato finale. Si lascia
al lettore per esercizio la dimostrazione del fatto che, per ogni MT M, se usata
come riconoscitore di linguaggio o come valutatore di funzione, è possibile
costruire facilmente una MT M' equivalente che soddisfi questa ipotesi.
- Le MT vengono usate per calcolare funzioni sui numeri naturali. Ciò non
causa alcuna perdita di generalità, in quanto si può stabilire un’opportuna
corrispondenza biiettiva fra N e N*, che può essere calcolata sia da una MT
che come funzione ricorsiva primitiva. Una corrispondenza di tal fatta è, ad
esempio, g: N * N dove
g(m, ...,nk) = 2n'-3n2 ... p”* .
dove pi è il numero primo z-esimo.
Sia x che/(x) vengono memorizzati sul nastro della macchina con codifica in base
unaria secondo la seguente regola.
207
Automi

La configurazione iniziale è (q0, Y4i...Ax) con Ai =A2= ... = X-i = 0, Ax = 1; la


configurazione finale è, se esiste, (qF, L8i ... Bf(x)) con B} = B2 = ... = 5^)-i = 0,
^ = 1-
I punti seguenti spiegano come costruire una finizione ricorsiva parziare RFM
che denoti la finizione calcolata dalla MT M.
1. Codifica dello stato di M.
Si supponga che la configurazione di Msia c = {q, a/la2) con oq = XF.Xr, a2
= Yj...Ys. Essa può essere rappresentata da una 4-upla di numeri naturali c =
{q ,a,m,ri) dove

- q è il numero che codifica lo stato q (Q è l’insieme dei numeri che


codificano gli stati di Q).
- ere {0,1}, è il simbolo corrente letto dalla testina di M.
r

- m = ^Xr_k-2k rappresenta la configurazione del nastro alla sinistra


1=0
della testina di M. Essa viene interpretata come numero binario, la cui
cifra meno significativa è quella alla sinistra della testina di lettura.
s
- n=^Yk-2k~2 rappresenta la configurazione del nastro alla destra della
4=2
testina. Essa viene interpretata come un numero binario letto da sinistra a
destra.
Si noti che m, a ed n rappresentano in maniera biunivoca la configurazione
del nastro.
2. Codifica della mossa di M.
Sia c*-M c . c' è ottenuto come una finizione ricorsiva primitiva di c .
Sia c = {q , a, m, n) e ò(q, a) = (p, s, y). Siano D(n) e R(n) definite dalle
equazioni n = 2 • D(n) + R(n), e 0 < R(n) < 1. Si definisca poi c ' come c ' =
(q , a , ni, n \ dove

- q ' è il numero che codifica p


- se y = R allora
- a = R(n)
- ni = 2m + s
- ri=D(n)
- sey = L allora
- a = R(ni)
- ni = D(ni)
- n' = 2n + s
- sey = S allora
- a =s
208
Informatica teorica

— ni =m
- n = n.

Si definisca la funzione YR : Q x A -> {0,1} dalla YR ( q ,a) = if y = R then 1


else 0. Le funzioni YL e Ys sono definite di conseguenza, sostituendo Le S a R.
Si ottiene
' $ '= P ’ _ _ _
- a'=R(n)-YR(q,à) + R(m)- YL (q,a)+S'Ys (q , a)\
- m' = (2m + s)- YR(q ,à) + D(m) ■ YL (q , a) + m - Ys (q ,a)\
- ri = D(n)- YR(q ,a) + (2n + s)- YL (q ,a)+ n-Ys (q,a).
Quindi, indicando c' = (Zq(c ), Za(c ), Zm(c ), Z„(c )), è immediato verificare
che Zq, Za, Zm, Z„ sono tutte funzioni ricorsive primitive.
3. Codifica della computazione di M.
Si consideri ora una sequenza di transizioni coi~Mc] E’ facile
comprendere che c, si può ottenere come una 4-upla

ct ~ (d’glA co )’ ^a(A co )’ d’mfA co )’ d’nlA co ))


dove sono funzioni ricorsive primitive. Infatti,
q , a, m, n) = q , per ogni a,m,n
4>q(t + 1, q , a, m, ri) =
4>q(t, Zq( q a, m, ri), Za( q , a, m, ri), Zm( q , a, m, ri), Z„( q , a, m, ri))

(per ogni a, m, n e per ogni t > 0. cpq si può facilmente dimostrare che è
primitiva ricorsiva, purché Zq, Za, Zm e Zn siano anch’esse ricorsive
primitive). Si noti, tuttavia, che <pq non è definita dall’applicazione
immediata della ricorsione primitiva.
Analogamente, si possono definire le funzioni <pa, 4ym, <t>n e si può dimostrare
che sono ricorsive primitive.
4. Definizione delle funzioni MT-computabili come funzioni ricorsive parziali.
Sia fM la funzione sui numeri naturali calcolata da M secondo le precedenti
convenzioni. M si ferma all’istante tf (se ciò avviene) tale che
Q f , ossia,

tf= in(4>q(t, , «o, m0, n0) - qf ) = 0

Si noti che tfè definita se e solo se M si ferma.


Infine, la funzione ricorsiva primitiva C: N —> N sia definita da

C(x) = if x = 2X' • 3*2... px„" , doveè il numero z'-esimo, then


Automi 209

ossia, C(x) è l’esponente del numero primo 2 nella fattorizzazione di x nel


prodotto di potenze di numeri primi.
Allora

fu(x) = Vo, 0, 0, 2%) - qf ) = 0), ?0,0, 0, 2Q.

Ciò conclude la dimostrazione del seguente teorema.

Teorema 4.24
Ogni funzione MT-computabile è una funzione ricorsiva parziale.

Corollario 4.25
Ogni funzione ricorsiva parziale si può definire mediante una sola applicazione
dell’operatore di minimalizzazione.

Note bibliografiche
Tutti i modelli presentati in questo capitolo vengono estensivamente impiegati in
campo informatico. Gli automi a stati finiti, gli automi a pila e le macchine di
Turing sono descritti praticamente in ogni libro sulla teoria della computazione.
Alcuni testi classici che coprono questi argomenti sono Ginsburg (1966), Hopcroft
e Ullman (1969), Hopcroft e Ullman (1979), Hopcroft, Motwani e Ullman (2006),
Harrison (1978), Lewis e Papadimitriou (1981), Sipser (1997). Questi testi sono
stati la fonte principale del materiale qui presentato. Essi sono anche suggeriti per
un’ulteriore lettura nel campo degli automi, dei linguaggi e della teoria della
computazione.
I lavori originali più importanti che hanno originato i risultati qui presentati
sono dovuti a Bar - Hillel, Perles e Shamir (1961), (Pumping Lemma), Myhill
(1957), Nerode (1958), (teorema di Myhill - Nerode) e Turing (1936) (macchine di
Turing).
La dimostrazione del Teorema 4.13 è dovuta a Ginsburg e Greibach ed è
riportata in Harrison (1978). La dimostrazione di Ginsburg e Greibach è stata
riveduta e corretta da Citrini, Crespi-Reghizzi e Mandrioli (1986). Il Teorema 4.18
è dovuto a Shannon (1956). Lo schema di dimostrazione dato qui è adattato da
Brady (1977).
Le reti di Petri non sono ancora un modello “classico” dell’informatica, ma
sono state analizzate profondamente nelle loro proprietà matematiche e sono state
anche ampiamente applicate a problemi pratici. Il lettore interessato può trovare
una descrizione ampia e di facile lettura di questo modello in Peterson (1977,
1981).
Z1U
Informatica teorica

Inoltre, esse hanno ispirato molti modelli utilizzati anche in ambito industriale.
Alcuni esempi sono gli activity diagram di UML e BPMN (Business Process
Modeling Notation), di cui si può trovare una descrizione nel libro di Allweyer
(2009).
Sempre in Peterson (1981) è possile trovare un’ampia descrizione dell’uso
delle reti di Petri come accettori di linguaggi, compresa la dimostrazione di
chiusura dei linguaggi riconosciuti da tali modelli rispetto all’intersezione. Altre
pubblicazioni importanti ed originali sulle reti di Petri e relative applicazioni sono
Petri (1962) e Holt e Commoner (1970).
Una visione generale sull’uso dei modelli stocastici e delle loro applicazioni
all’informatica si può ottenere da Kleinrock (1976).
Capitolo 5

GRAMMATICHE

Come abbiamo visto nel Capitolo 4, molto spesso gli automi vengono usati come
modelli astratti nei problemi di riconoscimento dei linguaggi. Un riconoscitore è,
quindi, uno strumento formale per la definizione di un particolare linguaggio,
quello costituito daH’insieme di tutte le stringhe accettate dal riconoscitore. Se A è
un riconoscitore ed I è il suo alfabeto di ingresso, si usa la notazione
L(A) = {x| x e /*, x è accettata da A}
per indicare il linguaggio definito da A. Questo tipo di formalismo è uno strumento
operativo che definisce un linguaggio, fornendo una procedura meccanica per
stabilire l’appartenenza o meno di una stringa al linguaggio definito.
Esistono però altri tipi di formalismi per descrivere linguaggi. In questo
capitolo viene presentato un altro dispositivo formale utilizzabile per questo scopo:
le grammatiche formali. Una grammatica formale definisce un linguaggio fornendo
il procedimento, mediante il quale si può derivare ogni stringa appartenente al
linguaggio; si tratta quindi di un meccanismo di tipo generativo.
Secondo la normale accezione del termine, una grammatica è un insieme di
regole per costruire le frasi di un particolare linguaggio, di qualsiasi tipo esso sia.
Una grammatica formale genera le stringhe di un linguaggio attraverso un
meccanismo di riscrittura (più spesso chiamato con il termine inglese rewriting')
definito matematicamente. Esso consiste in un ampio insieme di tecniche che
determinano come sostituire parti, o meglio sottotermini, di una “formula” con altri
termini. Tali tecniche si applicano in svariati campi, quali la matematica,
l’informatica e la logica, oltre, naturalmente, la linguistica. A seconda del contesto
dove ci si trova, la “formula” diventa quindi una frase di un linguaggio naturale,
un’equazione, un frammento di programma, uno spartito musicale ecc.
Constateremo inoltre che il processo di sostituzione di un sottotermine è
potenzialmente non deterministico, cioè un sottotermine può essere sostituito da
diversi termini la cui scelta non è determinata da nessun evento o storia precedente.
Esempi di riscrittura sono le regole che permettono di riscrivere una formula di
logica proposizionale con una formula semanticamente equivalente ad essa oppure
la costruzione di esempi di tautologie.
Anche la tecnica con cui si costruiscono le frasi in linguaggio naturale è
generalmente una tecnica di riscrittura. Ad esempio, è possibile costruire le frasi di
un linguaggio applicando le seguenti regole:
- Una frase è composta da un soggetto seguito da un predicato;
- Il soggetto può essere un nome oppure un pronome;
- Un predicato può essere un verbo seguito da un complemento;
212 Informatica teorica

e così via. Analogamente è possibile costruire programmi attraverso semplici


regole, che determinano la struttura del programma, come ad esempio le regole:
- Un programma consiste di una parte dichiarativa seguita da una parte
eseguibile;
- Un’istruzione può essere un’istruzione semplice o un’istruzione composta;
- Un’istruzione composta può essere un’istruzione condizionale o ... ;

In generale, un meccanismo di riscrittura è un insieme di regole linguistiche, di cui


una descrive Voggetto principale (ad esempio, la frase, il programma) come
sequenza di componenti. Ogni componente può poi essere “raffinato” da oggetti
più dettagliati e così via, fino ad ottenere una sequenza di oggetti elementari.
Una grammatica è un meccanismo linguistico, composto dall’oggetto
principale, o simbolo iniziale, da un insieme di componenti, a loro volta da
sostituire durante il processo di derivazione, detti simboli non terminali, un insieme
di elementi di base, o simboli terminali, e dalle regole di raffinamento o
sostituzione, chiamate produzioni. Formalizziamo ora questi concetti attraverso la
seguente definizione.

Definizione 5.1
Una grammatica non ristretta (o, brevemente, una grammatica) G è una quadrupla
G={VT, Vn,P,S)Aovz
- VT è un insieme finito di simboli terminali, detto alfabeto terminale',
- VN è un insieme finito di simboli non terminali, tali che VT n Fv = 0, detto
alfabeto non terminale. V indica VT u Fv5
- P è un sottoinsieme finito di , detto insieme delle produzioni di G. Un
elemento p = (a,p) di P verrà indicato con a —> p. La stringa « è la parte
sinistra di /?; la stringa p ne è invece la parte destra',
- S è un elemento particolare di VN, detto assioma, o simbolo iniziale.

Mettiamo esplicitamente in relazione gli elementi introdotti informalmente
precedentemente con gli elementi della Definizione 5.1: un elemento che deve
essere dettagliato o raffinato è un simbolo non terminale; un elemento di base è un
simbolo terminale. Le componenti di un oggetto possono essere sia simboli
terminali sia simboli non terminali, infatti la parte destra delle produzioni
appartiene a V*. Una produzione corrisponde a una regola di raffinamento: infatti,
sostituisce un non terminale o una sequenza di non terminali con una sequenza
generica di simboli, come illustrerà in maniera più precisa le Definizione 5.2.
Grammatiche 213

Esempio 5.1
Consideriamo la grammatica G = (VT, Vn, P, S), dove VN = {S, A, B, C, D} è
l’alfabeto non terminale, VT = {a, b, c} è l’alfabeto terminale e S è il simbolo
iniziale. Si noti che, anche se abbiamo usato il simbolo S anche nella Definizione
5.1, non è obbligatorio usare proprio questo simbolo come simbolo iniziale, ma è
sempre necessario specificare quale tra i simboli non terminali è quello iniziale.
Infine le produzioni sono:
P= { S^ AB,
BA -> cCD,
CBS ab,
A e}

Formalizziamo ora il concetto di generazione di linguaggio. Come anticipato
informalmente, ciò avviene attraverso la nozione di derivazione, che spiega come
passare da una stringa ad un’altra applicando le produzioni.

Definizione 5.2
Data una grammatica G, si definisce su F* la relazione binaria di derivazione
immediata, indicata dalla notazione => :a => p, sussiste se e solo se a = a/ycG,
G

P = aiba2, con ab a2 e 8 g V*, y g VN+, y -» 8 g P. Come al solito, => ,


+ k

=>,=>, indicano rispettivamente la chiusura riflessiva e transitiva, la chiusura


G G

transitiva e la concatenazione di lunghezza k di => . Il simbolo G verrà omesso


G

nella scrittura => quando il contesto non farà sorgere equivoci.

Esempio 5.2
Consideriamo la grammatica G introdotta nell’Esempio 5.1. Si verifica facilmente
che aaBAS => aacCDS, sostituendo BA con cCD, come indicato nella seconda
G

produzione di G. Analogamente da bcCBSAdd possiamo ottenere bcabAdd,


sostituendo CBS con ab, come indicato nella terza produzione.

Partendo dalla definizione di derivazione possiamo finalmente definire il
linguaggio generato da una grammatica, attraverso la seguente definizione.
214 Informatica teorica

Definizione 5.3
Data una grammatica G, il linguaggio L(G) generato daG è definito come
L(G) = {x| S=^*g x, x e VT*}.

Il linguaggio generato da una grammatica è quindi costituito da tutte e sole le
stringhe di soli simboli terminali, che possono essere derivate a partire dal simbolo
iniziale S, applicando un qualsiasi numero di sostituzioni.

Esempio 5.3
Si consideri la seguente grammatica
<?! = ({(), 1}, {5},Pb5)
dove
Pi= {S->05,5-> 15,S-> 1}.
Alcuni esempi di possibili derivazioni di Gj sono:
1
5^05^01
11
5=> 0S=> 005=> 001
5=> 0S=> 015=> Oli
105 => 101
5=> 15=> 115 => 111
È immediato generalizzare questi esempi ed osservare che:

Z(Gi)= {0,1}*-1
ossia che il linguaggio generato da risulta costituito dall’insieme delle sequenze
di bit che rappresentano un numero intero dispari. Infatti, si può notare che la
grammatica ha tre possibili produzioni, due delle quali sostituiscono il simbolo non
terminale S con una stringa composta da un terminale e dal non terminale S e una
che sostituisce S con il terminale 1. Per arrivare a una stringa di soli terminali è
quindi necessario sostituire il non terminale S con 1, che essendo il simbolo più a
destra della stringa generata, la rende la rappresentazione in binario di un numero
dispari.
£(Gi) coincide quindi con il linguaggio riconosciuto dall’automa discusso
nell’Esempio 4.1.

Esempio 5.4
Si consideri la seguente grammatica.
215
Grammatiche

G2 = ({a,b}, {S},P2,S)
dove
P2 = {SaSb, Sab}.
Alcuni esempi di possibili derivazioni di G2 sono:
S^> ab
S => aSb => aabb
S => aSb => aaSbb => aaabbb
Ogni stringa che appartiene a L(G2) può essere derivata mediante un’opportuna
sequenza di applicazioni della prima produzione (che genera amSbm, m > 0), seguita
dall’applicazione della seconda produzione (che genera amabbm, > 0). In questo
modo:
£(G2)= {anbn\n> 1}
Si osservi che anche il linguaggio generato da G2 coincide con quello riconosciuto
da un automa esaminato precedentemente, precisamente l’automa a pila di Figura
4.23.

Esempio 5.5
Si consideri ora la seguente grammatica.
U3 = {{a, b, c}, {S, A, B, C, D},P3, S)
dove
P3 = }S—>aACD, A^aAC, A^>s, B-Db , CDBDc , CB BC ,
D^&}.
Alcuni esempi di derivazioni di G3 sono
S => aACD => aCD => aBDc => abDc => abc
S => aACD => aaACCD => aaCCD => aaCBDc => aaNCDc => aabCDc =>
aabBDcc => aabbDcc => aabbcc

S^> aaaACCCD => aaaCCC


Una generalizzazione immediata permette di constatare che £(G3) = {anbncn |
n> 1}, che coincide con il linguaggio riconosciuto dalla MT dell’Esempio 4.13.
Infatti, il simbolo iniziale S viene sostituito con aACD, dove il simbolo non
terminale A può essere sostituito o con e o con aAC, ottenendo quindi una stringa
del tipo anCnD. A questo punto, l’unica sostituzione "utile”, che permetta di
derivare attraverso passi successivi una stringa di soli simboli terminali, è quella
che sostituisce la stringa CD con la stringa BDc (infatti, se si riscrivesse D come £,
216 Informatica teorica

ossia si cancellasse D, non sarebbe poi più possibile derivare alcunché dai soli C
rimasti). Poiché poi CB può essere sostituita con BC, l’applicazione di questa
ultima coppia di sostituzioni, ripetuta n volte, porta a a 'BnDcn. Infine il non
terminale D può essere sostituito con s e ogni non terminale B con il terminale b,
ottenendo anbncn.

I precedenti esempi dovrebbero aver chiarito perché le grammatiche costituiscono
un formalismo generativo per la definizione dei linguaggi. Le grammatiche
descrivono la generazione di tutte le stringhe appartenenti ad un linguaggio
mediante ima successione di passi di derivazione che partono da un assioma.
Ciascun passo della derivazione impiega una produzione a -> p come regola di
riscrittura, in grado cioè di specificare in che modo effettuare la sostituzione di una
sottostringa a con un’altra sottostringa p aH’intemo di una stringa data.
Il prossimo esempio serve ad illustrare una delle principali applicazioni di
questo formalismo in campo informatico, ossia la definizione dei linguaggi di
programmazione.

Esempio 5.6
Si consideri la seguente grammatica
G = ({if, do, begin, fi, od, end, $}, {S, A}, P, S)
dove
P = {5 -> begin A end, A if A fi, A do A od, A $}.
Alcuni esempi di derivazioni di G sono
S > begin A end > begin $ end
S => begin A end => begin if A fi end => begin if $ fi end
S => begin A end => begin do A od end => begin do $ od end
S => begin A end => begin do A od end => begin do if A fi od end
=> begin do if $ fi od end

Se consideriamo il simbolo terminale $ come l’abbreviazione di un qualsiasi


assegnamento, la grammatica G genera quindi “scheletri” di programmi strutturati,
consistenti neH’annidamento di istruzioni condizionali e cicliche. Il lettore è
invitato per esercizio ad arricchire la grammatica G in modo da renderla un po’ più
realistica, ad esempio sfruttando i suggerimenti seguenti:
- Invece del simbolo non terminale A si introduca il simbolo I per indicare una
generica istruzione;
- Un’istruzione a sua volta potrebbe essere:
o Una sequenza di istruzioni separate tra loro da un nuovo simbolo
terminale (essa può essere ottenuta riscrivendo il simbolo non
terminale I come I ; 7);
217
Grammatiche

oppure
o Un’istruzione condizionale (denotata ad esempio dal nuovo simbolo non
terminale C);
oppure
o Un’istruzione ciclica (denotata ad esempio dal nuovo simbolo non
terminale L -dal termine inglese loop-)
oppure
o Un’istruzione di assegnamento (denotata ad esempio dal simbolo non
terminale A);
Un’istruzione condizionale a sua volta può essere riscritta secondo la
produzione C if (E) then I else I fi, dove E è un nuovo simbolo non
terminale che verrà riscritto in modo da definire una generica espressione;

Nelle due sezioni seguenti introdurremo alcune classi particolari di grammatiche


particolarmente rilevanti in molte applicazioni pratiche; successivamente
approfondiremo la correlazione che esiste tra grammatiche e automi, già intuita
attraverso gli Esempi 5.3, 5.4, 5.5.

ESERCIZI
5.1 Si scriva una grammatica che genera il linguaggio L = {anbm [n>m>
5.2 Si scriva una grammatica che genera il linguaggio L = {a1 b” ab" | n pari e
^1}

5.1 La classificazione delle grammatiche

La definizione delle grammatiche, come presentate in questo capitolo, è dovuta


principalmente a Noam Chomsky, linguista e filosofo statunitense. Alla fine degli
anni ’50, oltre a presentare formalmente le grammatiche, spesso note come
grammatiche generative, Chomsky ha introdotto una classificazione di esse, nota
come gerarchia di Chomsky, a seconda della forma ammessa per le produzioni.
La gerarchia di Chomsky classifica le grammatiche in quattro gruppi. La
Definizione 5.1 corrisponde alle grammatiche di tipo 0, cioè grammatiche in cui
non vi è alcuna limitazione nel tipo di produzione. Ricordiamo che tali
grammatiche sono anche note con il nome di grammatiche non ristrette.
Introducendo il vincolo che le produzioni siano solamente della forma
cc4p —> ayP, dove A è un simbolo non terminale e a, P e y sono stringhe su V, cioè
formate da simboli terminali e non terminali, si ottengono le grammatiche di tipo 1,
anche note come grammatiche sensibili al contesto. Si noti che la stringa y deve
contenere almeno un simbolo, cioè non può essere la stringa vuota e che, se sono
permesse produzioni del tipo 8, allora 5 non può apparire nella parte sinistra di
218 Informatica teorica

nessuna produzione1. Questo tipo di grammatiche tuttavia non è di particolare


interesse in questo testo e non verrà ulteriormente approfondito.
Le due restanti sottoclassi di grammatiche, cioè le grammatiche di tipo 2 e di
tipo 3, sono particolarmente importanti nei casi pratici. Esse vengono
rispettivamente chiamate anche grammatiche non contestuali e grammatiche
regolari e sono definite nel modo seguente.

Definizione 5.4
Sia G = {VT, VN, P, S) una grammatica non ristretta. Se, per ogni produzione
a —> [3 contenuta in P, si verifica |a| = 1 (ossia a g Pn), allora G viene chiamata
grammatica non contestuale (NC) o di tipo 2. Un linguaggio L è non contestuale se
e solo se è generabile da una grammatica non contestuale.

Le grammatiche definite negli Esempi 5.3, 5.4 e 5.6 hanno tutte la forma richiesta
dalla Definizione 5.4 e sono quindi grammatiche non contestuali.

Definizione 5.5
Sia G = {VT, VN, P, S) una grammatica non ristretta. Si supponga che, per ogni
produzione a —> P in P
- |a| = 1 (ossia a g Vn);
- P sia nella forma aB o nella forma a, con B g VN,a& VT, oppure sia e.
Si dice allora che G è una grammatica regolare (R) o di tipo 3. Un linguaggio L è
regolare se e solo se è generabile da una grammatica regolare.

In alcuni casi la letteratura fornisce una definizione leggermente più restrittiva di
grammatica regolare: solo l’assioma S può avere e come parte destra; in tal caso
però S non può apparire nella parte sinistra di alcuna produzione. Secondo questa
definizione le grammatiche regolari dunque, a parte l’eccezione suddetta,
contengono solo produzioni del tipo A p, dove A è un simbolo non terminale e P
è una stringa composta da un simbolo terminale o da un simbolo terminale, seguito
da un simbolo non terminale (A —> aB). Secondo altre definizioni, ancora, le
produzioni ammissibili per le grammatiche regolari ammettono parti destre
costituite da un simbolo terminale o da un simbolo non terminale, seguito da un

1 La definizione qui riportata è quella originale di Chomsky, non perfettamente compatibile


con la definizione generale adottata in questo testo, che richiede che la parte sinistra di una
produzione non contenga simboli terminali. Per questo motivo in letteratura le grammatiche
di tipo 1 vengono spesso definite attraverso la restrizione |a| < |P| per qualsiasi produzione
a —>p con a e V+, con l’unica possibile eccezione del caso S —> e e del corrispondente
vincolo.
Grammatiche 219

Figura 5.1 Classificazione di Chomsky.

terminale (A Bà). E importante osservare però che produzioni del tipo A aB e


A ~> Ba non sono mai ammissibili contemporaneamente. Non è difficile dimostrare
(sfruttando anche il Teorema seguente 5.1) che le diverse definizioni, sono tra loro
equivalenti, ossia portano a definire la stessa classe di linguaggi; queste
dimostrazioni sono proposte negli Esercizi 5.18 e 5.26.
Il linguaggio L = {0, 1}* • 1, definito nell’Esempio 5.3, è regolare: infatti, la
grammatica che lo genera rispetta le limitazioni introdotte nella Definizione 5.5.
Le restrizioni introdotte nei diversi tipi di grammatiche da Chomsky sono di
tipo sintattico e sono sempre più restrittive. Le classi di grammatiche dei diversi
tipi sono quindi strettamente contenute una nell’altra come riportato in Figura 5.1.
Si osservi inoltre che nelle Definizioni 5.4 e 5.5 abbiamo definito come non
contestuali e regolari i linguaggi generabili rispettivamente da grammatiche non
contestuali e regolari. Il termine “generabile” viene usato al posto del termine
“generato” per indicare che un linguaggio viene classificato come non contestuale
o regolare, se esiste almeno una grammatica di quel tipo che lo genera, senza
ovviamente implicare che tutte le grammatiche che lo generano siano dello stesso
tipo.
È anche importante sottolineare che il fatto che le relazioni tra classi di
grammatiche di Figura 5.1 siano di contenimento stretto, non implica a priori che
lo stesso valga per le corrispondenti classi di linguaggi: per quanto visto finora, non
si potrebbe escludere, ad esempio, l’esistenza di un teorema che dimostri che ogni
linguaggio generato da una grammatica non ristretto può essere generato anche da
una grammatica regolare o non contestuale. Constateremo tra breve, però, che non
è questo il caso e che, effettivamente, la gerarchia tra grammatiche si riflette in una
gerarchia stretta anche tra le corrispondenti famiglie di linguaggi.
Le grammatiche regolari e non contestuali hanno importanti applicazioni
pratiche nella definizione dei linguaggi di programmazione e nel progetto dei
compilatori. Infatti, la struttura lessicale dei linguaggi viene generalmente definita
mediante una R-grammatica, mentre la loro struttura sintattica da una grammatica
220 Informatica teorica

NC. Come esempio, il lettore è invitato a verificare che la struttura lessicale degli
identificatori del Pascal (o del C o di lava, Ada ecc.) (si veda anche la Figura 4.4) è
descrivibile mediante una R-grammatica. Analogamente, l’annidamento sintattico
delle strutture degli stessi linguaggi (si veda l’Esempio 5.6) può essere descritto da
una grammatica NC.

ESERCIZI
5.3 Si scriva una R-grammatica che generi il linguaggio riconosciuto dall’AF di
Figura 4.4.
5.4 Si scriva una grammatica NC che generi il linguaggio L = {wcwÀ |
w e {a, b}*}, descritto nell’Esercizio 4.21.
5.5 Si scriva una grammatica NC che generi il linguaggio
L = {anbmambn\n,rn> 1}
5.6 Si analizzi la soluzione ottenuta nell’Esercizio 5.1 e si dica se è a potenza
minima, ossia non esiste ima grammatica ad essa equivalente appartenente
ad una classe di minor potenza generativa. In caso contrario si fornisca una
soluzione a potenza minima. In caso di difficoltà si suggerisce di tornare su
questo esercizio dopo la lettura della sezione seguente.
5.7 Si scrivano le grammatiche che generano i seguenti linguaggi.
{anbn2 \n> 1}
L2 = L^

5.2 Grammatiche e automi


Nella parte iniziale del capitolo sono stati fomiti alcuni esempi di grammatiche. In
particolare, seguendo le definizioni della Sezione 5.1, possiamo osservare che
l’Esempio 5.3 ha presentato una R-grammatica G), che genera esattamente lo
stesso linguaggio che è riconosciuto dall’AF dell’Esempio 4.1. Analogamente,
nell’Esempio 5.4 si è potuta osservare una grammatica NC U2 in grado di generare
lo stesso linguaggio riconosciuto dall’AP dell’Esempio 4.9. Un’importante
questione che emerge da questi esempi è lo studio della relazione che intercorre fra
automi riconoscitori e grammatiche, per quanto riguarda le classi di linguaggi
rispettivamente accettate e generate. In questa sezione studieremo la relazione tra i
due formalismi.

Teorema 5.1
Dato un AF A, è possibile costruire una R-grammatica G ad esso equivalente, ossia
in grado di generare lo stesso linguaggio riconosciuto da A, e viceversa.
221
Grammatiche

Traccia di dimostrazione
Cominciamo con il dimostrare che dato un AF A è sempre possibile costruire una
R-grammatica G che genera il linguaggio riconosciuto da A. Sia A = (Q, I, ò, q(l, F).
Si definisca G = (I, Q, P, q0), dove gli elementi di P hanno la seguente forma.
- 5 —> Z>C se e solo se C e ò(5, b);
- B £ se B e F.
È facile constatare (la dimostrazione può essere formalizzata mediante una
semplice induzione) che L(A) = L(G); si supponga infatti che x e L(G). Esiste
allora un’opportuna derivazione tale che

qo=> aia2A2=i- ... => aia2...a„A„ => x = a1a2-.-an


G G G G G

per Ai, ..., An e Q, a\, a2,..., an e I.


Si noti che, in generale, il numero di passi di derivazione è |x| + 1 (quindi è uguale
a 1 se x = e). Infatti, per la costruzione di G, i primi |x| passi della derivazione
corrispondono ad una sequenza di mosse che fanno entrare A nello stato Am
partendo da q0. L’ultimo passo della derivazione stabilisce semplicemente cancella
A„ mediante la produzione An —> £, che esiste solo se A„ e F. Di conseguenza, x
appartiene ad L(A).
Se si suppone invece che x e L(A), allora A effettua un’opportuna sequenza di
mosse prima di raggiungere uno stato q„ e F, leggendo x. Sia q<h q\,..., qn la
sequenza di stati attraversati da A durante il riconoscimento di x, con n = |x|.

Ciascuna mossa dell’automa corrisponde ad un passo della derivazione qG => xqn.


G

Poiché qn g F, qn -> £ è in P. Quindi x g L(G).


Dimostriamo ora che, data una G-grammatica G = <VN, VT, S, P>, è possibile
costruire un AFN A = (Q, I, ò, q(t, F) che accetta lo stesso linguaggio generato da
G. Con ima costruzione simmetrica rispetto alla precedente, A è definito nel modo
seguente:
- Q = Vn {<3f} ;
- /= Vf,
- = S;
-

- S è definita come segue


o Per ogni produzione della forma A -> bC, C g S(A, b);
o Per ogni produzione della forma A ~^b,qF g S(A, b).
La dimostrazione che L(A) = L(G) viene lasciata al lettore come esercizio.

Si noti che l’automa ottenuto a partire da G è, in generale, nondeterministico. Ciò
però non costituisce una limitazione grazie alla già dimostrata equivalenza tra AF e
AFN: la maggiore, ma inutile fatica, che sarebbe stata necessaria per costruire
222 Informatica teorica

direttamente un AF a partire da una G regolare, è un’ulteriore esempio dell’utilità


dei formalismi nondeterministici.
Prima di analizzare il rapporto fra le altre classi di grammatiche e le diverse
famiglie di automi vediamo due esempi della costruzione proposta nella
dimostrazione del Teorema 5.1.

Esempio 5.7
Si consideri il linguaggio L = {0"xl'"| n > 2, m > 2, x e {0, 1}*}, cioè il linguaggio
delle sequenze di 0 e 1 che cominciano con almeno due 0 e terminano con almeno
due 1, per cui si è costruito un AF nell’Esempio 4.2. L’AF A che riconosce tale
linguaggio viene riportato per convenienza in Figura 5.2.
La grammatica che genera tale linguaggio può essere costruita utilizzando la
costruzione proposta nella prima parte della dimostrazione del Teorema 5.1. Quindi
G= Vn, P; S), dove
- VTè l’alfabeto di ingresso di A, cioè {0, 1};
- VN è composto dagli stati di A, cioè{q0, qu q2, q3, qPp,
- P è definito come segue:
o qn Oqi
o qx 0q2
o q2 0q2 | 1#3
o q3 0q2 | 1^4
o q40q2 |1^4 |s
Si noti che l’insieme delle produzioni di G contiene tante produzioni quante
sono le transizioni in A, oltre alla produzione e, dovuta al fatto che è
uno stato finale in A;
- S è lo stato iniziale di A, cioè q0.

0 1

Figura 5.2 AF che riconosce stringhe che iniziano con almeno due 0 e
terminano con almeno due 1.
223
Grammatiche

0, 1

Figura 5.3 AFN che riconosce il linguaggio Z={0, 1}*-1

Esempio 5.8
Si consideri la G-grammatica Gz dell’Esempio 5.3, definita come
Gj = ({0,1}, {S},Pi,S)
dove
A= {S-> OS, S-> 1S,S^> 1}.
Essa genera il linguaggio delle stringhe su {0, 1} che terminano con 1. Utilizzando
la costruzione proposta nella seconda parte del Teorema 5.1, è possibile costruire
un AFN Ai, che riconosce lo stesso linguaggio. In particolare Ai ha due stati, S e
qF, cioè Q = {S, qF}, l’alfabeto dei simboli terminali di Gb {0, 1} come alfabeto di
ingresso, S come stato iniziale, qF come stato finale e 8 definita nel seguente modo:
- S g 8(S, 0), S g 8(S, 1), per via delle produzioni S —> OS, S —> 1S;
- qF g 8(S, 7) per via della produzione S —> 1.
L’automa^; è rappresentato in Figura 5.3.

ESERCIZI

5.8 I Teoremi 4.8, 4.9 e 4.10 hanno dimostrato che la classe dei linguaggi
riconoscibili da AF è chiusa rispetto all’unione, all’intersezione e al
complemento. Si dimostrino i medesimi risultati per le R-grammatiche in
maniera diretta, ossia senza passare attraverso gli automi equivalenti.
5.9 Si costruisca la R-grammatica che genera lo stesso linguaggio dell’AF
dell’Esempio 4.19.

Teorema 5.2
Dato un APN A, è possibile trovare una grammatica NC G ad esso equivalente,
ossia in grado di generare lo stesso linguaggio riconosciuto da A, e viceversa.
224 Informatica teorica

Traccia di dimostrazione
Vediamo prima come dimostrare che, dato un automa A, è possibile trovare una
grammatica NC G tale che L(A) = L(G). La dimostrazione è composta da due parti.
Bisogna infatti prima mostrare la costruzione di G a partire da A e poi
l’equivalenza tra L(Z) e L(G).
Sia A = {Q, I, T, 8, q0, ZQ, F), allora è possibile costruire una grammatica
G = (VT, VN, P, S) che generi lo stesso linguaggio riconosciuto da A. Per rendere più
semplice la costruzione di G, è conveniente preliminarmente trasformare A in un
APN A ’ equivalente ad A, che riconosce cioè lo stesso linguaggio di A, ma che
svuota la pila prima di entrare nello stato finale. In primo luogo, se così già non
fosse, il simbolo iniziale di pila Zo è utilizzato esclusivamente come marca per
indicare il fondo della pila, ossia sempre e soltanto all’inizio della pila; è un facile
esercizio operare ima tale trasformazione, se necessaria; su una tale base A ’ è così
definito: A ’ = (Q u {qs, qF}, I, T, 8 q0, Zo, {qF}), dove 8 ’ aggiunge a 8 le seguenti
transizioni:
1. Vq eF, \/A eT-{Z0},<qs,£> e 8(q,e,A);
2. Vq g F, <qF, e> € ò(q, e, Zo);
3. VA g r - {Zo}, 8fe, s, A) = {<q$, e >};
4. 8(qs, e, Zo) = {<qF, £>}.
Intuitivamente le transizioni di tipo 1. portano con un £-mossa A ’ da ogni stato
finale di A allo stato di supporto qs per qualsiasi simbolo di pila tranne Zq. Nello
stato qs, con qualsiasi simbolo sulla pila tranne Zo, A ' rimane in qs, svuotando la
pila (transizioni di tipo 3.). Le transizioni di tipo 2. e 4. portano invece A’ nello
stato finale qF e svuotano completamente la pila quando è stata ridotta a contenere
solo Zo.
La non difficile dimostrazione che L(A ’) = L(A) viene lasciata per esercizio al
lettore, osservando però che la costruzione suggerita potrebbe trasformare un
automa A originariamente deterministico in un A ’ nondeterministico.
Notiamo che l’automa A ’ ha per costruzione un unico stato finale qF. Questa
premessa, insieme alla condizione di aver svuotato la pila prima di accettare una
parola, semplifica notevolmente la costruzione della grammatica NC equivalente
G. Partendo da A ’ possiamo quindi costruire la grammatica G nel seguente modo:
- vT=i-
- Lv= {[qMq']: V q, q' e Q e V M e T}; '
- L’insieme delle produzioni P contiene i seguenti elementi:
o [qMq'} —> a, ^<q', £ > g Ò(q, a, M)~ dove a può anche essere e;
o [qMq„ +1] -> a[q ’Niq2} [q\N2qr} ...[qn.2Nn. iq„. J [q„. iN„q„]
per ogni possibile scelta di qr, q2, ...,q„ <= Q,\f <q y> g Ò(q, a, M),
con y = V N2...Nn. i N„, dove a può anche essere e;
- [qoZfflp} è l’assioma della grammatica.
Intuitivamente, tale grammatica è costruita in modo da simulare il comportamento
dell’APN A. L’assioma ([qoZoqF}') “simula” le sequenze di mosse che portano dalla
configurazione iniziale (<q<j, x, Zo>), allo stato finale (qF). Le produzioni di
225
Grammatiche

secondo tipo simulano transizioni tra due generici stati q e q’, che scrivono una
stringa non vuota sulla pila, generando il non terminale che porta da q a q’ e
considerando tutte le altre possibili sequenze di generazione (che rappresentano i
possibili cammini su A ’ con la nuova condizione della pila); le produzioni del
primo tipo, invece, eliminano un simbolo non terminale sostituendolo con un
simbolo terminalem quando la corrispondente transizione nell’automa A ’ fa una
transizione che non prevede la scrittura sulla pila, quindi elimina semplicemente il
simbolo letto. Vedremo nell’Esempio 5.9 un’applicazione di questa costruzione.
Per concludere la prima metà della dimostrazione è necessario ora mostrare
che L(A 9 = L(G). Questa dimostrazione può essere fatta per induzione, la cui parte
*
essenziale consiste nel fare vedere che V q, W, q' [qWq'l => w se e solo se A
trovandosi in una configurazione in cui lo stato è q e il simbolo in cima alla pila W.
scandendo w, si porta nello stato q' e toglie W dalla pila (senza toccarne la parte
sottostante durante la scansione di w). Un’ulteriore generalizzazione permette poi
di stabilire che
*
[glPi?'] w/se e solo se (q, w, W) >-* (q', e, f.
Il completamento della dimostrazione è lasciato al lettore come esercizio (non
banale).
Passiamo ora alla seconda parte della dimostrazione: data una grammatica G è
sempre possibile costruire un APN A che riconosca lo stesso linguaggio generato
da G. Per questa parte di dimostrazione è conveniente introdurre una particolare
forma normale per le grammatiche NC, detta forma normale di Greibach (FNG).
Una grammatica NC è in FNG se tutte le sue produzioni sono del tipo A —> a[5 con
A e Jfr, a e VT e 0 g Fy*, cioè le produzioni hanno come parte sinistra un simbolo
terminale seguito da una stringa, eventualmente vuota, di non terminali. E possibile
dimostrare che ogni grammatica NC può essere trasformata in FNG (a meno della
generazione della stringa nulla che verrà considerata a parte). Il lettore interessato
può trovare dettagli su questa trasformazione nelle note bibliografiche.
Sia quindi G in FNG e quindi tale che £ <£. L(G). In questo caso l’APN
equivalente A è composto da tre soli stati e i simboli non terminali della
grammatica possono essere usati come simboli di pila. Costruiamo quindi A =
{Q, I, T, 8, q0, Zo, F) nel seguente modo:
- Q= {qo, q,qp};
- i= Vf,
- r= Fy c Zo;
- F={qp}-,
- 8 è definita come:
o 8(<?0> e, Zq) = {<q, SZq >} ;
o Per ogni produzione A —> ay con y e VN*> <q, y> 6 ò(q, a, A);
o 5(q, e, Zo) = {<qF, Zq>} .
226 Informatica teorica

Si noti che la prima transizione (ò(r/0, e, Zo) = {<?, SZ0>}) ha l’unico scopo di
aggiungere l’assioma S sulla pila, per poi simulare in q l’applicazione delle diverse
produzioni di G.
Se rimuoviamo il vincolo s g L(G), basta aggiungere una nuova transizione
<qF, s>g 8(^0, £, ^ó) (8(^0, s, Zo) = {<q, SZ0>, <qF, s>}) per considerare la
stringa vuota. Un esempio di tale costruzione è riportato nell’Esempio 5.10.
Per completare la dimostrazione è necessario ora dimostrare che L(G) = L(A).
Analogamente al caso opposto, tale dimostrazione viene fatta per induzione ed è
lasciata al lettore come esercizio.

Il Teorema 5.2 dimostra l’equivalenza tra il potere di riconoscimento degli APN
(più potenti di quelli deterministici) e il potere di generazione delle grammatiche
NC, cioè afferma che la classe dei linguaggi riconosciuti dagli APN è la stessa di
quella generata dalle grammatiche NC, cioè la classe dei linguaggi non contestuali.
Vediamo ora due esempi di trasformazione: da grammatica NC ad APN e
viceversa.

Esempio 5.9
Consideriamo il linguaggio L = {anbn\ »>!}, analizzato nell’Esempio 4.8, di cui
riportiamo l’automa riconoscitore in Figura 5.4 per convenienza.
La costruzione della grammatica G che riconosce L può essere fatta a partire
da A seguendo la costruzione proposta nella prima parte della dimostrazione del
Teorema 5.2. Notiamo che, in questo esempio, A si muove nello stato finale

u,ZqIZZq

a.ZIZZ b,Zk

Figura 5.4 AP A che riconosce L = {anbn\ n> 1}.


227
Grammatiche

quando sulla pila contiene il solo simbolo Zo, cancellandolo. Quindi A è già nella
forma richiesta dalla trasformazione proposta. In particolare, G sarà così definita:
- L’insieme dei simboli terminali Vt corrisponde all’alfabeto di ingresso di A,
cioè {a, b};
- L’insieme dei simboli non terminali è formato dai termini costruiti
combinando in tutti i modi possibili coppie di stati e simboli dell’alfabeto
della pila: {[^0Z0^0], [qoZqd, [qoZoqi], [qoZqi], [qoZoq2], [qoZq2], [qiZoqo],
[qiZq0], [qiZoqi], [q^j, [qjZ0q2], [qiZq2], [q2Z0q0], [q2Zq0], [tfzZotfi], feZ^i],
\q2Zgq2}, \q2Zq2\] . Tra questi [^0Z0^2] è l’assioma;
- Le produzioni in P sono costruite a partire dalla relazione di transizione di A
nel seguente modo:
o [^oZ?i] —> b poiché <q2, 8> e 8(7/0, b, Z)
o [^iZ^i] —> b poiché <qi,é> e 8(#i, b, Z)
o [^iZ0^2] s poiché <q2, 8> e S(gb 8, Zo)
o [^oZo^o] a[qQZqo\[qoZoqo] | alq^Zq^q^Z^qg] | a[q0Zq2][q2Z0q0]
[qoZoqi] alqoZqoJlqoZoqi] | g[^0Z7i][^iZ0^i] | a[qGZq2][q2Z^{\
[qgZgq2] alqoZqQWq^q^ | a\_qoZq\\[q\Z()q2\ | a^Zq^q^q^
poiché <qn, ZZg> e S( qQ, a, Zo)
o [<?0Zg0] a[tfoZtfo][tfoZtfo] | a^gZq^q^Zqg] | a[q0Zq2\[q2Zq0]
[qGZq{\ alqoZqollqoZq^ | alqoZq^lq^Zq^ | a^oZ^lfeZ^]
[q0Zq2] a[qoZqo][qoZq2] | alqgZq^qiZq^ | a[q0Zq2\[q2Zq2]
poiché <q0, ZZ> e 5(7/0, a, Z).
Possiamo semplificare la grammatica così ottenuta rinominando i simboli \_qiZoqj}
con Zjj, i simboli [qiZqj\ con Ay e il simbolo [qgZgq^ con S, ottenendo il seguente
insieme di produzioni
Zoo —> aAooZoo | gZqiZio | aAg2Z2g .
Zoi —> aZooZoi | aZoiZn | aAo2Z2i
S —> aAooS | aZoiZ|2 | oAq2Z22
Aqo —> ciAooAqo | aAgiAio | aAQ2A2o
Aqi —> aAooAoi | azloi^ii I oAq2A2\
Zq2—> gZooZo2 | <Zz4oiZ12 I <^402^22
Aqi —> b
Zìi —> b
Z12 —> e
Eliminando i non terminali che non possono essere sostituiti e le relative
produzioni e le produzioni non feconde si ottiene:
S —> a Z01Z12
Agi —> a AqiAh
Agi —> b
An^b
Z12 —> 8
228 Informatica teorica

Figura 5.5 APN che riconosce il linguaggio delle stringhe ben parentetizzate.

Esempio 5.8
Consideriamo ora la grammatica G = ({(,)}, {5, C}, {8 (C | (CS | (SC, C )},
8), che genera il linguaggio delle stringhe ben parentetizzate. Costruiamo ora l’AP
A che riconosce lo stesso linguaggio generato da G, sfruttando la procedura
proposta nella seconda parte della dimostrazione del Teorema 5.2. Osserviamo che
G è già in FNG e quindi possiamo direttamente costruire l’automa A senza ulteriori
passaggi. A contiene tre stati {q0, q, qF}, dove q0 è lo stato iniziale e qF lo stato
finale. L’alfabeto di ingresso di A corrisponde a VT, cioè {(, )} e l’alfabeto della
pila corrisponde al simbolo iniziale Zo unito all’insieme dei simboli non terminali
VF, cioè T = {Zo, S, C}. La funzione di transizione 8 è definita nel seguente modo:
- 8(^0, S, Zo) = <q, szo>;
- <q, C> e ò(q, (, 8) poiché S —> (C;
- <q, CS> e <5(q, (, S) poiché S —> (CS;
- <q, SC> e 8(q, (, S) poiché S (SC;
- <q, s> e ò(q, ), Q poiché C —> );
- 5(q, s, Zo) = <qF, Zo>.
La rappresentazione grafica dell’automa è riportata in Figura 5.5.
Si noti che l’AP ottenuto è non deterministico. Il linguaggio delle stringhe ben
parentetizzate non richiede il non determinismo per essere riconosciuto, ma, visto
che l’insieme dei linguaggi NC corrisponde a quello riconosciuto da APN, e
contiene quindi quello dei linguaggi riconosciuti da AP, la procedura proposta
come dimostrazione del Teorema 5.2 costruisce in generale un APN.

ESERCIZI
5.10 Si costruisca una grammatica NC che genera il linguaggio L = {wwÀ|
w e {a, b}*} (Si veda l’Esercizio 4.62).
5.11 Si costruisca un automa a pila equivalente alla grammatica seguente:
Grammatiche 229

S —> aAB
A —> aAA | bAB | c
B^> bBB \ aBA | c
Se l’automa così costruito non è deterministico, si analizzi se è possibile
costruirne uno equivalente deterministico.

Analogamente a quanto visto per la classe dei linguaggi regolari, cioè quelli
riconoscibili da AF, è possibile dare delle condizioni necessarie sulla struttura dei
linguaggi appartenenti a tale classe, attraverso una versione del Pumping Lemma
per i linguaggi NC.

Teorema 5.3 (Pumping Lemma)


Sia L un linguaggio libero da contesto. Esiste allora una costante p, dipendente solo
da L, tale che se x g L e |x| > p, allora x può essere scritta come uvwzy, in modo
che:
1. \vwz\ < p;
2. Al più uno tra v e z è la stringa vuota (yz s);
3. Vz > 0 uvwzy g L.

Intuitivamente il Pumping Lemma afferma che i linguaggi NC devono avere ima
certa regolarità nella struttura, cioè l’appartenza di una stringa sufficientemente
lunga ad un linguaggio NC implica l’appartenenza allo stesso linguaggio di un
insieme infinito di altre stringhe strutturalmente simili ad essa.
Si noti, però, che tale regolarità non è esclusiva dei linguaggi NC, cioè
possono esistere linguaggi con tale struttura che non appartengono alla classe dei
linguaggi NC. Il Pumping Lemma, fornendo solo una condizione necessaria, viene
quindi spesso utilizzato in senso negativo, cioè per dimostrare che un linguaggio
non è NC, come mostrato nell’Esempio 5.11.
Intuitivamente la dimostrazione del Pumping Lemma è basata
sull’individuazione di “derivazioni cicliche” delle grammatiche NC, del tipo
*
A => vAz, indispensabili per generare stringhe di lunghezza arbitraria e quindi
linguaggi infiniti, generalizzando l’analoga osservazione sul comportamento
ciclico degli automi a stati finiti e delle corrispondenti grammatiche regolari. I
tecnicismi di una dimostrazione completa e formale del lemma non fanno però
parte degli obiettivi di questo libro. Il lettore interessato può trovare riferimenti alla
letteratura più specializzata nei cenni bibliografici oppure può cimentarsi egli
stesso nello sviluppo della dimostrazione per esercizio.
230 Informatica teorica

Esempio 5.11
Consideriamo il linguaggio L = | n>0}, che, come abbiamo visto
intuitivamente nelle Sezioni 4.2 e 4.4 non è riconoscibile da AP e da APN e non è,
quindi, un linguaggio NC. Dimostriamolo ora rigorosamente attraverso l’uso del
Pumping Lemma, cioè verifichiamo che esso non ha la regolarità richiesta ai
linguaggi NC.
Mostriamo quindi che per qualsiasi valore di p esiste una stringa x, con |x| > n,
che non è decomponibile in uvwzy, con le cinque stringhe aventi le proprietà
richieste dal lemma.
Supponiamo per assurdo che sia x = anbncn = uvwzy, secondo le specifiche del
lemma. Allora abbiamo due casi possibili: o almeno una tra v e z contiene almeno
due simboli diversi (ad esempio z potrebbe contenere una sottostringa del tipo bkch)
o sono composte entrambe da un unico simbolo. Nel primo caso, se consideriamo
la stringa uv2w^y, che, se la scomposizione di x fosse corretta, dovrebbe
appartenere a L, notiamo che essa contiene simboli mescolati (ad esempio, b seguiti
da c e poi ancora da 6) e quindi non può appartenere a L. Nel secondo caso, cioè se
v e z contengono un solo simbolo, uv2wx2y non può avere un ugual numero di a, di
b e di c e quindi non può appartenere a L.

ESERCIZI
5.12 Si dimostri, facendo uso del Pumping Lemma, che il linguaggio L = {ww |
w e {a, b}*} non èNC.
5.13 (Eliminazione delle E-produzioni in una grammatica NC). Sia G una
grammatica NC. Si dimostri che è possibile costruire una grammatica NC G'
equivalente a G tale che
- Se a £ L(G) allora G' non ha produzioni del tipo A e.
- Se e e L(G) allora S —> e è la sola produzione di G che genera la
stringa vuota ed S non compare nella parte destra di alcuna produzione.
Suggerimenti:
- Si trovi una procedura che stabilisca, per ogni non terminale A di G, se

A => E.
G

*
- Si osservi che se A => e e .8 —> (X]Aa2, con aia2 e, è una
G

produzione di G, L(G) non viene alterato dall’aggiunta della produzione


B —> ccia2.

Rimangono da analizzare le relazioni tra le grammatiche di tipo 1 e 0 e gli automi.


Le grammatiche di tipo 1 sono formalismi equivalenti agli automi lineari limitati,
una versione ristretta di MT a nastro singolo, in cui solo una parte del nastro
Grammatiche ZJ1

contigua alla stringa in ingresso può essere letta o scritta dalla testina. Per
approfondimenti su tali automi e la loro relazione con le grammatiche di tipo 1 si
rimanda il lettore ai cenni bibliografici.
Analizziamo ora invece la relazione delle grammatiche non ristrette, o di tipo
0, con le MT.

Teorema 5.4
Le grammatiche non ristrette sono equivalenti a MT usate come accettori di
linguaggi; ossia, per ogni grammatica G non ristretta, esiste una MT M che accetta
lo stesso linguaggio generato da G, e viceversa.

Traccia della dimostrazione


Viene dapprima presentata una dimostrazione schematica del fatto che, per ogni
MT a nastro singolo M, dotata di un nastro infinito a destra, è possibile costruire
una grammatica G ad essa equivalente. Poiché questo tipo particolare di MT risulta
equivalente a tutte le altre MT, questa limitazione non provocherà alcuna perdita di
generalità.
Siano Q l’insieme degli stati, A l’insieme dei simboli (JG^A, ie(A- Vr)),
F=Q l’insieme degli stati finali e 5 la finizione di transizione di M. L(M) viene
allora definito nel seguente modo:
L(M) = {x| x e VT* (q0, Tx) ‘-‘Xq, aiTa2), ab a2 e A*, q e F}.
Per costruire una grammatica G che soddisfi la relazione L(G) = L(M), si procede
nel modo seguente. Come primo passo, G genera tutte le stringhe di tipo x$X, con
x e VT* e X costruita come una “copia di x” composta di soli simboli non terminali.
Ad esempio, se x è la stringa aba, x$X è aba$ABA. A questo punto, G simula le
diverse configurazioni di M sfruttando la stringa a destra del simbolo $. G viene
*
quindi definita in modo da avere un derivazione x$X => x se e solo se x è accettata
daAL
L’intuizione dietro a questo procedimento è quella di simulare ogni mossa di
M con una derivazione immediata di G. Per fare ciò G deve poter rappresentare le
diverse configurazioni di M e le transizioni tra esse. Una generica configurazione
{q, c/JFACfì), come mostrata in Figura 5.6, viene rappresentata dalla stringa
SaBqACp.
232 Informatica teorica

Figura 5.6 Generica configurazione di una MT.

Per simulare le mosse di Avvengono poi introdotte le seguenti produzioni:


- Se è definita ò(q, A) = <q',A', R> si definisce in G la produzione qA —>A’q’;
- Se è definita ò(q, A) = <q',A', S> si definisce in G la produzione qA qA;'
- Se è definita ò(q, A) = <q', A', L> si definisce in G la produzione
BqA q'BA' \7B dell’alfabeto di M (che, poiché M è una MT a nastro
singolo, è l’unico alfabeto usato sia ingresso, sia in memoria, sia in uscita);
- I casi particolari in cui la testina di M si trovi a alla sinistra di a o alla destra
di p sono lasciati al lettore da completare come esercizio.
Introducendo queste produzioni, si simula una transizione c '-m c ’ di M mediante
una derivazione immediata di G. Perciò^#, y) •- M (q y') se e solo se in G è
possibile derivare x$yi'q' da x$y^ y2, con yj y2= y e yi' y2' = y'.
Per completare la costruzione è infine necessario aggiungere le produzioni che
permettono a G di derivare da x$aBqFACfì la sola x solo e soltanto nei casi in cui
M giunge a una configurazione di accettazione (aBqFACfì). Queste produzioni
cancellano tutto ciò che si trova a destra di $, $ compreso.
Tratteggiamo ora la costruzione di una MT M in grado di accettare L(G) per
ogni grammatica G = (VT, VN, P, S) assegnata.
Per comodità, poiché il non determinismo non cambia il potere espressivo
delle MT, utilizzeremo per questa costruzione una MT ND. Sarebbe stato possibile
formulare questa costruzione anche senza ricorrere al non determinismo, al prezzo
però di un procedimento nettamente più complesso. Data G, una MTND M, che
accetta L(G) è una MT a un nastro così costruita:
- La stringa di ingresso x si trova nel nastro di ingresso;
- Il nastro di memoria viene inizializzato con Z0S;
- Ad ogni passo di computazione, il nastro di memoria, che in generale conterrà
una stringa a e V*, viene scandito alla ricerca della parte sinistra di qualche
produzione di P. Quando se ne trova una, che non è necessariamente la prima
trovata, operando una scelta non deterministica, essa viene sostituita dalla
corrispondente parte destra; se a una parte sinistra corrispondono più parti
destre, la scelta fra esse viene nuovamente operata in modo non
deterministico.
233
Grammatiche

In questo modo vi è in G una derivazione che porta dalla stringa a alla stringa |3 se
e solo se esiste in M una sequenza di mosse che porta dalla configurazione c =
<q, alla configurazione c ' = <q, Zop>.
Se e quando sul nastro si trova una stringa y e VT*, cioè una stringa composta
da soli simboli terminali, la si confronta con la stringa x in ingresso. Se x e y
coincidono, x viene accettata, altrimenti questa particolare sequenza di mosse non
porta a una configurazione di accettazione.
Si noti che nel caso in cui x £ L(G), M potrebbe tentare infinite sostituzioni,
alcune delle quali anche non terminanti, senza poter giustamente concludere che
x e L(G), ma neanche il contrario. Infatti, la definizione di accettazione richiede
che M giunga in una configurazione di accettazione se e solo se x e L, ma non
richiede che M termini la computazione se x £ L.

ESERCIZI

5.14 Si completi, in modo formale, la dimostrazione del Teorema 5.4.


5.15 Si scriva una grammatica che generi il linguaggio L = {a"w | w e {b, c}* e
#(b, w) = #(c, w) = n, n > 1}, dove la funzione #(z, x) denota il numero di
occorrenze del carattere i nella stringa x. Si dica poi, giustificando la
risposta, se la soluzione fornita è a potenza minima.

RIASSUNTO DEL CAPITOLO


Questo capitolo ha introdotto le grammatiche, un formalismo in grado di descrivere
i linguaggi in modo generativo, cioè fornendo un insieme di regole che permettono
di generare tutte e sole le stringhe del linguaggio che si sta descrivendo.
Insieme alla definizione di grammatica, è stato introdotto il concetto di
derivazione, che permette di definire in modo preciso il linguaggio generato da una
grammatica data.
Abbiamo poi classificato le diverse grammatiche attraverso la gerarchia di
Chomsky, che raggruppa le grammatiche a seconda del tipo di produzioni che esse
contengono, ottenendo tre classi di grammatiche: le grammatiche di tipo 0, o non
ristrette, le grammatiche di tipo 1, o sensibili al contesto, le grammatiche di tipo 2,
o non contestuali, e le grammatiche di tipo 3, o regolari.
Infine, l’ultima sezione è stata dedicata a studiare la relazione che intercorre
tra le grammatiche e gli automi, dimostrando che gli automi a stati finiti hanno lo
stesso potere espressivo delle grammatiche regolari, gli automi a pila non
deterministici lo stesso delle grammatiche non contestuali e le macchine di Turing
lo stesso delle grammatiche non ristrette.
234 Informatica teorica

ALTRI ESERCIZI
5.17 Si considerino Li = {a"è2,”| n, m> 1}, L2 = {a"è3"| n > 0}. Si costruisca una
grammatica G che generi il linguaggio L = Li n L2. È preferibile una
grammatica a potenza minima.
5.18 E noto che le due seguenti definizioni di grammatica regolare sono
equivalenti, ossia generano la stessa classe di linguaggi:
- Va —> P g P, |a| = 1 e P g VNVT u Pfu {e}
- Va —> P g L, |a| = 1 e P g VTVN 'jVTlu {e}
Si dica, giustificando la risposta, se la seguente ulteriore modifica della
definizione è anch’essa equivalente alle precedenti:
Va -> p g P, |a| = 1 e p € VNVTv Pru VTVNu {s}
5.19 Si scriva una grammatica che generi il linguaggio L = {x g {a, b, c} * |
#(o, x) = 2n, #(b, x) = 4, #(c, x) = n, n > 0}, dove la funzione # è definita
come nell’Esercizio 5.15. Si dica, giustificando la risposta, se la grammatica
scritta è a potenza minima tra quelle che riconoscono L.
5.20 Si scrivano un automa e una grammatica che, rispettivamente, riconosca e
generi il linguaggio L ={anamamam \n, m>l, n pari, m dispari}.
5.21 Si consideri il linguaggio L = {wcJP | w, W g {a, b} *; Wè una stringa che
differisce dalla stringa riflessa, vZ, di w, per al più 3 caratteri} Si scrivano
una grammatica e un automa, preferibilmente a potenza minima che generi e
riconosca L, rispettivamente.
5.22 Sia dato il linguaggio L sull'alfabeto {a, b, c} contenente tutte e sole le
stringhe che hanno le seguenti caratteristiche:
- la stringa inizia e termina con esattamente lo stesso numero di a;
- la stringa contiene almeno un carattere diverso da a.
Ad esempio, aabcaa, aca, aacabbabaa e aaaacaaaa appartengono a L,
mentre aaabaa, aaaa e aaacaccaaaaaa non appartengono a L. Si scriva una
grammatica che generi il linguaggio L.
5.23 Siano Ax e A2 due alfabeti e h una finizione da.<41 ad A2. h è estesa ad un
omomorfismo da ri]* adA2* come segue:
- ù(e) = e;
- Vx, y g Ai *, h(xy) = h(x)h(y).
Siano Li e L2 due linguaggi non contestuali.
Dire se le seguenti affermazioni sono vere o false, giustificando brevemente
la risposta:
1. Li o L2 è sicuramente non contestuale;
2. Li n L2 è sicuramente non contestuale;
3. h(Li) è sicuramente non contestuale;
4. Li o L2 è sicuramente regolare;
5. Li n L2 è sicuramente regolare;
6. ù(Li) è sicuramente regolare;
7. Se Li e L2 non sono regolari, Li o L2 è sicuramente non regolare;
8. Se Li e L2 non sono regolari, Li n L2 è sicuramente non regolare;
235
Grammatiche

9. Se L\ non è regolare, h(L.L) è sicuramente non regolare.


5.24 Si considerino le seguenti definizioni:
- Una grammatica si dice lineare a destra se le sue produzioni sono del
tipo A Bx o A —> x, con x e VT* eA,B e VN.
- Una grammatica si dice lineare a sinistra se le sue produzioni sono del
tipo A —> xB o A —> x, con x e VT* e A,B e VN.
- Una grammatica si dice lineare a destra o a sinistra se le sue produzioni
sono del tipo A Bx o A —> xB o A —> x, con x e VT* e AJÌ e VN.
- Una grammatica si dice lineare se le sue produzioni sono del tipo
A —> yBx o A xB o A x, con x,y e VT* eA,B e VN.
Si confronti la potenza di:
1. grammatiche lineari a sinistra e grammatiche lineari a destra;
2. grammatiche lineari a sinistra e AF;
3. grammatiche lineari a sinistra e grammatiche lineari a destra o a
sinistra;
4. grammatiche lineari a destra o a sinistra e grammatiche lineari;
5. grammatiche lineari e grammatiche NC.
5.25 Si consideri il linguaggio L = | n > 0, w, in {0,1} *, #(1, w;) >
#(1, w( + i), #(1, w,) > 3}. Si scriva una grammatica, non necessariamente a
potenza minima, che generi L.
5.26 In alcuni casi, come osservato a seguito della Definizione 5.5, le
grammatiche regolari sono definite in modo leggermente più restrittivo della
Definizione 5.5 proibendo che ima parte destra possa essere s, eventualmente
con l’eccezione della regola S —> s, soggetta però all’ulteriore vincolo che S
non compaia in alcuna parte destra (con lo scopo evidente di permettere la
derivazione della stringa nulla in un linguaggio regolare).
Si dimostri che tale restrizione non diminuisce la capacità generativa delle
R-grammatiche. In particolare, si modifichi la dimostrazione del Teorema
5.1 in modo che esso valga anche per questa definizione di R-grammatiche.

Soluzioni schematiche di esercizi scelti


5.1 Una grammatica che risolve l’esercizio proposto è così composta
- VT={a,b}-,
- Vn={S,A,C}~
- P= {S^aACb,A^aA\a,C^aCb\e}-,
- S è il simbolo iniziale.
5.4 Una grammatica che risolve l’esercizio proposto è così composta
- VT= {a, b, c};
- Vn={S};
- P= {S^c\aSa\bSb};
236 Informatica teorica

- S è il simbolo iniziale.
5.15 Una grammatica che risolve l’esercizio proposto è così composta

- VT= {a, b, c};


- Uv= {S,B, C,H};
- P= {S^>aSH\aH,H^>BC,BC^>CB,B^>b,C^>c,}-,
- S è il simbolo iniziale.
La grammatica appena descritta non è NC. Non è possibile generare lo stesso
linguaggio mediante una grammatica NC, perché per riconoscere L occorre
tenere aggiornati i conteggi sia delle b che delle c, per confrontarli con il
numero di a letto inizialmente.
5.18 La nuova definizione non è equivalente alle precedenti. Infatti la grammatica
G = {{a, b}, {5, A}, {5 —> aA, A —> Sb | b}, S), che genera il linguaggio
L = {anbn | n > 1}, è della forma proposta ed è noto che L non è regolare.
5.21 L è evidentemente un linguaggio non contestuale. Una grammatica che
risolve l’esercizio proposto è così composta
- VT= {a, b,c};
- VN= {S,A,B,C};
- P = {S —> c | aSa | bSb | aAb | bAa,
- A —> c | aAa | bAb | aBb | bBa,
B —> c | aBa | bBb | aCb | bCa,
C —> c | aCa | bCb}',
- S è il simbolo iniziale.
Un AP che riconosce lo stesso linguaggio è riportato in Figura 5.7. Si noti
che tale automa è stato sviluppato indipendentemente dalla grammatica
proposta, ma poteva essere ottenuto anche con il procedimento proposto
nella dimostrazione del Teorema 5.2, ottenendo però un APN.

et, Zo\AZo
a, X|jU
a, BfAB
b, Zo\BZq
b, A\BA
b, B\BB

Figura 5.7 AP soluzione dell’Esercizio 5.21.


Grammatiche 237

5.23 1. £i u L2 è sicuramente non contestuale. Siano, infatti, Gì e G2 due


grammatiche che generano rispettivamente L\ e L2 (con vocabolari
disgiunti). La grammatica G, le cui produzioni sono l’unione delle
produzioni di Gì e G2 con l’aggiunta della produzione S —> S) | S2 è
ancora non contestuale e genera Z-i u L
2. £i n £2 non è sicuramente non contestuale. Siano, infatti £] = {anbmcm | n,
m > 0} e L2 = {anbncm \n,m> 0}, allora £j n£2 = {anbncn \n,m>Q} che
è noto non essere NC.
3. /z(£i) è sicuramente non contestuale. Sia infatti Gì una grammatica che
genera £b per ottenere una grammatica G che genera /z(£i) è sufficiente
sostituire in ogni produzione di Gb a ogni occorrenza di un carattere a
dell’alfabeto £b il corrispondente carattere h(a) in£2.
Le affermazioni 4-6 sono tutte false; è, infatti, banale trovare linguaggi NC,
le cui unioni, intersezioni e immagini omomorfe non siano regolari.
Le affermazioni 7 e 8 sono entrambe false; infatti se si considerano due
linguaggi, £i e £2, che sono uno il complemento dell’altro, la loro unione e
intersezione sono rispettivamente l’insieme universo e l’insieme vuoto.
9. Se £i non è regolare, Z?(£i) può essere regolare. Si consideri, ad esempio,
£i = {«"è" | n > 0}, che è ben noto non essere regolare, e h così definita:
h(a) = c e /?(£>) = c. Z?(£i) risulta quindi essere £i = {c2” | n > 0}, cioè
l’insieme delle stringhe con un numero pari di c, che è regolare.

Note bibliografiche
Le grammatiche hanno ampio uso in informatica, soprattutto nel campo dei
linguaggi di programmazione e relativi compilatori. Il lavoro di Noam Chomsky è
stato fondamentale per lo sviluppo delle grammatiche (Chomsky 1956, Chomsky
1957, Chomsky 1965).
Le grammatiche sono inoltre descritte, come molti dei modelli presentati nel
Capitolo 4, in quasi ogni libro sulla teoria della computazione. Alcuni testi classici
che coprono questi argomenti sono Hopcroft e Ullman (1969), Hopcroft e Ullman
(1979), Kozen(1997) Hopcroft, Motwani e Ullman (2006), Harrison (1978), Lewis
e Papadimitriou (1981), Sipser (1997). Essi sono stati la fonte principale del
materiale qui presentato e sono anche suggeriti per un’ulteriore lettura in questo
campo.
Tutte le dimostrazioni presentate nel capitolo solo informalmente e la
trasformazione delle grammatiche NC in FNG sono presentate nei testi sopra citati.
In Blum e Kloch (1999), si trova invece una procedura non classica per la
trasformazione in FNG.
Esistono diversi tipi di forme normali per le grammatiche NC, non presentate
in questo testo e la cui descrizione si può trovare nei testi sopra citati. Tra esse
ricordiamo la BNF (Backus-Naur forni), un metalinguaggio usato per descrivere
grammatiche NC (Backus 1959). Essa è stata ampiamente applicata in campo
238 Informatica teorica

informatico, per descrivere la sintassi dei linguaggi di programmazione, dei


protocolli di rete e così via. L'acronimo BNF, inizialmente inteso come Backus
Nonnal Form, fu successivamente riletto come Backus-Naur Form, in onore di
Peter Naur, pioniere dei linguaggi di programmazione (Knuth 1964).
Capitolo 6
DEFINIZIONI DENOTAZIONALI DEI LINGUAGGI

In questa parte del testo abbiamo dedicato molta attenzione ad automi (Capitolo 4)
e grammatiche (Capitolo 5), visti (ma non solo) come formalismi in grado di
definire linguaggi. In questo capitolo analizzeremo altri due formalismi in grado di
definire linguaggi, ma con caratteristiche completamente diverse dai precedenti: le
espressioni regolari e le equazioni negli spazi dei linguaggi. Tra i possibili
formalismi presenti in letteratura, abbiamo scelto di trattare questi argomenti per la
loro eleganza matematica e perché offrono la possibilità di affrontare e
comprendere un diverso stile di costruzione dei modelli formali: lo stile
denotazionale. Tale stile presenta notevoli differenze rispetto allo stile
operazionale, analizzato in precedenza con automi e grammatiche, ed è quindi
interessante sottolinearne le differenze.
Nel resto del capitolo, presenteremo prima singolarmente i due nuovi modelli
e, successivamente, li confronteremo, dal punto di vista stilistico, con i modelli già
noti.

6.1 Le espressioni regolari


Un’espressione regolare (ER) è un’espressione utilizzabile per denotare un
linguaggio attraverso la struttura delle stringhe che lo compongono. Dapprima
definiamo le ER e i linguaggi da esse denotate, poi dimostreremo che la classe di
tali linguaggi coincide con la classe dei linguaggi regolari, cioè la classe dei
linguaggi riconoscibili da automi a stati finiti e generabili da grammatiche regolari.

Definizione 6.1
Dato un alfabeto di simboli terminali, che per analogia con la notazione usata per le
grammatiche nel Capitolo 5 chiameremo VT, mediante le seguenti regole si
definiscono, su di esso, le espressioni regolari e i corrispondenti linguaggi
denotati.
1. 0 è un’espressione regolare che denota il linguaggio vuoto.
2. Va e VT, a è un’espressione regolare. Il linguaggio denotato da a è costituito
dall’unica stringa a.
3. Se 7?। e 7?2 sono espressioni regolari, anche (7?, u 7?2), spesso indicata come (7?,
+ 7?2), è un’espressione regolare. Il linguaggio denotato da (7?, u7?2) è l’unione
dei linguaggi denotati da 7?j e da 7?2.
4. Se Rx e 7?2 sono espressioni regolari, anche (7?, • 7?2), spesso indicata come
(7?i7?2) è un’espressione regolare. Il linguaggio denotato da (R\ • 7?2) è la
concatenazione dei linguaggi denotati da R\ e da R2.
240 Informatica teorica

5. Se R è un’espressione regolare, anche R* è un’espressione regolare. Il


linguaggio denotato da R* è il risultato dell’operazione stella di Kleene
applicata al linguaggio denotato da R.
6. Nient’altro è un’espressione regolare.

Vi sono alcuni punti importanti da sotto lineare nella Definizione 6.1. Dapprima si
osservi che i simboli u, • e * usati nelle ER hanno lo stesso aspetto lessicale degli
operatori unione, concatenazione e stella di Kleene, ma appaiono in grassetto. Essi
si devono interpretare come operatori, definiti per le ER, che rappresentano
l’unione, la concatenazione e la stella di Kleene dei linguaggi denotati dalle
corrispondenti ER che fungono da operandi.
Osserviamo anche che le parentesi introdotte nelle regole 3. e 4. possono
essere omesse, in pratica, se non insorgono ambiguità nell’interpretazione delle
ER.

Esempio 6.1
Sia VT = {a, b}. Allora R = (a u b)* è un’espressione regolare. Infatti in base alla
regola 2. della Definizione 6.1, a e b sono ER. Poiché a e b sono ER, per la regola
3. anche (a u b) è una ER. Infine, quindi poiché (a u b) è una ER, in base alla
regola 5., R è una ER.
Il linguaggio L denotato da R è
L = {e, a, b, aa, ab, ba, bb, ...} = V *

Si noti che, in modo analogo a quanto avviene per le operazioni aritmetiche e le
operazioni insiemistiche, gli operatori u, • e *, definiti per le ER, hanno un
implicito ordine di applicazione, se non indicato diversamente dall’uso delle
parentesi. In particolare, * ha precedenza rispetto a •, che ha precedenza rispetto a
u. Quindi, in tutti i casi in cui l’applicazione desiderata degli operatori segue
l’ordine di precedenza, le parentesi possono essere omesse.

Esempio 6.2
Sia VT = {0, 1}. Allora R = ((0 • (0 u 1)*) u ((0 u 1)* • 0)) è un’espressione
regolare sull’alfabeto E, anche indicata come ((0 (0 + 1)*) + ((0 + 1)* 0)).
Osserviamo che alcune parentesi in questo caso sono ridondanti e R può essere
scritta come 0 • (0u 1)* u(0u 1)* • 0, o alternativamente come 0 (0 + 1)* + (0 +
1)* 0.
Il linguaggio denotato da R è composto dalle stringhe su {0, 1} che iniziano
con 0 (come descritto dall’espressione 0 (0 + 1)*) o finiscono con 0 (come
descritto dall’espressione (0 o 1)* • 0).
Definizioni denotazionali dei linguaggi 241

ESERCIZI
6.1 Qual è il linguaggio definito dalle seguenti ER su a e bl
Ri: (a* u è*)*
Z2: a* ■ b
R3: ((a u è)* u (a • b)*) • b • a*
6.2 Si definiscano due ER che denotano, rispettivamente, Zi = {a”ù| n > 0} e
Z2 = {x| x contiene almeno due a consecutive}.

Consideriamo ora, ad esempio, il linguaggio Z] = {w | w e {a, 6}*}.


Intuitivamente, osserviamo che non è possibile descrivere tale linguaggio
attraverso un’espressione regolare. Infatti Zi richiede di descrivere, non solo la
concatenazione di due stringhe (descrivibile tramite l’operatore •), ma anche
l’uguaglianza tra le due stringhe concatenate, di cui non è nota a priori la
lunghezza.
Questa seconda caratteristica non è rappresentabile da una ER. Infatti, gli
operatori a disposizione permettono solo di denotare l’alternativa tra due simboli o
stringhe, la concatenazione e una forma limitata di ripetizione. L’operatore * infatti
si applica a un simbolo o a una stringa e denota che la porzione a cui è applicato
può essere ripetuta zero o più volte, ma, solo in caso di stringhe finite e che non
fanno uso dell’operatore u, la stringa viene ripetuta in modo identico. Quindi,
possiamo ad esempio denotare con (aa)* il linguaggio delle stringhe su {a} che
hanno un numero di simboli pari, ma non possiamo descrivere Zb
Inoltre gli operatori a disposizione non permettono di contare un numero
illimitato di simboli. Possiamo quindi denotare con una ER, anche se in modo poco
compatto, il linguaggio Z2 = {anbn I 1 < n < 10}, ma non possiamo descrivere il
linguaggio Z3 = {anbn \n> 1}.
Intuitivamente, non avendo memoria illimitata, gli operatori a disposizione per
descrivere espressioni regolari consentono di descrivere la stessa classe di
linguaggi riconoscibili dagli automi a stati finiti e generabili dalle grammatiche
regolari, cioè la classe dei linguaggi regolari. Formalizziamo questa intuizione
mediante il seguente teorema.

Teorema 6.1
La classe dei linguaggi denotati dalle espressioni regolari coincide con la classe dei
linguaggi regolari.

La dimostrazione del Teorema 6.1 è lasciata al lettore per esercizio.
Si suggerisce di dimostrare prima che ogni linguaggio denotato da una ER è
regolare, osservando che:
- Il linguaggio vuoto e tutti gli elementi di Vt sono linguaggi regolari.
242 Informatica teorica

- I linguaggi regolari sono chiusi rispetto all’unione, alla concatenazione e alla


operazione stella di Kleene (ad esempio, se G è una R-grammatica, L(G)* è
generato dalla R-grammatica ottenuta da G, aggiungendo al suo insieme di
produzioni la regola A aS, dove S è l’assioma di G, per ciascuna regola del
tipo A a).
Allo scopo di dimostrare l’affermazione inversa, si osservi che il linguaggio
generato dalla R-grammatica il cui insieme di produzioni è
5 aA, A —> bB, B —> aA, A —> aC, C —> bC, C —> a
è denotato dalla ER a • (ha)* • a • (b)* • a e si cerchi di generalizzare questo fatto.
Vediamo ora un esempio di “trasformazione” di un automa a stati finiti in ER,
cioè proviamo a costruire, in modo intuitivo, una ER che denoti il linguaggio
riconosciuto da un automa a stati finiti.

Esempio 6.3
Si consideri l’automa A rappresentato in Figura 6.1. Cerchiamo di dedurre a partire
da esso l’ER che denota il linguaggio riconosciuto da A.
Procediamo identificando “sottoparti” del linguaggio. Poiché tutte le parole
accettate da A, sono quelle per cui esiste un cammino dallo stato iniziale allo stato
finale, iniziamo ad analizzare le sottostringhe che “portano” da q0 a q0. In
particolare, poiché q0 ammette una transizione che porta in q0 etichettata con b, la
stringa che permette di percorrere zero o più volte quel cammino sarà denotata da
b*.
Se consideriamo ora tutti i cammini che portano da q0 a q^, otteniamo
b*a(bb*a)*, dove b* descrive la transizione da g0 a q$ etichettata con b, a denota la
transizione da qo&qi e bb*a un generico cammino da q} a qL che passa per q0. Tale
cammino può essere percorso zero o più volte. Si osservi che, anche se non
propriamente parte degli operatori usati per definire ER, spesso si usa
l’abbreviazione a+ al posto di aa*, ad indicare che il simbolo a viene ripetuto una o
più volte. L’espressione b*a(bb*a)* è quindi equivalente a b*a(b+a)*.

Figura 6.1 L’automa a stati finiti dell’Esempio 6.3.


243
Definizioni denotazionali dei linguaggi

Consideriamo ora tutti i cammini che portano da q0 a q2, cioè i cammini percorsi
dalle stringhe riconosciute da A. Le stringhe che permettono di percorrere questo
cammino sono denotatate dall’ER b*a(b+a)*a(a* + (b(b+a)*a)*)*, dove b*a(b+a)*
descrive le stringhe che portano da q0 a qi, a descrive la transizione tra q> e q2 e
(a* + (ò(ò+a)*a)*)* rappresenta i possibili cammini da q2 a q2. In particolare a*
descrive un autoanello su q2 e (b(b+a)*d)* un ciclo che passa per q< ed
eventualmente ((Z>+a)*) per q$.

Questo procedimento si può generalizzare ottenendo un algoritmo che permette di
costruire un’espressione regolare a partire da un automa a stati finiti e viceversa. In
questo testo non approfondiremo oltre le ER, formalismo peraltro semplice e
intuitivo, grazie all’equivalenza con automi a stati finiti e grammatiche regolari. Il
lettore interessato ad approfondire l’argomento può fare riferimento ai tradizionali
libri sulla teoria della computazione, già citati nelle note bibliografiche dei Capitoli
4 e 5.

6.2 Equazioni negli spati dei linguaggi


Analizziamo ora come si possano utilizzare equazioni per denotare linguaggi. Sia
ancora VT un alfabeto di simboli terminali. Come si è visto nella Sezione 1.5, la
famiglia di tutti i linguaggi definiti su un alfabeto VT, @(Fr*), è dotata di alcune
operazioni algebriche come le operazioni insiemistiche, la concatenazione ecc.
Analogamente a quanto viene fatto per le algebre più convenzionali, si
possono definire equazioni in cui il dominio delle variabili siano linguaggi. Ad
esempio, considerando la famiglia di linguaggi su Vt, un sistema generale di
equazioni ha la seguente forma:
fi(XY, ...,Xm) = 0
f2(X, ...,Xm) = 0

fi(X, ...,Xm) = 0
dove sono funzioni fi: ftfVffij = 1, ..., m sono variabili
definite su p(Fr*).
Una soluzione del sistema di equazioni è una nz-upla di linguaggi che,
sostituiti alle variabili, soddisfano tutte le equazioni.
Nel seguito si concentrerà l’attenzione su equazioni scritte nella forma X =
F(X), in cui X= (Ai, ..., Xm) è un vettore di variabili, F = fi, è un vettore di
funzioni e ciascuna fi è ottenuta come composizione di unioni “u” e di
concatenazioni “ • ”. Poiché “ • ” è distributiva rispetto a “u” (si veda l’Esercizio
1.6), ogni fi può essere espressa, in una forma normale, come l’unione della
concatenazione di variabili (e costanti).
244 Informatica teorica

Ad esempio, fiXi, X2, A3) = Ai • A • X2 u X2 • A3 • B, dove Ab X2 e A3 sono variabili-


linguaggio, mentre A e B sono costanti-linguaggio, è espressa in forma normale. Si
suppone, inoltre, che le costanti-linguaggio siano linguaggi finiti.
Una soluzione A di un sistema del tipo A = F(A) viene anche chiamata un
punto fisso di F, poiché la corrispondenza F: p(Er*)" p(Fr*)” trasforma A in se
stesso. Ad esempio, se consideriamo il più convenzionale dominio dei numeri
interi e la finizione fix) = x2 -3x + 4, allora 2 è un punto fisso per/ poiché fi2) = 2.
Estendiamo ora la notazione vettoriale stabilendo le seguenti convenzioni:
- La n-upla (0, ..., 0) viene ancora indicata dal simbolo 0, dato che non vi è
rischio di confusione.
- La relazione A <z X' è la naturale estensione della relazione di inclusione alle
n-uple di linguaggi, ossia AcA' se e solo se Ac A/, ..., A„ c A„'.
Per capire meglio quanto descritto fino ad ora analizziamo un esempio di
equazione nello spazio dei linguaggi.

Esempio 6.4
Si consideri il seguente sistema costituito da una sola equazione nella variabile-
linguaggio A:
X = A-X-B'jA-B

dove A = {a} qB = {b}, per cui l’equazione diventa A= {a} -X- {b} u {a} ■ {b}.
Il linguaggio L = {<Fb”\ n > 1} è un punto fisso per la precedente equazione, poiché
{a}L • {b} = {anbn\ n > 2} e {a}-L • {b} u {a} ■ {b} = {anbn | n > 2} u {a} • {b} =
{anbn\ n> 1} = L.

Si osservi che, in generale, un sistema di equazioni nella forma A = F(A) non ha un
unico punto fisso. Questa caratteristica viene illustrata nel seguente esempio.

Esempio 6.5
Si consideri la seguente equazione in una sola variabile-linguaggio A:
A=A-AuA
dove A = {aa}.
Questa equazione ha infiniti punti fissi del tipo
Lk = Fu Llk per ogni k>Q con
L0={a2n\n>Q},Lìk={a2n+k\n>()}.

Infatti, per ogni k > 0 si ottiene


Lk-Lk = (Lo MLiQ • (Lo U Lb) =
= Lq- L0'U Lik- Lq'u Lq- L\k (j LifLn =
Definizioni denotazionali dei linguaggi

= {rz2”| n>Q} u {a2n + Vml n,m>0} u {o2'"rz2', + ì n,m > 0} M {^na2m\


n,m > 0} =
= {a2”! n > 0} u {a2r + i|r>0} u {a2(* + r) | r>0} =
= {a2>>0} u {a2” + i|»>0}
Quindi Lk-Lk u {aa} = {a2”! n > 0} u {rz2”| n = 1} u {an + ì n > 0} = Lk.

Un dubbio che sorge quasi spontaneamente, dopo aver osservato che per
un’equazione vettoriale X = F(X) possono esistere più punti fissi, eventualmente
anche infiniti, è se esista sempre un punto fisso, e, tra tanti, un minimo punto fisso.
Per minimo punto fìsso si intende una w-upla di linguaggi L tale che L = F(L) e, per
ogni L', tale che L' = F(L'\ L czL'. Ovviamente un minimo punto fisso, se esiste,
deve essere unico. Infatti, se L\ e L2 fossero due minimi punti fissi, per definizione
dovrebbero valere entrambe le relazioni L\ cz L2 e L2 £ L\. Quindi necessariamente
deve risultare LY=L2.
Per equazioni vettoriali del tipo considerato in questa sezione, cioè equazioni
X=F(X), dove F è una funzione vettoriale i cui elementi sono unioni di
concatenazioni di variabili-linguaggio e di costanti-linguaggio, esiste sempre un
minimo punto fisso L, dato dalla seguente formula:

L= Jf'(0)
J=1
cioè, L = 0 u F(0) u F(F(0))....’
Le equazioni del tipo precedente possono quindi essere usate per indicare il
loro unico minimo punto fìsso. Poiché esiste una corrispondenza uno-ad-uno fra
questa classe speciale di equazioni negli spazi dei linguaggi ed i loro minimi punti
fissi, si dice che le equazioni denotano un linguaggio: il loro minimo punto fisso.
Consideriamo ora le grammatiche non contestuali, introdotte nella Sezione
5.1, e osserviamo che l’insieme di produzioni di una grammatica NC G = (VT, VN,
P, S) si può scrivere come:

Xi —> an | an... | a1Ai


X2 —> a22 | a22... | a2h

1 II lettore non digiuno di matematica del continuo avrà sicuramente riconosciuto l’analogia
con il classico teorema di punto fisso dell’analisi matematica, dove i domini delle variabili
sono i più tradizionali numeri reali o complessi (o altri ancora, più sofisticati, sempre dotati
della caratteristica di continuità). Infatti non solo la forma dell’equazione (x = ffxj) è
identica, ma anche il modo con cui se ne dimostra l’esistenza della soluzione, o punto fisso,
e la si costruisce è lo stesso: nel continuo il punto fisso (unico in un opportuno sottoinsieme
dello spazio e sotto opportune condizioni per la trasformazione f) è il limite di una
successione {x„}, dove xn+I =f(x„) (con xn scelto arbitrariamente in un certo sottoinsieme).
246 Informatica teorica

Xn | • • I ® fl/in

dove VN = {A,| i = 1, n}, V= VN<J VT, cty g V* e dove

Xj “ajl I aj2- • • I aJltj

è una abbreviazione di

X —> otyi, X —> cty2, • • ., X —et h , 1 <j < n.

Se ora si interpretano i non terminali di una grammatica non contestuale come


variabili-linguaggio, la grammatica può anche essere considerata come un sistema
di equazioni nelle variabili-linguaggio.

Xi = a ii u a 12 u ... u a'1A] =/ì(Ai, ..., X„)

X2 = a 21 cl 22 ... U =fi.(X\, ...,Xn)

Xn CtniLJCtn2^ ... Ct nfi„ fn(X\, ..., AQ

dove, se

allora

“■ti ..............

Il precedente sistema di equazioni è chiamato sistema di equazioni associato alla


grammatica G. Se si pone X = (Xx, ..., Xj), il sistema di equazioni può essere
scritto come un’equazione vettoriale X= F(X). Questa riscrittura e interpretazione
delle grammatiche NC suggerisce il seguente risultato.

Teorema 6.2
Sia G = (VT, Vn, P, S) una grammatica non contestuale in cui VN = {Xi, Xn} e sia
S = Xh Sia X = F(X) il sistema di equazioni associato a G. Allora la n-upla di
*
linguaggi L = (Lr, L„), con L, = {x, e VT* \ X (=> xj, i = 1, ..., n, è il minimo
G

punto fìsso del sistema di equazioni. In particolare, L(G) = Lx.

Traccia di dimostrazione
Come osservato in precedenza, l’equazione vettoriale X = F(X) ammette minimo
punto fisso. Sia dunque X tale minimo punto fìsso. Si intuisce immediatamente
247
Definizioni denotazionali dei linguaggi

che x non è altro che l’intersezione di tutti i punti fissi dell’equazione X= F(X).
x è quindi dato da:
x = r>x \ x = F(X) .
La dimostrazione di questa affermazione è lasciata al lettore per esercizio.
Sia Xf la componente z-esima del vettore x . Si è già potuto notare, in
CO
precedenza, che X = [J FJ (0). Viene ora dimostrato che, per tutti gli interi k e z,
7=1
k

con k > 1, 1 < z < n, X,■ = S => x se e solo se x appartiene alla componente z-esima
G

k *

di I I FJ (0). Ciò consente di concludere che, per ogni x e Fp*, X, => x, se e solo
G
7=1

se x e Xt, il che implica la tesi del teorema. La dimostrazione è effettuata per


induzione sul valore di k.
1. base dell 'induzione
Xi^>x se e solo se x e /(0).
SeXi^> x, allora l’equazione z-esima del sistema avrà la formaX, =f(X) = {x}
u ... u .... Di conseguenza, x e fiX).
Viceversa, sia x e /(0) -/(0) l’unione delle costanti-linguaggio, poiché la
I concatenazione di ogni linguaggio con 0 è 0. Allora,X, = f(X) = {x} o ... o

Quindi Xj^> x è nell’insieme di produzioni di G.


ii. passo induttivo
h

Si supponga che, per ogni Xh X, x con h < k se e solo se x appartiene alla


k

componente z-esima di [J FJ (0).


7=1

£+1 k

Si supponga ora che A, x, ossia che Xt =^> x con = x0Y]X]Y2. .. Y„xm,


Xi e VT*, Yi e VN.
Tuttavia, Xi => £,• se e solo se la z-esima equazione di X = F\X) è del tipo
X^pj....
h

Inoltre, Yr => zr, per ogni hr<k, per ogni r, con 1 < r < m e XgZiXj... zinxm = x.
k

Per l’ipotesi di induzione, zr appartiene alla componente r-esima di [J FJ (0)


7=1
per ogni r, con 1 < r < n.
248 Informatica teorica

k i+l
Quindi x appartiene alla componente z-esima di F(|J F3 (0)) c F3 (0). In
7=1 7=1
modo analogo si può dimostrare l’enunciato opposto, ossia che, se x appartiene alla
, h

componente z-esima di 7** \0), allora X => x, h < k + 1.



ESERCIZIO
6.3 Si completi la dimostrazione del Teorema 5.2 mostrando che
X = rX \X = F(X).

Esempio 5.4
Si consideri la grammatica
G = ({a, b}, {.S}, {.S -> aSb, S ab}, S):G si trasforma nella seguente equazione
S = {a}-S- {b}u {ab}.
Come è noto, L = {anbn\ n > 1} è il linguaggio generato da G ed è anche un punto
fisso per l’equazione corrispondente. In base al teorema precedente, L è il minimo
punto fisso.
00
L si può anche ottenere come F3 (0)={ab} u {aabb} u .... Si verifica
7=1
immediatamente in questo esempio che il processo di costruzione di L come
00
F3 (0) produce stringhe le cui derivazioni grammaticali hanno lunghezza
7=1
crescente. Questa è una proprietà generale della costruzione.

ESERCIZIO
6.4 Si calcolino i minimi punti fissi delle seguenti equazioni e si verifichi che
coincidono con i linguaggi generati dalle grammatiche corrispondenti. Si
controlli anche se, per le medesime equazioni, esiste qualche punto fisso non
minimo e lo si confronti con il punto fisso minimo.
St:X=aX
S2'. X ~ aX VJ a
jX = aY
S3 :
[Y = bX ub

S4:X=Xua
Definizioni denotazionali dei linguaggi 249

S5:X=X2Uaub
U = auXY
S6: J
y = 6u xy

6.3 Confronto tra modelli denotazionali e modelli operazionali


Sia le espressioni regolari che le equazioni negli spazi dei linguaggi sono modelli
formali per la definizione di linguaggi. Trascurando le questioni concernenti la
potenza di calcolo dei formalismi, le grammatiche e gli automi possono essere usati
esattamente per il medesimo scopo. Poiché i modelli si presentano in modo molto
diverso, viene naturale chiedersi in che modo si possono confrontare e quali siano
le differenze di stile, da un punto di vista concettuale.
Le espressioni regolari e le equazioni da un lato e gli automi dall’altro
appartengono a due classi diverse di modelli concettuali: i modelli descrittivi o
denotazionali e i modelli operazionali (o evolutivi), rispettivamente. Come
abbiamo già visto nel Capitolo 3, i modelli evolutivi simulano il comportamento
dell’entità che viene modellata descrivendo la sequenza di stati in cui essa entra nel
corso della sua evoluzione dinamica, partendo da un determinato stato iniziale2. Ad
esempio, gli automi definiscono un linguaggio attraverso un insieme di transizioni
che, quando la stringa di ingresso appartiene al linguaggio, portano dalla
configurazione iniziale ad una configurazione finale. Gli automi possono quindi
essere considerati come processori astratti e le transizioni come passi
computazionali effettuati dal processore durante il riconoscimento di stringhe
appartenenti al linguaggio. Viceversa, sia le espressioni regolari che le equazioni
sono formalismi “statici”. Le espressioni regolari denotano i linguaggi
corrispondenti mediante un’opportuna interpretazione matematica dei loro
operatori. Analogamente, le equazioni negli spazi dei linguaggi denotano la loro
soluzione, un linguaggio, senza specificare come la soluzione si possa
effettivamente calcolare. Si potrebbe affermare che, nell’ambito della definizione
dei linguaggi, le espressioni regolari e le equazioni negli spazi dei linguaggi sono
formalismi puramente astratti, mentre gli automi sono più concreti, “orientati
all’implementazione”, in quanto suggeriscono un modo meccanico per accettare le
stringhe appartenenti al linguaggio.
Rimane da spiegare dove si possono collocare le grammatiche all’interno di
questa classificazione. Come visto nella Sezione 6.2, le grammatiche possono

2 Si avverte il lettore che il termine “stato” viene qui impiegato informalmente con un
significato più generale di quello che caratterizza lo stato dell’unità di controllo di un
automa. Un simile uso informale e colloquiale del termine “stato” è molto comune, anche
se, in termini formali, il concetto intuitivo di stato viene effettivamente formalizzato dalla
nozione di configurazione.

3
250 Informatica teorica

essere viste sia come un insieme di equazioni che come un modo per computare la
soluzione di un insieme di equazioni: L = F(0) o F(F(0)) u .... Quindi, è
possibile affermare che le grammatiche costituiscono una sorta di ponte ideale fra i
modelli puramente denotazionali e quelli puramente evolutivi. In ogni caso è bene
ricordare, come già preavvisato nel Capitolo 3, che questa classificazione dei
modelli ha un “sapore” prettamente stilistico ed è legata più all’intuizione che a
una definizione matematicamente precisa; non a caso i diversi teoremi di
equivalenza dimostrati in questi capitoli permettono di passare agevolmente da uno
“stile di definizione” all’altro.
Nel Capitolo 7 vedremo un’ulteriore importante classe di modelli descrittivi: la
logica matematica, introdotta nel Capitolo 2 verrà applicata alla definizione di
sistemi informatici (e non solo) di vario tipo.

RIASSUNTO DEL CAPITOLO


In questo capitolo sono stati brevemente illustrati due formalismi che appartengono
alla classe dei modelli denotazionali, che, a differenza dei modelli visti nei capitoli
precedenti sono formalismi puramente astratti.
In particolare abbiamo analizzato le espressioni regolari e le equazioni negli
spazi dei linguaggi. Le prime descrivono linguaggi regolari attraverso
un’opportuna interpretazione matematica dei loro operatori, mentre le seconde
denotano, attraverso la loro soluzione, un linguaggio, senza specificare come la
soluzione si possa effettivamente calcolare.

ALTRI ESERCIZI
6.5 Si scrivano le ER che denotano i seguenti linguaggi:
- L = {x e {a, b}* | ogni a è seguito da almeno tre b}\
- L2 = {x e {a, b}* | ogni a è seguito da esattamente tre b}; ,
- L3= {x e {a, b}* | x inizia con almeno due a};
- L4= {x e {a, è}* | x contiene un numero pari di a e di b};
- £5 = {x e {a, è}* | il numero di a è divisibile per 5}.
6.6 Si costruisca l’espressione regolare che denota il linguaggio accettato
dall’AFN in Figura 4.43(a).
6.7 Si costruisca l’espressione regolare che denota il linguaggio accettato
dall’AFN in Figura 4.44.
6.8 Si costruisca l’automa a stati finiti che riconosce il linguaggio denotato dalla
seguente ER
a* + (a b*}*.
Cosa cambia se b * viene sostituito con b+r!
6.9 Si descriva l’equazione che denota li linguaggio L = | w G {a, b}*}.
Cosa cambia se w e {a, b}+r?
6.10 Data la grammatica NC G = ({a, b}, {S, X, Y},P, S), dove P è formata dalle
seguenti produzioni:
251
Definizioni denotazionali dei linguaggi

aSa I bSb | aXb | bXa | 8


X-> aXa\bXb\ aYb \bYa\z
Y^> aYa | bYb | e
la si trasformi in un sistema di equazioni e si dica quale linguaggio genera.

Soluzioni schematiche di esercizi scelti


6.1 Consideriamo l’espressione R3 = ((a u b)* u (a • è)*) • b • a*. Poiché la
prima parte ((a u b)* \J (a • è)*) è concatenata a b • a*, allora tutte le
stringhe di R3 termineranno con una b seguita eventualmente da una o più a.
Poiché il prefisso di tali stringhe è descritto dall’unione di (a \J è)* e
(a • b)*, e (a \J è)*rappresenta tutte le stringhe su {a, b}, allora quello sul
suffisso è l’unico vincolo sul linguaggio. Quindi R3 descrive il linguaggio
delle stringhe su {a, b} che terminano con una b seguita eventualmente da
una o più a.
6.6 Per costruire la ER che denota il linguaggio riconosciuto dall’automa in
Figura 4.43(a) dobbiamo considerare tutti i cammini che portano dallo stato
iniziale a quello finale, cioè, q0, q\, q2 e q0, q2, q>-
Il cammino q§, qi viene percorso da tutte le stringhe denotate da
a (b (ab*a}* a}*, in quanto a permette di muoversi da qQ a qi, (b (ab*a}* a)*
descrive i cicli su qi che passano per q<s ed eventualmente per q2. Il cammino
q<s, qi, q2 è, quindi, descritto da a (b (ab* a)* a)* a.
Il cammino qfh q2 viene, invece, percorso da tutte le stringhe denotate da
a (b* + (a(aby*a)*)*, dove a permette di muoversi da q0 a q?, b* descrive
l’autoanello su q2 e (a(ab)*a)* i cicli su </2che passano per q0 ed
eventualmente per q2. Si noti che (b* + a*)* è equivalente a (b + a)*, per cui
a (b* + (a(ab)*«)*)* può essere riscritta come a (b + a(ab)*a)*. Il cammino
<?o, qi, è, quindi, descritto da a (b + a(ab)*a)* a.
Il linguaggio riconosciuto dall’AFN è quindi
a (b (ab*a)* a)* a + a(b + a(ab)*a)* a.
6.8 La ER a* + (a b*)* denota il linguaggio riconosciuto dall’automa A\ in
Figura 6.2. Lo stato iniziale è anche finale poiché il linguaggio denotato da
ER contiene anche la stringa vuota. L’autoanello su q0 etichettato con a
descrive a*, mentre la transizione da q0 a q1 etichettata con a descrive la a di
a b*; infine, l’autoanello su qt descrive (a b*)*.

Figura 6.2 Automa che riconosce il linguaggio denotato da a* + (a b*)*.


252 Informatica teorica

Se si considera la ER a* + (a b+)*, il linguaggio da essa denotato viene


riconosciuto dall’automa A2 in Figura 6.3. Lo stato iniziale è anche finale,
poiché il linguaggio denotato da ER contiene anche la stringa vuota.
Analogamente al caso precedente, l’autoanello su q0 etichettato con a
descrive a*, mentre la transizione da qQ a qy etichettata con a descrive la a di
a b+, la transizione da qx a q2 etichettata con b descrive la b di a b+,
l’autoanello su q2 descrive b* implicitamente contenuto in a b+ e, infine, il
ciclo tra q2 e qx descrive la stella di Kleene applicata a (a b+)*.

Figura 6.3 Automa che riconosce il linguaggio denotato da a* + (a b+)*.

6.9 L’equazione che denota L è così definita


X= {a} X {a} u {b} X {b} u {e}.
Qualora w e {a, b}+, diventerebbe
X— {a} X {a} u {b} X {b} u {aa} u {bb}.

Note bibliografiche
Le espressioni regolari sono state studiate da McNaughton e Yamada (1960). Per il
lettore interessato ad approfondire l’argomento, vale la pena menzionare le
applicazioni dei modelli denotazionali, basati sulle equazioni su algebre
eterogenee, alla descrizione, implementazione e verifica di correttezza dei tipi di
dati astratti (ADI (1975) e Guttag (1977)).
Le equazioni negli spazi di linguaggi sono forse meno ampiamente usate e il
loro studio è in genere di interesse più specifico. Esse sono state trattate, ad
esempio, in Gross e Lentin (1967) e approfondite in Gatto e Mandrioli (1974).
Questo formalismo è stato presentato qui solo come semplice ed elegante esempio
introduttivo di un modello denotazionale. Vale anche la pena di segnalare che un
approccio denotazionale basato su equazioni e relativo punto fisso è stato utilizzato
in informatica anche per definire la semantica dei linguaggi, oltre che la loro
sintassi, come abbiamo fatto in questo capitolo. Il lettore interessato può fare
riferimento al Capitolo 4 del “fratello maggiore” di questo testo (Ghezzi e
Mandrioli, 1989) per un’introduzione abbastanza accurata alla semantica
denotazionale dei linguaggi (di programmazione) e alla relativa bibliografia per
eventuali approfondimenti.
Capitolo 7

LA LOGICA MATEMATICA IN INFORMATICA


In questo capitolo verrà analizzata la logica, introdotta sinteticamente nel Capitolo
2, dal punto di vista della sua utilizzazione nell’ingegneria informatica e, più in
generale, nell’ingegneria dei sistemi. A differenza dei modelli presentati nei
Capitoli 4 e 5, la logica è un formalismo descrittivo, cioè permette di descrivere le
proprietà che si vogliono ottenere da un sistema, o simmetricamente, le proprietà
che si vogliono evitare, senza necessariamente definire un modello del sistema e
del suo funzionamento.
La logica, tradizionalmente studiata da filosofi e matematici, gioca, ai giorni
nostri, un ruolo importante anche nell’informatica, al punto da essere spesso
chiamata “il calcolo dell’informatica”. Secondo Moshe Vardi1:
“La logica gioca nell 'informatica un ruolo simile a quello dell 'analisi in fisica e
nelle discipline ingegneristiche tradizionali
La logica è uno strumento utilizzato sia dai matematici sia dagli informatici, ma, se
in generale i primi focalizzano la loro attenzione sulla sintassi e la semantica dei
linguaggi e ne studiano il potere espressivo e le proprietà, gli informatici usano la
logica per modellizzare sistemi e analizzarne le proprietà e combinano le basi della
ricerca sulla logica con l’applicazione a contesti ingegneristici.
Esiste una varietà di linguaggi logici, che dipendono dal livello di astrazione
rispetto al linguaggio naturale. Il calcolo proposizionale ( e la logica del
prim’ordine introdotti nel Capitolo 2, sono due importanti esempi, ma ci
sono molti altri linguaggi come, ad esempio, la logica descrittiva, la logica
temporale e la logica modale. Questi ultimi tipi di logica, più avanzati e
specialistici, non sono trattati in questo libro, ma hanno ricevuto grande attenzione
e sviluppo nelle applicazioni informatiche, specialmente nelle ultime due decadi. Il
lettore interessato ad approfondire questi temi può fare riferimento alle note
bibliografiche.
Poiché la logica è un formalismo universale, che nasce per coniugare il rigore
della matematica con qualsiasi forma di ragionamento umano, essa può essere
applicata in numerosi contesti nell’ambito dell’informatica e dell’ingegneria in
generale. Alcuni esempi di tali contesti sono:
- Le porte logiche nell’architettura dei calcolatori;
- La specifica e la verifica di sistemi nell’ingegneria del software;
- La definizione della semantica dei linguaggi di programmazione;
- La programmazione logica;

1 Professore presso la Rice University e personalità di spicco in campo informatico.


254 Informatica teorica

- L’Algebra relazionale nelle basi dati;


- La dimostrazione automatica di teoremi (e.g., in intelligenza artificiale);
- La teoria della computazione.
Ad esempio, il AAè usato per progettare circuiti elettronici digitali, mentre la
che è più potente, viene utilizzata per applicazioni in campi più vasti, quali,
l’ingegneria del software e dei sistemi e l’intelligenza artificiale. In questo capitolo
illustreremo brevemente l’uso della nei tre seguenti contesti, che
rappresentano alcune tra le sue principali applicazioni:
- Definizione di linguaggi formali (Sezione 7.1);
- Formalizzazione di proprietà di programmi (Sezione 7.2);
- Specifica di proprietà dei sistemi (Sezione 7.3).
Mentre nel Capitolo 2, nello scrivere formule logiche, è stato adottato uno stile
tipicamente matematico (le funzioni sono state denotate da lettere di funzione, i
predicati da lettere di predicato, ed entrambe le lettere presentavano Parità come
apice e l’identificatore come pedice), in questo capitolo, facendo riferimento a casi
applicativi, useremo identificatori significativi per indicare i predicati e le funzioni,
analogamente a quanto viene fatto nell'attribuire i nomi alle variabili e ad altri
oggetti nei linguaggi di programmazione. Infatti, anche se il loro nome può essere
dato in modo arbitrario, se ricorda il loro significato (ovvero l’interpretazione che
si ha in mente), le specifiche (o i programmi) diventano più leggibili.

7.1 L’uso della logica per definire linguaggi formali


In questa sezione viene analizzato come la logica possa essere utilizzata per
descrivere linguaggi formali. Questo problema è di notevole interesse in quanto,
come abbiamo visto nei capitoli precedenti, i linguaggi definiti mediante modelli
matematici possono essere usati per rappresentare molti problemi informatici. Un
linguaggio è infatti un modo di rappresentare o comunicare informazioni e non
solamente un insieme di stringhe senza significato. Per esempio, la computazione
di un sistema può essere descritta come una sequenza di operazioni (o azioni)
permesse. Quindi si possono rappresentare le proprietà di un sistema definendo
l’insieme di possibili sequenze di azioni che il sistema può eseguire (gli
identificatori delle singole azioni costituiscono l’alfabeto e l’insieme di tutte le
sequenze ammissibili di azioni costituisce il “linguaggio del sistema”).
Un linguaggio può quindi essere visto come un mezzo per esprimere un
problema e/o la sua soluzione; perciò è importante trovare un modo appropriato
(chiaro, conciso, dettagliato, preciso ecc.) per rappresentarlo.
Abbiamo visto nei capitoli precedenti che i linguaggi possono essere descritti
attraverso formalismi diversi, ad esempio tramite modelli operazionali, come gli
automi e le reti di Petri (Capitolo 4), o modelli generativi, come le grammatiche
(Capitolo 5) e i traduttori (Capitolo 4). Tutti questi modelli descrivono i linguaggi
specificando come le stringhe vengano accettate o descrivendo come vengono
255
La logica matematica in informatica

generate. La logica matematica, invece, permette di spiegare i linguaggi in modo


descrittivo mediante l’uso di formalismi matematici, che si focalizzano su proprietà
rilevanti delle stringhe del linguaggio anziché sul modo in cui esse sono generate o
riconosciute.
La può aiutare nel descrivere un linguaggio. Avendo già familiarità con
le notazioni classiche per rappresentare insiemi (e.g., S = {x | x gode di una data
proprietà}), questo non è sorprendente; infatti la teoria tradizionale degli insiemi
può essere formalizzata come una particolare teoria del prim’ordine e le notazioni
tipiche prese in prestito dalla teoria degli insiemi possono essere viste come
abbreviazioni per formule ben formate (fbf) di Vediamone un primo
esempio.

Esempio 7.1
Il linguaggio L formato dall’insieme di tutte le stringhe sull’alfabeto {a, b} che
iniziano con a, cioè, L = {x e {a, b}*\ x = a.y ey e {a, b}*}, può essere espresso
nella sintassi della mediante la fbf:
x e L <=> 3y(x = a.y ay e {a, b}*).

L’Esempio 7.1 evidenzia già alcune domande che nascono quando si vuole
ricorrere alla logica per definire linguaggi: che cosa si deve formalizzare? Con
quali elementi della EErEEEl Come si possono comporre le diverse parti della
definizione? Che cosa si può assumere come elemento primitivo?
Consideriamo nuovamente il linguaggio (a"b" | n > 1}, più “vincolato”
rispetto al linguaggio dell’Esempio 7.1. La notazione precedente può essere vista
come un’abbreviazione della formula x g L <=> 3n(n > 1 a x = an.bn). Una prima
osservazione è che si usano simboli noti quali g, >, a , = come predicati; inoltre si
usa anche il tradizionale simbolo di esponente (potenza di) come simbolo di
funzione. Come già accennato, in qualche modo si devia dalle rigide regole
sintattiche di ATTESE in favore della leggibilità: e.g. il simbolo di predicato ‘g’ è
usato al posto di un “anonimo” , questo suggerisce la sua interpretazione
naturale; inoltre si passa da una forma puramente prefissa a una infissa; si agisce in
modo simile per altri simboli di funzioni note e simboli di predicati. In letteratura, è
comune riferirsi a notazioni di formalismi modificati e/o arricchiti senza
cambiamenti nella semantica, con l’obiettivo di renderli più facilmente intuibili,
con l’espressione “zucchero sintattico”.
Si noti che non tutte le notazioni funzionali e relazionali possono essere viste
ed interpretate come elementi di base: in realtà, dovrebbero essere necessari solo
poche operazioni di base e i simboli a loro correlati, mentre tutti i rimanenti
dovrebbero essere definiti come derivati.
Nel primo esempio si è visto che la concatenazione e la potenza sono funzioni.
Ma cosa vuol dire x che appare nel secondo esempio? Si tratta di una funzione
256 Informatica teorica

derivata che può essere definita in modo ricorsivo sulla base della concatenazione,
come segue:
Vw((w = 0 => xn = s) a (n > 0 => xn = x"'7.x)).
Perciò essa non può essere considerata una primitiva.
In generale, tutti i predicati e le funzioni non elementari necessitano di essere
definiti mediante formule adatte; per semplicità, considereremo invece come
elementari, oltre la concatenazione di stringhe, i seguenti elementi, presi “in
prestito” dalla tradizionale teoria degli insiemi e daH’aritmetica:
- L’uguaglianza, indicata con il simbolo di predicato ‘=’;
- L’ordinamento (stretto, debole, parziale o totale), generalmente indicato con i
simboli ‘<’, ‘>’, ‘<’, ‘>’;
- L ’ appartenenza a un insieme : ‘ g ’ ;
- Le operazioni aritmetiche di base ‘+, -, *, /’.

Esempio 7.2
Si consideri il linguaggio L, composto dalle stringhe in {a, b, c}*, in cui tutte le a
(se ce ne sono) precedono tutte le b (se ce ne sono) e queste a loro volta precedono
tutte le c (se ce ne sono), in altri termini L = a*Z>*c*. Tra i tanti modi possibili di
definire formalmente L, consideriamo il seguente: si possono definire due
linguaggi ausiliari più semplici Li = «*A* e L2 = A*c* e affermare che una stringa
appartiene a L se:
- èinLbo
- è in L2, o
- può essere decomposta nel carattere a seguito da un suffisso y che appartiene a
sua volta a L, 0
- può essere decomposta in un prefisso y che appartiene a L seguito da una c.
Questa definizione può essere formalizzata nel modo seguente:
x e L <=>(x g Li) v (x g L2) v
3y{(x = a.y aj g L)) v (x =y.c aj g L)).
Rimangono quindi semplicemente da definire Li e L2; poiché hanno la medesima
struttura, focalizziamoci solo su Lb Li è il linguaggio delle stringhe in {a, b}* con
tutte le a che precedono le b. Più precisamente, una stringa è in Li se è la stringa
vuota oppure si può decomporre nel carattere a seguito da un suffisso y che
appartiene esso stesso a Lb o in un prefisso y (appartenente esso stesso a Li)
seguito da una b. Questo può essere espresso mediante la formula
xgLi<5>x = sv 3y(* = a.y a y g Li) v 3j/(x =y. b a y g Li).
257
La logica matematica in informatica

Un’altra possibilità è definire la funzione generale “concatenazione tra linguaggi”


come una funzione derivata basata sull’omonima funzione tra stringhe (risulta
necessario un piccolo overloading di simboli di funzione):
x e L1.L2 <=> 3y,z (x =y.z a y g Li /\z g L2).
Quindi, dopo aver definito e fr2, L risulta definito dalla loro concatenazione.

Si noti che non esiste una “formula magica” per ottenere la descrizione in
degli insiemi, ma dalla pratica si possono evidenziare alcune utili linee guida:
- Dall’esempio precedente possiamo ricavare che, quando si rivolge l’attenzione
all’ordine con cui si susseguono le lettere in un linguaggio, si può far ricorso a
formule in cui le stringhe siano decomposte nella concatenazione di
sottostringhe appartenenti ad altri linguaggi, eventualmente definiti
ricorsivamente.
- Quando è necessario contare le occorrenze di alcune lettere, risulta utile
definire una funzione in grado di contare i simboli a cui si è interessati, come
illustrato dall’esempio seguente.

Esempio 7.3
Sia L {x g {a, b}*\ il numero di a sia uguale al numero di b}. Per definire questo
linguaggio in si può introdurre la funzione di arità 2 #(a, x), che conta il
numero di apparizioni del simbolo a nella stringa x. Questa funzione può essere
formalmente definita dalla formula
(x = e => #(a, x) = 0) a
VX(X = a-y => x)= y)+1 ) a
(x = b.y => #(a, x) = #(a, y)).
Si noti che questa definizione dipende dall’alfabeto su cui x è definita.
Sulla base della funzione #, il linguaggio L può essere definito mediante la
seguente fbf in
x g L <=> x g {a,b}* a #(óz, x) = #(b, x).

ESERCIZI
7.1 Dare due differenti formule in T^LES7 per definire il linguaggio L = {anbn |
n > 0}.
Suggerimento', una formula può sfruttare la ricorsione, formalizzando che in
un “caso normale” una stringa di L consiste nella coppia di caratteri a e b che
include un altra stringa di L\ un’altra formula potrebbe esprimere la stringa
di L come sequenza di a seguita da una sequenza di b, tale che il numero di
occorenze di a si uguale al numero di b.
258 Informatica teorica

7.2 Scrivere una formula in che definisca il linguaggio L c {a, b, c}*, le


cui stringhe siano tali che due a adiacenti non possano essere seguite da
alcuna b finché non appare una c. Ad esempio, babaacb e babaaaa
appartengono a L, mentre aacaab e abababaab no.
7.3 Si formalizzi in il linguaggio L composto da stringhe del tipo wcu, in
cui w e u sono due stringhe sull’alfabeto {a, b} tali che, almeno in una
posizione a partire dal loro inizio, si trovi lo stesso carattere.

7.2 L’uso della logica per esprimere proprietà dei programmi


Quando un algoritmo viene implementato usando un linguaggio di
programmazione, è consigliabile definire precisamente che cosa deve essere in
grado di fare prima di descrivere come funzioni. Un modo classico per specificare
tali caratteristiche dei programmi - o frammenti di programmi - è l’utilizzo delle
precondizioni e delle postcondizioni:
- le precondizioni indicano che cosa deve essere vero prima di eseguire un
programma (o frammento di programma), cioè, sotto quali ipotesi il
programma deve garantire il comportamento atteso;
- le postcondizioni indicano che cosa deve essere vero una volta terminata
l’esecuzione, cioè, quali risultati il programma deve fornire quando valgono le
ipotesi stabilite nella precondizione.
La struttura generale di questo schema di ragionamento è presentata nella Figura
7.1, che può essere descritta come una specifica per un (frammento di) programma:
P deve essere tale che se Pre vale prima della sua esecuzione, allora Post vale dopo
l’esecuzione.
Le precondizioni e le postcondizioni possono essere definite in diversi modi:
- Utilizzando il linguaggio naturale: apparentemente facile, ma esposto ad
ambiguità e imprecisioni (in un certo senso, “troppo ricco”);
- Utilizzando un adeguato linguaggio di asserzioni definito ad hoc per questo
scopo, come JML per i programmi lava;
- Utilizzando la strumento formale e generale o altro tipo di logica.

{Precondizione: Pre}
Programma o frammento di programma : P
{Postcondizione: Post}
Figura 7.1 Specifica delle proprietà di un programma mediante pre e
postcondizioni.
259
La logica matematica in informatica

In questa sezione viene illustrato come utilizzare la per definire


precondizioni e postcondizioni, attraverso alcuni esempi. Considerata la generalità
della il lettore sarà in grado di applicare i principi generali anche se dovrà
adottare un linguaggio di specifica diverso. Per maggiori dettagli si faccia
riferimento alle note bibliografiche.

Esempio 7.4
Sia P un programma implementato per svolgere la ricerca di un elemento x in un
array ordinato A di n elementi. La precondizione, ovvero l’ipotesi sotto cui
l’algoritmo proposto deve funzionare correttamente, è, ovviamente, che l’array sia
ordinato. La postcondizione stabilisce che la variabile logica trovato sia vera se e
solo se esiste nell’array l’elemento x cercato dall’algoritmo.
Si noti che definire le precondizioni e le postcondizioni non implica alcuna
assunzione su come il programma sia implementato. Per esempio, anche se è
richiesto che l’array A sia ordinato, questo non obbliga il programmatore a
implementare un algoritmo binario di ricerca.
Formalmente la precondizione può essere espressa in J’KS’come segue:
{Vz(l <i<n^A[i]<A[i + 1])}
Ovvero, ciascun elemento dell’array è minore o uguale di quello che lo segue (è
implicito che n denoti la cardinalità dell’array).
Analogamente si definisce la postcondizione:
{trovato <=> 3z (1 <i<n a A[i] = x}
Ovvero la variabile logica trovato è vera se e solo se esiste un indice valido
dell’array corrispondente all’elemento cercato.
La struttura completa è quindi:
{Vz(l < z < n A[i] < A[i + 1])}
P
{trovato <=> Ez (1 < z < n a A[z] = x}
Si noti che gli elementi di un array sono indicati secondo la normale notazione
usata in programmazione, che enfatizza che gli array sono casi speciali di
funzione2.

2 Questa naturale assunzione potrebbe creare difficoltà nei casi in cui sia necessario
quantificare gli array visti come variabili di programma: le teorie del prim’ordine infatti
non permettono di applicare quantificatori ai simboli di funzione. In questi casi si può fare
riferimento a teorie di ordine superiore che sciolgono questi vincoli, oppure applicare
qualche “trucchetto” alle formule del prim’ordine. Tali casi non rientrano però negli scopi
di questo testo.
260 Informatica teorica

Esempio 7.5
Sia ORD un programma per ordinare un array A di n elementi che non contenga
apparizioni ripetute del medesimo elemento. La precondizione è che l’array non
contenga ripetizioni, in quanto l’algoritmo deve lavorare sotto questa ipotesi. La
postcondizione, invece, stabilisce che l’array ottenuto sia ordinato, cioè, se un
elemento x precede un elemento y nell’array A, allora x è minore di y.
Formalmente, la specifica generale può essere rappresentata come segue:
{-i(3z'3j (1 < z < n a 1 <j < n a i a A[i] = ^4[/]))} ,
ORD
{Vz (1 <i<n^A[i] <34[z + 1]}
Questa fbf è una formalizzazione corretta della specifica espressa precedentemente
in linguaggio naturale. Una specifica però, indipendentemente dal linguaggio con
cui viene espressa, potrebbe non essere adeguata, ossia non riflettere in maniera
completa o esatta ciò che realmente si desidera ottenere dal programma. In questo
caso, ad esempio, abbiamo omesso di esplicitare un requisito essenziale di ogni
algoritmo di ordinamento, che molti darebbero per scontato, ma la cui mancanza
renderebbe accettabili (formalmente “corretti”) programmi che non eseguono un
vero ordinamento di un array.
Si considerino per esempio i seguenti valori dell’array A:
- A prima dell’esecuzione di ORD è [7 6 2 4 22],
- A dopo l’esecuzione ORD è [2 6 22].
In questo caso A rispetta sia la precondizione sia la postcondizione, ma il valore di
A dopo l’esecuzione non è quello atteso: mancano infatti alcuni elementi di A dopo
l’esecuzione di ORD.
Si noti che anche [3 5 10 11 29] rispetta la postcondizione, anche se nessun
elemento di tale array era in A prima dell’esecuzione di ORD: non è infatti
esplicitato che il programma preservi gli elementi di A, ma solamente che A sia un
array ordinato. La postcondizione deve, quindi, stabilire che tutti e soli gli elementi
che erano nell’array prima dell’esecuzione siano contenuti nell’array ottenuto da
ORD. Si deve perciò stabilire una relazione tra il valore di una variabile prima
dell’esecuzione del programma (o frammento di programma) e il valore della
stessa variabile dopo l’esecuzione. A questo scopo conviene introdurre una
variabile ausiliaria che non è parte del programma, ma ha l’unico scopo di definire
il valore di una variabile a un dato punto del programma. In questo caso un “array
di supporto” B è utilizzato per denotare i valori degli elementi di A prima

3 Si noti che il simbolo di ordinamento “minore o uguale” può essere sostituito dal simbolo
“strettamente minore” in quanto l’array A per ipotesi non contiene ripetizioni. Utilizziamo
questa formulazione apparentemente più debole - che comunque non influisce sui risultati
prodotti dal programma - per supportare future osservazioni.

i
La logica matematica in informatica

dell’esecuzione del programma. È necessario aggiungere nella precondizione che B


sia esattamente uguale all’array A; B non è una variabile del programma, quindi
non cambia durante l’esecuzione di ORD. Si deve aggiungere alla postcondizione
che A, una volta ordinato dal programma, contenga ancora tutti e soli gli elementi
di B. La specifica diventa quindi:
{^(3zB/(l<z<n a 1 <j < n a z a A[i] = A[/])) a
Vz'(l < z < n => A[i] = 5[z'])}
ORD
{Vz (1 < z < n =>A[i] < A[i + 1]) a
Vz (1 < z < n => 3/ (1 <j' < n a A[i] = -8[/]))4 a .
V/’ (1 3z (1 < z < n a A[i] = 5[/]))5}

Una specifica può essere vista come una sorta di “contratto” tra un ipotetico (o
reale) “cliente” e un “fornitore” incaricato di soddisfarne le esigenze mediante un
adeguato software: se il cliente fornisce al software dati che soddisfano la
precondizione, allora l’esecuzione del programma deve produrre dati che
garantiscano la postcondizione; è quindi opportuno che il “contratto” specifichi
tutte (e sole) le informazioni desiderate: niente dovrebbe essere lasciato implicito
per evitare eventuali “contenziosi”, a seguito di risultati che non soddisfino i
desideri del cliente.
Si noti che, quando vengono eliminati alcuni vincoli dalla precondizione, la
specifica complessiva può non essere più adeguata, ossia non rappresentare più
esattamente lo scopo del programma. Per esempio, anche in un caso semplice e
noto come il problema dell’ordinamento, è interessante esaminare che cosa accade
se viene tolto dalla precondizione il vincolo che l’array da ordinare non contenga
alcuna ripetizione. Apparentemente, ciò viene formalizzato semplicemente
cancellando la prima riga dalla precondizione sopra presentata. Questa volta, però,
usare il simbolo ‘<’ invece che ‘<’ è cruciale. L’intera specifica è ancora adeguata?
Si assuma, ad esempio, che il contenuto iniziale di A sia [3, 9, 4, 10, 4]; il
“contratto” stabilito tra il “cliente” e l’implementatore può essere considerato
soddisfatto dal nuovo valore^ = [3, 3, 4, 10]?
Basandosi sull’intuizione che il risultato giusto dovrebbe essere A = [3, 4, 4,
10], si dovrebbe rispondere “no” alla domanda sopra presentata. E pur tuttavia
chiaro che in accordo con il “contratto” un implementatore, il cui programma
produca l’altro risultato, non può essere biasimato e il contratto deve essere
“onorato dal cliente”.

4 La formula stabilisce che tutti gli elementi dell’array ordinato siano presenti anche
nell’array da ordinare.
5 La formula stabilisce che tutti gli elementi nell’array da ordinare siano presenti anche
nell’array ordinato.
262 Informatica teorica

D’altro canto, analizzando in modo più dettagliato il problema dell’ordinamento


inizialmente formulato in modo impreciso, non c’è evidenza che il risultato debba
necessariamente essere [3, 4, 4, 10] piuttosto che [3, 3, 4, 10]: dopo tutto, in alcuni
casi le ripetizioni sono permesse, ma non sono rilevanti, ad esempio, nel caso in cui
un array sia utilizzato come “deposito” di un insieme, cosicché [3, 4, 4, 10] e [3, 3,
4, 10] possano essere considerati completamente equivalenti come output per il
programma.
In altri casi ancora il numero di possibili ripetizioni non influisce sul
significato che viene assegnato al contenuto delle sequenze da ordinare, un
algoritmo potrebbe anche eliminare le occorrenze ripetute e prive di utilità. Ciò
potrebbe risultare abbastanza naturale se, per esempio, invece di ordinare un array,
che usualmente è implicitamente associato a un numero fisso di elementi, si deve
ordinare un file: in questo caso un risultato come [3, 4, 10] probabilmente non
verrebbe rigettato come non adeguato rispetto ai desideri dell’utilizzatore.
L’osservazione appena sollevata dovrebbe motivare maggiormente la necessità
di essere estremamente precisi quando si stabiliscono le richieste per ogni artefatto;
il prezzo da pagare in caso di mancanza di sufficiente precisione potrebbe essere
un’incomprensione tra i diversi “attori”, i cui effetti spaziano dal fastidioso al
disastroso.

ESERCIZI
7.4 Si modifichi la specifica originale per l’algoritmo di ordinamento, in modo
che il risultato desiderato mantenga il numero di occorrenze di tutti gli
elementi di un array inalterato. ’
7.5 Specificare precondizione e postcondizione per una funzione che, dati due
numeri naturali, x e y, restituisce la migliore approssimazione intera per
difetto di logxy.
7.6 Specificare precondizione e postcondizione per un programma che, dati due
numeri interi positivi, x e y, pone la variabile z a 1, se x e y sono primi tra
loro, a 0, altrimenti.
7.7 Si specifichi mediante opportune precondizioni e postcondizioni il seguente
requisito di un frammento di programma FP:
- Sia dato un array A contenente n < interi positivi;
- Gli n interi sono memorizzati nelle prime posizioni di A a partire da
^4[0]. Dopo di essi vi è il valore 0, usato come terminatore;
- FP deve produrre un array B (anche esso terminato da uno 0) che sia il
risultato dell’ordinamento in ordine crescente di A e dell’eliminazione
di eventuali ripetizioni;
- FP usa A come variabile “read-only”.
La logica matematica in informatica 203

7.3 L’uso della logica per specificare proprietà di sistemi


In questa sezione continuiamo ad analizzare il ruolo della logica nell’informatica
seguendo un approccio tipo zooming out; abbiamo iniziato formalizzando linguaggi
formali semplici, e meno semplici, poi abbiamo mostrato come definire proprietà
di programmi; infine generalizziamo il campo di applicazione della .333^
mostrando come possa essere applicata in modo pratico per descrivere sistemi di
vario tipo e le relative proprietà. Questo modo di procedere sottolinea la forza della
logica matematica nella pratica, cioè la sua generalità. Poiché la logica è nata come
uno strumento formale per rendere il ragionamento umano preciso e rigoroso, non
sorprende che possa trovare applicazioni nei più svariati campi dell’ingegneria6.
Non sorprendono neanche l’evoluzione dei sistemi informatici, che pervadono
quasi tutte le attività umane e gli artefatti di ingegneria, e la parallela diffusione dei
linguaggi logici: l’informatica infatti si confronta con la possibilità di processare
automaticamente ogni informazione, quindi le notazioni concepite per formalizzare
informazioni di vario tipo sono particolarmente utili.
In questa sezione, seguendo l’approccio solito usato nel resto del capitolo,
cerchiamo di fornire le basi per permettere al lettore di sfruttare appieno il grande
potenziale della logica matematica nella descrizione e nell’analisi di sistemi
ingegneristici; in particolare concentriamo l’attenzione sull’uso della J^SS^’per la
specifica delle proprietà di sistemi. Rimandiamo il lettore alle note bibliografiche
per riferimenti a una letteratura più avanzata su questo argomento.
Consideriamo inizialmente un semplice esempio. Successivamente
introdurremo un approccio sistematico per specificare requisiti di tipo temporale e,
infine, riprenderemo il caso di studio analizzato nell’Esempio 4.30.

Esempio 7.6
Si consideri un semplicissimo sistema di illuminazione che consiste in una lampada
controllata mediante un interruttore; il suo comportamento è dettato dalla seguente
regola: “Se l’interruttore è premuto, la luce si accende entro A unità di tempo”. Per
descrivere questa specifica, si usano due predicati:
- P_B(t)-. l’interruttore viene premuto al tempo t;
- L_On(t)-. la luce viene accesa al tempo t.
La variabile t indica un istante di tempo; il dominio su cui la formula è definita è
un insieme numerico in cui ciascun elemento rappresenta un istante di tempo. Una
formula di 333.che rappresenta il requisito richiesto è la seguente:

=> 3^(7 < t\ < t + A a L_On(ti))).

6 Crediamo fortemente che la maggior parte delle attività umane potrebbe beneficiare
dell’esposizione alla logica matematica, specialmente le attività di alto livello intellettuale
come organizzazione della società, politica, affari legali ecc.
264 Informatica teorica

La fbf dell’Esempio 7.6 è una formalizzazione corretta della specifica


precedentemente espressa in linguaggio naturale, ma un’osservazione più attenta
mostra immediatamente che essa non è realistica, in quanto sia la versione in
linguaggio naturale che la corrispondente formalizzazione in 5^52?’ lasciano molti
quesiti importanti senza risposta: ad esempio, che cosa accade se l’interruttore
viene premuto quando la luce è accesa? Si spegne o no? Rimane accesa per sempre
o per un tempo prefissato finché l’interruttore o un altro interruttore vengono
premuti? La luce può essere accesa anche se nessuno preme Linterruttore? Si
possono ovviamente fornire le risposte a queste e ad altre domande in relazione alla
specifica originale; alcune di esse però non sarebbero “accettabili” per qualsiasi
utente, sulla base del senso comune - per esempio, chi comprerebbe una lampada
che è sempre accesa indipendentemente dall’utilizzo dell’interruttore,
funzionamento compatibile con la specifica come reso evidente dall’analisi della
corrispondente formula? - altre invece potrebbero essere accettabili in diversi casi,
ma condurrebbero a dispositivi diversi con comportamenti differenti. Per esempio,
alcuni dispositivi, una volta accesi rimangono tali per un tempo prefissato, dopo il
quale si spengono autonomamente; altri si spengono con lo stesso interruttore di
accensione, altri ancora utilizzano un dispositivo differente per spegnersi.
Le osservazioni precedenti mostrano che è necessaria molta più precisione nel
definire il comportamento del piccolo sistema presentato nell’Esempio 7.6 per
ottenerne “un’adeguata specifica”. La Figura 7.2 rappresenta uno schema di questo
comportamento e prova a rispondere alle domande aperte. Questo schema
stabilisce che la lampada è inizialmente spenta e si accende quando l’interruttore
viene premuto. Quando la lampada si accende, rimane accesa per K istanti di tempo
e poi si spegne.

L On

L_Off

P_B(t) t+K

Figura 7.2 Schema del comportamento di una lampada.


La logica matematica in informatica

Con l’aiuto della Figura 7.2 si può produrre una nuova formula in che
formalizza meglio il comportamento atteso della lampada. Per prima cosa si
aggiunge il predicato L_Off(t), con l’ovvio significato di rendere la specifica più
leggibile, anche se è solo il “complemento” di LjDn(t)\ Come conseguenza, si
deve anche esplicitare la complementarietà tra i due predicati mediante la formula
L_0n(t): \/t(L_On(f) <4> —,
La formula seguente, che sostituisce la precedente dell’Esempio 7.6, dovrebbe
esprimere meglio il comportamento esemplificato nella Figura 7.2:
Vt(P_B(f) => $/t\(t < t\ < t + K L_On(tx)) /\Wt2(t +K<t2=> P_Offit2)))).
La nuova formula fornisce certamente un’idea migliore del comportamento della
lampada, ma non è ancora sufficientemente precisa. Rispetto alla formula
precedente essa specifica la durata per cui la luce rimane accesa e definisce
chiaramente che dopo questo tempo la lampada si spegne automaticamente; manca
però di chiarezza in alcuni punti, ad esempio, non definisce se la luce possa
accendersi in assenza di azioni sull’interruttore.
La specifica sopra presentata implica che la lampada si accenda(o sia accesa)
quando l’interruttore è premuto; da quel momento la lampada rimane accesa per K
unità di tempo e poi rimane spenta per sempre. Ancor più gravemente, essa
comporta una contraddizione nel caso in cui, dopo aver premuto Finterruttore ad
un tempo t, lo si prema nuovamente ad un tempo t’, con t’ > t. In questo caso infatti
la formula implica che, per ogni istante nell’intervallo [f ... t’ + K\, la luce sia
accesa, ma, poiché t’ + K è > t + K, essa deve essere anche spenta; questa
contraddizione è data dalla mutua esclusione tra L_On e L Off. Si noti, infatti, che
in accordo alla semantica della una struttura interpretativa in cui la relazione
associata con il predicato P_B valga in più di un istante di tempo t comporta che la
formula non venga soddisfatta.
Questo è un esempio di un errore che si verifica abbastanza frequentemente
quando si generalizza il comportamento di un sistema partendo da pochi
“esperimenti”: in questo caso la Figura 7.2 fornisce una buona “schermata” di un
singolo possibile comportamento, il più semplice corrispondente a una sola azione
suH’interruttore, ma, basando la formalizzazione sull’osservazione di quel solo
caso, si ottiene una specifica abbastanza superficiale. Una volta realizzato questo
nuovo errore, si può porre rimedio con un’altra formula:
Vt(P_B(t) a LOfflf) => V6((t < h < t + K => L On(tx)) a + X))) a
\/t^t2(L_Off{tx) A < t < t2 => -n P_B(t)) => L_Offlt2)).

7 Si noti che questa affermazione non è necessariamente vera: ci sono casi in cui una
lampada può impiegare un po’ di tempo per passare da accesa a spenta e in questo tempo
non può essere considerata né accesa né spenta.
266 Informatica teorica

La prima formula della congiunzione stabilisce che se l’interruttore è azionato


quando la luce è spenta, la lampada si accende e rimane accesa per K unità di
tempo prima di spegnersi; la seconda invece stabilisce che la luce rimane spenta se
l’interruttore non è premuto. In questo modo si specificano in modo adeguato e
senza rischi di contraddizione tutti i possibili comportamenti del sistema. Si noti
che la formalizzazione introduce implicitamente la convenzione che la luce è
accesa in intervalli di tempo chiusi, mentre è spenta in intervalli aperti: si tratta di
un’assunzione ragionevole ma non logicamente necessaria. Inoltre si ottiene in
questo modo una risposta alla domanda “che cosa succede se l’interruttore viene
premuto quando la luce è già accesa?” La risposta è “Niente”: infatti l’antecedente
della prima congiunzione richiede che l’interruttore sia azionato quando la luce è
spenta; poi rimane accesa per K unità di tempo indipendentemente da nuove
possibili azioni sull’interruttore. Questo comportamento è coerente con alcuni
impianti reali (per esempio l’illuminazione di alcuni edifici), benché in alcuni casi
si possa preferire un comportamento tale per cui una nuova azione sull’interruttore
a luce già accesa estenda la durata dell’illuminazione per altre K unità.
In conclusione, anche questo semplice esempio permette di sottolineare alcune
importanti lezioni sui requisiti nelle specifiche di sistema:
- Le specifiche devono essere accurate, complete e precise abbastanza da fornire
aH’implementatore esattamente le informazioni necessarie per costruire il
sistema desiderato, evitando peraltro vincoli troppo rigidi che limitino
inutilmente la libertà di progettazione del design e la realizzazione.
- L’utilizzo di un formalismo matematico non garantisce di scrivere appropriate
specifiche, ma aiuta a trovare “buchi” ed errori che potrebbero rimanere
“nascosti” in specifiche informali.
- In generale, una specifica in può essere una formalizzazione “corretta”
di una informale ma non realistica, nel senso che non cattura esattamente i
requisiti necessari per specificare un “buon sistema”8.

ESERCIZI
7.8 Si modifichi la precedente specifica in ..33S" in modo che, se l’interruttore
viene azionato mentre la lampada è già accesa, essa rimanga accesa per
ulteriori K unità di tempo.
7.9 Si formalizzino mediante formule in 3~(33\e, seguenti affermazioni:
1. L’insieme dei numeri naturali primi è illimitato.
2. Esistono macchine di Turing in grado di stabilire se un generico numero
è primo o no.

8 Si noti che in casi complessi potrebbero esserci differenze importanti tra quello che
l'utilizzatore desidera o crede di desiderare e quello che è veramente necessario per il buon
funzionamento del sistema. Questo argomento va, però, oltre lo scopo introduttivo di
questo testo.
La logica matematica in informatica 267

3. Esistono macchine di Turing che calcolano funzioni con dominio finito.


7.10 Si specifichi, mediante una formula di /7' A, un apparato che funziona nel
seguente modo:
1. All’istante 0 l’impianto emette un segnale s, che può essere 0 o 1;
2. Se, dopo l’emissione di 5 e prima dello scadere di 3 secondi, viene
ricevuto lo stesso segnale in risposta, allora allo scadere del terzo
secondo viene emesso un nuovo segnale, invertendo il valore di quello
inviato precedentemente;
3. Se, dopo l’emissione di 5 e prima dello scadere di 3 secondi, non viene
ricevuto lo stesso segnale in risposta, allora allo scadere del terzo
secondo viene emesso lo stesso segnale inviato in precedenza.
Il comportamento dell’apparato si ripete periodicamente con periodo di 3
secondi applicando sempre la stessa regola. Si noti che l’apparato emette un
segnale soltanto in istanti multipli di 3.
7.11 Specificare, mediante formule della J'^SS?7 il comportamento di un canale di
comunicazione che riceve in ingresso una sequenza finita di caratteri
terminata dal simbolo speciale ‘$’ e produce in uscita una sequenza finita di
caratteri, anch’essa terminata da ‘$’, in cui compaiono tutti e soli gli
elementi della sequenza in ingresso, una sola volta.

7.3.1 Un approccio sistematico per la rappresentazione di requisiti temporali


In generale, l’utilità dei formalismi nella pratica dipende dalla disponibilità di
metodi adatti che aiutino e guidino l’utente attraverso il processo di trasformazione
di poche, spesso vaghe ed imprecise, idee in formule ben scritte e coerenti.
Analogamente, imparare la sintassi e la semantica di un linguaggio di
programmazione non fa un buon programmatore se non vengono
contemporaneamente compresi i principi fondamentali come la modularizzazione,
l’astrazione ecc. Benché sviluppare una competenza e una metodologia per
utilizzare gli strumenti formali nella pratica vada molto oltre lo scopo di questo
testo, in questa sezione si introduce un approccio sistematico per trattare vincoli
temporali nella specifica dei sistemi.
Nello specificare un sistema in cui il tempo gioca un ruolo cruciale, una scelta
importate consiste nel determinare il dominio temporale in cui la specifica è
definita.
Finora abbiamo considerato il dominio temporale come un generico dominio
numerico; ci sono tuttavia importanti differenze tra i diversi tipi di questo critico
dominio che si devono prendere in considerazione. L’attenzione è qui focalizzata
sulle più importanti, in particolare la differenza tra:
- Domini discreti: tipicamente numeri Naturali o Interi;
- Domini densi: tipicamente numeri Razionali;
- Domini continui: tipicamente, numeri Reali.
268 Informatica teorica

Vedremo che la formalizzazione e l’interpretazione di alcuni requisiti dipendono


dal dominio scelto.
Oltre al dominio temporale, prima di iniziare a definire le formule di una
specifica, bisogna identificare i predicati necessari. Facendo riferimento
all’esempio precedente della specifica della lampada, consideriamo i predicati:
F’_5(t), L_On(f) and LOfflt).
Abbastanza spesso, ispirandosi ai modelli operazionali tradizionali, conviene
specializzare i predicati utilizzati in un sistema di specifica in:
- Predicati che rappresentano uno stato del sistema (per esempio, L_On(t),

- Predicati che rappresentano un evento (ad esempio, P_B(t)).


Analizzando il significato di “stato”, si osserva che uno stato rappresenta una
“fotografia” del sistema, ad esempio la condizione in cui si trova il sistema in un
determinato momento della sua evoluzione.
In questo contesto consideriamo solo sistemi con domini di stato finiti (o
almeno discreti): in questi sistemi rappresentare gli stati come predicati logici è
abbastanza naturale e semplice, come mostra il caso L_On e L_Off. In generale, se
il tempo non è un dominio discreto, uno stato permane nel tempo e non è
istantaneo.
Stati differenti di sistemi differenti condividono alcune proprietà tipiche di
evoluzione nel tempo. È, quindi, utile introdurre notazioni abbreviate adatte a
descrivere situazioni comuni. Dato che uno stato è una fotografia del sistema che lo
descrive nel tempo e un sistema può cambiare il suo stato, è interessante stabilire
per quanto tempo il sistema rimane in uno stato e in che istante preciso lo cambia.
Si introducono quindi le notazioni Fino_ad_ora_S(t) e Da_ora_in_avanti_S(f), per
indicare che il sistema è nello stato S fino al tempo t e che il sistema è nello stato S
a partire dal tempo t. Le abbreviazioni vengono definite in modo formale come
segue:
- Fino_ad_ora_S (t): 38(8 > 0 a V6(t - 8 < t\ < t => 5(6)))
Si noti che la formula non implica che S sia lo stato presente.
- Da_ora_in_avanti_S(f)\ 38(8 >0AV6(t<6<t + 8=> 5(6)))
In questa formula il presente è incluso, anzi 5 si mantiene per tutti gli istanti 6
tali che 6 sia nell’intervallo [6 t + 8).
A differenza degli stati, gli eventi sono occorrenze istantanee; perciò, sono
formalizzati da predicati che valgono (sono veri) solo in un isolato istante di
tempo; questa proprietà è particolarmente significativa quando il dominio di tempo
non è discreto; tipicamente, ma non necessariamente, un evento coincide
temporalmente con un cambio di stato e può esserne la causa - ad esempio, P_B -
o il “segnale” - ad esempio la transizione da On a Off o viceversa. Per ciascun
predicato E che definisce un evento, si introduce la notazione Evento_E, come
abbreviazione per la formula generale
269
La logica matematica in informatica

=> 38(8 > 0 a V/f((t - 8<Zf<fvf<Zt<f + 8)^>—i ^(fi)))),


che indica che nell’intervallo (t-8, t+8) E si verifica solo in t.
Si consideri nuovamente l’esempio della lampada introdotto nella sezione
precedente. Sulla base della nuova notazione si può riscrivere la formalizzazione
precedente in modo più leggibile e sistematico, come segue:
dt(L_On (t) « -.L_Off (0) a

EventoPB a

Vt(^P_B(t ) a Fino ad ora L Off (t)) =>

Vtj((t < tj < t+ k) => L_On (tj) a L Off (t + &))) a

VtvtfLJ)ff(t ,)aW( <t<t2)^ —tP_B(f)) LOff (t2)) .

La prima espressione indica la mutua esclusione e la complementarità tra On e Off;


la seconda stabilisce che P_B(t) è un evento; la terza e la quarta riscrivono la
fomulazione precedente in modo più sistematico, esplicitando la nuova notazione
l'Fino_ad_ora'' (non si usa invece l’analoga Da_ora_in_avanti per la clausola
Vti(t < fi < t + K => L_On(t^}, perché essa specifica la durata esatta dello stato a
differenza della generica Da_ora_ln__avanti. )
Si sottolinea che l’introduzione di Finoadora e di Da_ora_in_avanti altera
leggermente la precedente convenzione sulla durata dello stato, assegnando a
ciascuno stato un intervallo chiuso a sinistra e aperto a destra.

ESERCIZI
7.12 Nell’esempio sopra riportato si interpreta la mutua esclusione tra i due stati
L_On e LjOff assumendo che in qualunque momento la lampada sia
necessariamente in uno dei due stati. Ci sono casi in cui quando la
transizione tra i due stati non può essere trascurata, esiste un intervallo di
tempo non nullo in cui la lampada non è né accesa né spenta. Si adatti la
specifica precedente a questi casi. Si noti che tale compito può essere
eseguito in due modi differenti:
1. Si introducono nuovi stati per esprimere che il sistema passa da uno
stato stabile a un altro.
2. Il dominio stato si mantiene inalterato, ma il requisito di mutua
esclusione deve essere modificato e le transizioni tra stati non possono
essere modellate mediante notazioni Fino ad ora e Da_ora_in_avanti
relative allo stesso istante.
7.13 Si formalizzi in il comportamento di una cabina telefonica, tale per
cui quando una persona arriva e trova la cabina occupata non può entrare;
quando invece la cabina è libera, allora la persona entra ed esce entro un
tempo K.
7.14 Si formalizzino le seguenti regole per l’apertura di una cassaforte:
1. esistono 2 chiavi diverse in possesso di 2 impiegati diversi;
270 Informatica teorica

2. la cassaforte si apre se e solo se le due chiavi vengono inserite a


distanza non superiore a A istanti temporali una dall’altra, quando la
cassaforte è chiusa;
3. una volta aperta, la cassaforte si richiude automaticamente dopo K
istanti temporali;
4. la chiusura e l’apertura della cassaforte sono istantanee.
Si consideri il dominio temporale continuo.
7.15 Si considerino le seguenti regole che descrivono il comportamento di un
Bancomat:
1. Inizialmente il terminale è nello stato Idle',
2. L’inserimento di una carta genera la transizione allo stato
InserimentoCodice',
3. Il codice è composto da 5 cifre;
4. L’inserimento del codice termina quando l’utente ha inserito le 5 cifre e
ha premuto il bottone ENTER, o dopo 5 secondi dall’inserimento della
quinta cifra;
5. Quando il codice è stato inserito, il terminale va nello stato Verifica',
6. Quando il terminale si trova nello stato InserimentoCodice, se passano
più di 5 secondi senza che venga inserita una cifra e la quinta cifra non
è ancora stata inserita, la carta viene restituita all’utente e il terminale
toma nello stato Idle.
Si formalizzino le regole 4., 5., e 6. attraverso formule della CFÌESi Si
utilizzino i seguenti predicati:
- Idle(t),
- InserimentoCodice^),
- Verificati),
- Cifralnserita (t, k) indica che la A>esima cifra è stata inserita entro
l’istante t;
- PremiCifra(t)
- PremiEnter(t)
- CartaRestituita(t)

7.3.2 Caso di studio


Per concludere la sezione ritorniamo all’esempio di un semplice passaggio a
livello, formalizzato precedentemente nell’Esempio 4.30 mediante le reti di Petri.
Analogamente a quanto fatto precedentemente, tra le varie versioni del problema si
considera in questo caso una versione molto semplificata, in cui il sistema consiste
di un solo binario unidirezionale e di un solo treno. Il requisito principale è
ovviamente che quando il treno sta attraversando la strada - nota come la sezione
critica - la sbarra del passaggio a livello sia chiusa.
271
La logica matematica in informatica

È necessario creare un modello di due differenti parti del sistema: il


comportamento del treno e il controllo della sbarra. Ricordiamo prima però la
notazione introdotta neH’Esempio 4.30: R è la sezione inclusa tra il punto in cui il
passaggio del treno è rilevato da appositi sensori e l’inizio della sezione critica e d
è la sua lunghezza; 7 è la sezione critica, cioè l’incrocio dove si trova la sbarra, e la
sua lunghezza è A; Ar e En sono gli estremi di R e Ex è l’estremo finale di I. Nella
specifica logica useremo gli stessi simboli come predicati, per cui Ar(t) indica che
il treno sta entrando in R e similmente per gli altri predicati.
Si ricorda inoltre che la velocità del treno varia nell’intervallo Kmin, Fmax]. Il
tempo necessario per attraversare Rei, varia quindi in base alla velocità del treno,
in particolare:
Se il treno viaggia alla velocità Pn,in, il tempo per attraversare le sezioni R,I e
1 + R sarà massimo:

- Se il treno sta viaggiando alla velocità Fmax, il tempo per attraversare le sezioni
R,IeI + R sarà il minimo :
d d+h
R in in )A(5Imb
Vmax Vmax
Analizziamo ora il comportamento del treno:
- Se il treno entra nella regione R, esce da R ed entra in I dopo almeno 8Rniin
istanti di tempo e al più òRmax; formalmente
< t, < t+ 5Sirax )) ;
- Se il treno esce dalla regione R, per entrare nella regione I, significa che era
entrato in R almeno 8Rniin istanti di tempo prima e al più òRmax istanti di tempo
prima, e uscirà da I almeno 8Tmin istanti prima e al più S^x istanti prima:
at.UrOjA (7-s^ <t, <t-zRvia ))) A
ar/^COA (r + staùl <r( ))) ;

- Se il treno esce dalla regione I significa che era entrato in R almeno 8min istanti
di tempo prima e al più 8max istanti di tempo prima:
< t, < t- 6nm ))) ;
- Il treno è nella sezione critica al tempo t se vi è entrato prima di t e da quel
momento non è ancora uscito:
Vr(Zw(O« ^(JEn^)^ <t2 <t)))) •

E abbastanza intuitivo che In è uno stato del sistema, mentre Ar, En ed Ex sono
eventi. Perciò si può fare riferimento alle proprietà generali introdotte nella Sezione
7.3.1. Inoltre, dato che si assume di considerare l’arrivo di un solo treno, si può
272 Informatica teorica

specificare che questi eventi occorrono solo una volta nell’intero dominio di
tempo:
^(O^V^^tv^Q^.^))))
con E che definisce un qualunque evento tra Ar, En, Ex.
Si deve ora descrivere il comportamento della sbarra. Prima di tutto, come ulteriore
e “drastica” semplificazione, si assuma che il tempo per l’apertura e per la chiusura
della sbarra sia trascurabile. Gli stati della sbarra vengono quindi formalizzati dalla
coppia di predicati Down e Up. Ovviamente essi sono in mutua esclusione e
complementari, cioè:
— \/t(Down (/) —.Up (/)) .

A questo punto è necessario delineare la strategia di funzionamento della sbarra,


cioè quando tenere la sbarra alzata e quando abbassarla. Tale strategia deve essere
decisa tenendo conto che l’obiettivo principale di un passaggio a livello è la
sicurezza delle macchine e delle persone che attraversano la strada interessata.
Questo requisito fondamentale è formalizzato dalla formula:
— Vr(Z«(r) => Down (t))
che indica che quando il treno è nella regione critica la sbarra deve essere
chiusa.
- Con questo obiettivo ben presente, si osservi che, quando il treno entra in R (e
quindi il suo arrivo viene segnalato dal sensore), ha bisogno di almeno 8Rrrlìr,
istanti di tempo per entrare in I e al massimo 8max per uscire da I: in questo
intervallo di tempo la sbarra deve essere chiusa; formalmente, poiché non è
nota a priori la velocità del treno, è necessario assumere una posizione
prudenziale per assicurare il requisito di sicurezza, quindi, la sbarra sarà tenuta
chiusa per tutto questo intervallo:
V/(^r(Q Vtj((t + òRmin <t{ <t + òrrax ) Down (z\ ))) .
- In questo modo è piuttosto semplice verificare che la sicurezza viene garantita,
poiché la proprietà utilizzata per esprimerla è implicata dalla decisione di
tenere la sbarra chiusa per tutto l’intervallo considerato al punto precedente (la
dimostrazione formale viene riportata nell’appendice a questo capitolo). Si
noti tuttavia che, in modo analogo alla domanda posta per la lampada
introdotta nell’Esempio 7.6 (“la lampada si può accendere anche se non viene
premuto l’interruttore?”), la formula precedente non specifica se e quando la
sbarra non deve essere chiusa. Quindi, un “responsabile pigro” potrebbe
decidere di tenere la sbarra sempre chiusa, in modo da garantire la sicurezza e
adempiere al suo “contratto”. Il contratto, infatti, è focalizzato sul requisito più
critico, cioè la sicurezza, senza prestare sufficiente attenzione all’utilità
(utility, in termini tecnici) dell’intero sistema. Per evitare questo
inconveniente, è necessario specificare esplicitamente che la sbarra è chiusa
La logica matematica in informatica 273

nell’intervallo di tempo critico solo se un treno entra nella regione R,


precisamente:
Vt(Down (t)=> - 8^ <^-8^ )A^r(t1))) .
Nonostante le consistenti semplificazioni introdotte nel caso di studio, alcuni punti
delicati meritano qualche approfondimento:
- La scelta di tenere la sbarra chiusa solo nell’intervallo [8^^ 8max] è
ovviamente ragionevole per garantire la sicurezza. Tuttavia, anche la scelta
più conservativa di un intervallo di tempo più lungo, che includa |~3Rniin 8max],
avrebbe funzionato, portando però a periodi di chiusura della sbarra non
necessari e, quindi, a una perdita di utilità. Sarebbe possibile considerare
invece un intervallo più breve? E ancora, come si può formalizzare il requisito
sull’utilità in modo da garantire che la sbarra non rimanga chiusa se non è
strettamente necessario, cioè se il treno non è in /?
- Inizialmente potrebbe sembrare necessario utilizzare la doppia implicazione
nel requisito di sicurezza:
¥t(In(t) o Down (t)) .
Bisogna però ricordare che la velocità del treno non è fissata a priori, quindi
non è noto esattamente quando il treno sarà in I, di conseguenza è necessario
decidere quando chiudere la sbarra una volta che il treno è entrato in R.
- Un possibile “trucco” permetterebbe di sfruttare l’assunzione che l’apertura e
la chiusura della sbarra avvengono senza “consumare” tempo. Quindi,
riducendo la dimensione di 7? a 0, si può ottenere che la sbarra sia chiusa se e
solo se il treno è in I, indipendentemente dalla velocità. E’ evidente però che
questo risultato viene ottenuto sulla base di un’astrazione matematica che
introduce una semplificazione non realistica della descrizione del problema. In
questo caso dunque il modello adottato si rivela eccessivamente semplicistico
per rappresentare in maniera completamente adeguata il comportamento del
sistema reale. •
Le osservazioni appena fatte suggeriscono come spostarsi dal caso di studio molto
semplificato appena visto a una sua versione più realistica e complessa (e gli
Esercizi 7.15 e 7.16 suggeriscono alcuni passi in tale direzione). Per completare
l’analisi del caso di studio, mancano ancora alcune osservazioni:
- La specifica presentata è composta da tre parti fondamentali:
1 . La descrizione del contesto in cui il sistema opera: in questo caso il treno
e la sua velocità.
2 La definizione delle proprietà che devono essere garantite dal
comportamento del sistema: in questo caso la proprietà di sicurezza, ma
anche quella di utilità.
3 Le decisioni di progettazione prese per garantire le proprietà desiderate:
nel caso considerato, quando aprire e chiudere la sbarra.
274 Informatica teorica

Figura 7.3 Schema generale per la progettazione di sistemi di controllo.

- Questa è una situazione abbastanza tipica quando viene progettato uno


strumento di controllo in un ambiente “da controllare”, come suggerito dalla
Figura 7.3.
- Il caso di studio mostra anche che le tre parti della specifica non sono
composte da formule indipendenti: le formule che costituiscono la terza parte
sono costruite con l’obiettivo di garantire le proprietà specificate nella seconda
parte. Quindi, dovrebbe essere anche possibile dedurre le formule della
seconda parte come conseguenze logiche delle formule nelle altre due: in altre
parole, è possibile mostrare la correttezza (o 1’esistenza di eventuali errori)
della specifica rispetto al requisito di sicurezza9, mostrando che la prima e la
terza parte implicano (o non garantiscono) la seconda.
- L’implicazione appena evidenziata è abbastanza chiara anche dalla
descrizione informale delle diverse formule che compongono la specifica. In
generale, però, dedurre che alcune formule logiche implicano logicamente
altri requisiti è un compito non banale. In tal caso, l’intuizione può essere
supportata da un’ulteriore, applicazione della logica matematica che, come
abbiamo visto nel Capitolo?, oltre a offrire uno strumento per esprimere
proprietà in modo formale, offre gli strumenti per costruire una prova formale
di implicazioni logiche: in questo caso quindi le proprietà desiderate possono
essere garantite con vere e proprie dimostrazioni di teoremi. Ancora con
riferimento al Capitolo 2, le dimostrazioni completamente formalizzate sono
molto più dettagliate di quelle basate sull’intuizione umana; quindi, da un lato,
sono molto meno intuitive e più noiose, ma dall’altro hanno la precisione e il
rigore, e quindi la “garanzia”, tipici dei linguaggi formali; inoltre, sono adatte

9 Lo stesso tipo di osservazione potrebbe essere sollevata anche nel caso di proprietà di
programmi: una volta che le precondizioni e le postcondizioni sono state definite ed è stato
costruito un programma allo scopo di garantire le postcondizioni sotto le ipotesi delle
precondizioni , è possibile affrontare il problema di verificare se il programma progettato
rispetta gli obiettivi prefissati.
275
La logica matematica in informatica

per essere supportate da strumenti “meccanici” appropriati. Questo importante


aspetto tuttavia non è tra gli obiettivi di questo libro e lasciamo al lettore
l’approfondimento di tale argomento attraverso le letture consigliate nelle note
bibliografiche. L’appendice di questo capitolo, tuttavia, offre una breve
introduzione a questa forma più approfondita di applicazione della logica
matematica.

RIASSUNTO DEL CAPITOLO


In questo capitolo abbiamo introdotto l’uso della logica in ambito informatico. In
particolare ci siamo concentrati sulle possibili applicazioni della logica del
prim’ordine.
Il capitolo ha un approccio tipo zooming out: infatti abbiamo iniziato
definendo mediante formule della i linguaggi formali, poi abbiamo studiato
come definire le proprietà dei programmi e, infine, abbiamo generalizzato le
applicazioni della mostrando come possa essere usata per definire generici
sistemi e le loro proprietà. Questo modo di procedere enfatizza la generalità della
logica matematica nella pratica. Poiché la logica è nata come uno strumento
formale per rendere il ragionamento umano preciso e rigoroso, non sorprende che
essa possa trovare applicazioni in praticamente ogni campo dell’ingegneria. Per
sottolineare maggiormente la versatilità della logica, il capitolo ha presentato anche
un caso di studio che permette il confronto con l’uso di formalismi di tipo
operazionale.

ALTRI ESERCIZI
7.16 L’assunzione che il tempo impiegato dalla sbarra per passare dallo stato di
“aperto” a quello di “chiuso” sia trascurabile è, come precedentemente
osservato, una semplificazione drastica che rende il modello lontano dalla
realtà. Infatti, il treno potrebbe percorre un considerevole tratto di strada
mentre la sbarra cambia la sua posizione.
Si modifichi la specifica appena introdotta, assumendo che la sbarra si alzi e
si abbassi in y unità di tempo. Che relazione deve legare y e le altre costanti
di tempo per garantire la sicurezza? Quando deve essere dato il comando di
chiudere o aprire la sbarra dal controllore al sottosistema? Qual è l’ultimo
istante di tempo utile per dare tale commando in modo da evitare di tenere la
sbarra inutilmente chiusa e garantire comunque la sicurezza?
7.17 Dopo aver introdotto la generalizzazione proposta nell’Esercizio 7.16, si
generalizzi ulteriormente il problema considerando la possibilità di avere più
treni che viaggiano nella stessa direzione sul binario. Si può e si dovrebbe
assumere che la distanza (sia di spazio sia di tempo) tra due treni consecutivi
sia sufficiente per garantire la sicurezza, cioè che, dopo che la sbarra ha
iniziato ad aprirsi, essa deve essere completamente aperta prima di essere
chiusa nuovamente, garantendo però la completa chiusura prima dell’arrivo
del prossimo treno.
276 Informatica teorica

Soluzioni schematiche di esercizi scelti


7.3 x e L <=>
(3 w, u,v,z
(x = wfucvfz A
H = |v| A
(f= a v f= b) /\
(w,u,v^z e {a,è}*)).
7.5 Precondizione: xeNa^sN
Postcondizione: flx.y) e N a (x^ < y a x^44 > y).
7.7 Introduciamo in primo luogo la funzione lungh(z)w che conta il numero di
elementi diversi da 0 e “terminati” da uno 0 in un array z:
lungh(z) = « <=> ((0 < « < Nmax a z[n] = 0 a
-i3z'(0 < i < n a z[z] = 0)) v
(m = -1 /\-3i(0 < i < Nmax az[z] =0))).
Sfruttando tale funzione possiamo ora definire le formule che descrivono la
precondizione e la postcondizione.
{3m(m > 0 a lungh(A) = n a Vz'(0 < z < n => d[z] > 0))}
FP
{Bm (lungh(B) = m a Vz (0 < z < m -1 => B[z] < B\i +1]) a
Vz(O < z < lungh(A) => 3j((0 <j <m) /\ (J[z] = B[/]))) a
V/(0 <j< m=FBi((Q <i < lungh(A)) a (J[z] = 5[f]))))}.
7.9 Indichiamo con la funzione a due argomenti f(M, x) il valore calcolato dalla
MT M con ingresso x e introduciamo il predicato Primo(x) così definito
Primo(x) -i3z3w (z^l a wFL a x = zw)
(a) Vx3y(y > x a Primo(y)')
(b) 3A7Vx(((/(A7, x) = 1) <=> Primo(xj)/\ x) = 0) <=> —iPrzmo(x)))
(c) BMBydz(z >y z) = ±).
7.11 Introduciamo le funzioni
- in(z) che restituisce l’z'-esimo carattere della stringa di ingresso
(per convenzione scriveremo in(/) = a per indicare che in(/) non esiste);
- out(z') che restituisce l’z-esimo carattere della stringa di uscita.
Una possibile soluzione è la seguente:
Bn,m [(«> 0 a in(w) = $ a 'djlj <n=> in(/) $ a in(/) s) a
V/V > n => in(/) = s) a
(0 < m < w a out(m) = $ a V/(/ < m => out(/) $ a out(j) a) a

10 Si noti che, per rendere le formule più comprensibili, ci siamo concessi qualche “libertà”
rispetto alle strette regole della sintassi della FFFF'-. se gli array sono rappresentati come
funzioni, applicare una funzione a un simbolo di funzione dovrebbe essere vietato.
Possiamo però interpretare la scritturalungh(z) = n come una notazione abbreviata per
lungh_z(n), dove lungh_z è un predicato vero quando la lunghezza di z è n. Similmente z <
lungh(A) va letto come 3zn (lungh_A{m) /\i< m), e così via.
La logica matematica in informatica

V/(/ > W => out(/) = £)) A


Vj (/' <n^> 3i!ya.!j) = out(i)) a
Vj (/ < w => 3z (in(z') = out(/))) a
Vikfj!!j < m /\ i < m /\ i *f) => out(/) out(z))].
7.15 Si noti che i predicati Idle(t), InserimentoCodicef), Verifica/),
Cifrainserita/, k) rappresentano degli stati, mentre PremiCifra/),
PremiEnter(t) e CartaRestituita/) rappresentano degli eventi.
Regole (d) ed (e):

!Fino_ad_ora/Inserimento Codice!/) /\
Fino_ad_ora_CifraInserita/, k)
a 0 k < 5 a PremiCifra/))
=> Da_ora_in_avantiJlnserimentoCodice/) a
Da_ora_in_avanti_CifraInserita/, £+1)) a
{Fino_ad_ora_InserimentoCodice(t, 5) a PremiEnter/)
=} Da_ora_in_avanti_~InserimentoCodice!t) a
Da_ora_in_avanti_Verifìca/)) a
!Fino_ad_ora_InserimentoCodice/, 5) a PremiCifra/ - 5) a
(Vt7 !ft - 5 < ti < f) => (—PremiEnter(t) a —PremiCifraity)))
=> Da_ora_in_avanti_—InserimentoCodice(t) a
Da_ora_in_avanti_ Verifica!/)) ;
Regola (f):

Fino_ad_ora_InserimentoCodice/) /\
Fino_ad_ora_CifraInserita!t, k)/\0^k<5/\
kftì !t~5 <t]<t=> —PremiEnter(t) a —PremiCifra(t))
=> Da_ora_in_avanti_—InserimentoCodice(t) a
Da_ora_in_avanti_Idle!t) a Cartarestituita/).
278 Informatica teorica

APPENDICE 7.A

Dimostrazioni formali di correttezza

In questo capitolo abbiamo mostrato come la logica matematica possa essere un


valido strumento per formalizzare proprietà e specificare sistemi (sia informatici
che, in generale, ingegneristici) in modo preciso. Inoltre nel caso di studio della
Sezione 7.3.3 si è mostrato intuitivamente come, una volta specificato un sistema
in modo formale tramite ad esempio si possa provare la validità di alcune
sue proprietà, deducendole dalla specifica introdotta. Poter utilizzare la specifica
logica per dimostrare formalmente alcune proprietà del sistema descritto aumenta
le potenzialità di tale strumento. Abbiamo infatti osservato che descrivere
formalmente i requisiti di un sistema attraverso la ._^7?gc’aiuta a tovare errori e a
ragionare sul sistema in analisi, ma questo strumento non ci dava comunque
garanzie di correttezza, che vengono invece ottenute mediante una dimostrazione
formale.
In questa appendice ci proponiamo, quindi, di approfondire l’aspetto della
verifica formale di proprietà di sistemi, limitandoci però a due specifici contesti fra
quelli affrontati in questo capitolo. In particolare, analizzeremo come sia possibile
provare proprietà di programmi, attraverso un insieme di regole, che permettono di
verificare che, quando vale la precondizione, dopo l’esecuzione del programma, se
questo è corretto, vale anche la postcondizione. Inoltre analizzeremo come sia
possibile provare la validità di proprietà per sistemi di vario altro tipo modellizzati
attraverso la , A7-^ Precisiamo da subito che questa appendice non vuole analizzare
l’argomento della verifica formale in modo esaustivo né approfondito, ma si
propone solamente di descrivere alcuni concetti introduttivi che completino la
trattazione dell’uso nella logica in informatica e diano l’intuizione delle
potenzialità di tale strumento formale nello sviluppo di programmi e sistemi
corretti.
L’approccio tradizionale alla correttezza formale di programmi e sistemi11 è in
generale affidato ad una attività più o meno sistematica di testing e debugging. Una
volta codificato completamente il programma o modellizzato il sistema, il
programma o il modello viene testato mediante dei casi di test allo scopo di
verificare se si comporta correttamente. Se ciò non avviene, ossia i risultati non
corrispondono alle attese, bisogna cercare il bug, cioè la fonte dell’errore,
correggere il programma e ripetere la fase di testing sulla versione aggiornata.
Quando il programma (il modello) si rivela soddisfacente per un numero
sufficientemente grande e significativo di casi di test, si giunge alla conclusione

11 I sistemi (informatici o ingegneristici, in generale) vengono solitamente rappresentati


attraverso un modello (formale o semi formale) operazionale e tale modello viene utilizzato
per analizzare la correttezza del sistema che si vuole sviluppare.
279
La logica matematica in informatica

che il programma, probabilmente, è corretto. Non è questo il luogo per discutere


metodi e tecniche di testing e debugging sistematico dei programmi, argomento
tipico dell’ingegneria del software. Teniamo semplicemente a puntualizzare la
debolezza intrinseca di tale metodologia per garantire la correttezza di un
programma. Infatti, il solo modo per raggiungere la sicurezza totale della
correttezza di un programma, servendosi di tale metodologia, consiste nella verifica
del funzionamento del programma per un insieme di casi di test che possano
ricoprire tutti i possibili dati in ingresso, il che, in generale, risulta impossibile
quando il dominio dei dati in ingresso è infinito. Inoltre, non è sempre possibile
anticipare i risultati attesi da un programma per un dato ingresso. Ad esempio,
mentre, nel caso di un programma di riordinamento di vettori, si può facilmente
effettuare il testing fornendo i dati in ingresso secondo qualche criterio,
calcolandone, a mano, il risultato corrispondente, facendo girare il programma e
verificando, infine, che i valori di uscita coincidano, se scriviamo un programma
che calcola la «-esima cifra decimale di re, difficilmente riusciremmo a dimostrare
la sua correttezza calcolando, a mano, il risultato corrispondente, per esempio, a
n = IO9.
Se fossimo invece in grado di dimostrare la correttezza formale di un
programma (modello) sulla base della sua semantica, mediante un appropriato
ragionamento matematico, non avremmo più bisogno di affidarci al testing. Non si
deve però giungere alla conclusione errata che, in futuro, la dimostrazione formale
della correttezza renderà il testing dei programmi obsoleto o inutile. Infatti ci sono
svariati motivi che permettono di giungere alla conclusione che le dimostrazioni
formali di corretteza da sole non bastano o sono irrealistiche. Alcuni di questi sono
i seguenti:
1. Le dimostrazioni formali richiedono l’espressione in termini formali delle
specifiche dei programmi. Molto spesso però si compiono errori proprio nel
processo che conduce dalle descrizioni informali dei problemi a quelle
formali.
2. Le dimostrazioni formali possono presentare una complessità pari a quella
delle stesse attività di progetto e di codifica e risultano quindi soggette alla
medesima probabilità di errore.
3. E richiesta, al programmatore, una conoscenza matematica di base spesso
troppo approfondita rispetto alla formazione media.
Lo scopo di questa appendice è quindi limitato a fornire al lettore i primi rudimenti
per poter successivamente padroneggiare le difficoltà inerenti alla dimostrazione
formale di proprietà di programmi e di sistemi descritti con formalismi logici. Con
ciò non intendiamo entrare nella disputa (spesso senza senso) se sia meglio la
dimostrazione formale o il testing. A nostro parere, il testing di programmi e
prototipi di sistemi e la dimostrazione di correttezza sono strumenti complementari,
entrambi utili nell’ingegneria del software per migliorare la fiducia del progettista
sul suo prodotto.
280 Informatica teorica

Infatti, la dimostrazione di correttezza di programmi e la dimostrazione della


validità di una proprietà a partire da una specifica formale sono vere e proprie
dimostrazioni di teoremi matematici. Quando ci si trova di fronte a un problema
nuovo e ancora irrisolto, il matematico formula una congettura (candidata a
diventare un teorema), cercando di dimostrarla. Dopo qualche tentativo infruttuoso,
egli verifica la validità del suo teorema su qualche caso, scelto come campione,
cercando di trovare possibili controesempi che possano provare che l’affermazione
è falsa. Molto spesso, nel processo infruttuoso di ricerca di controesempi, il
matematico scopre nuovi aspetti rilevanti che possono suggerire un nuovo metodo
di dimostrazione e così via. Analogamente, alternando tra tentativi di
dimostrazione e testing, si può giungere alla conclusione che un programma è
corretto oppure rilevare qualche errore.
Osserviamo che, in questa procedura, il testing viene utilizzato con lo scopo
esplicito di provocare una condizione di errore. Ciò si accorda perfettamente con
una celebre affermazione di E. W. Dijkstra in cui affermò che “il testing è utile per
dimostrare la presenza di errori, ma non la loro assenza”.
E importante inoltre osservare che in questa appendice affronteremo la prova
di proprietà di programmi e sistemi utilizzando la deduzione logica, attraverso
l’assiomatizzazione, come visto per l’aritmetica di Peano nella Sezione 2.4.1. Tale
tecnica, come già accennato, richiede robuste conoscenze matematiche e una certa
profondità di analisi da parte dello sviluppatore. In letteratura (si vedano le note
bibliografiche di questo capitolo) esistono altre tecniche per provare proprietà, che
se pur parzialmente, si basano su una procedura algoritmica. Un esempio fra tutti è
la risoluzione, che permette di ricavare la validità di proprietà a partire da una
specifica attraverso regole semplificative.
Infine, prima di entrare effettivamente nello studio di come effettuare
dimostrazioni formali nei casi di interesse, discutiamo brevemente la relazione tra
correttezza e il concetto più generale di affidabilità. L’aver dimostrato la
correttezza di un programma o la validità di certe proprietà in una specifica
aumenta la nostra fiducia sul comportamento dell’oggetto di studio. La correttezza
però è solo uno degli aspetti che contribuiscono a ciò che classifichiamo
informalmente come affidabilità. Ad esempio, si supponga di progettare un
programma che calcoli il fattoriale di un qualunque intero n > 0. Supponiamo
anche di trovare due programmi diversi, P e P’, che risolvono il problema e per i
quali sia già stata dimostrata la correttezza rispetto ai requisiti precedenti. Si
supponga tuttavia, che nei casi in cui n < 0, P cicli indefinitamente, mentre P'
mandi in uscita un messaggio diagnostico e si fermi. Chiaramente ci si fiderà
maggiormente di P’ piuttosto che di P.
In alcuni casi, un programma non corretto potrebbe risultare più affidabile di
uno corretto. Nell’esempio precedente, si supponga che sia P cha P’ usino metodi
approssimati per calcolare il fattoriale di n, quando n cresce oltre un certo limite. Si
potrebbe stabilire, come requisito di correttezza, che l’uscita del programma sia
un’approssimazione del valore effettivo di n\ entro un determinato intervallo, ad
281
La logica matematica in informatica

esempio I Ora. Si supponga ora che P sia corretto rispetto a questo requisito, mentre
P’ garantisca solo un limite di approssimazione di 10.00In. Chiaramente,
considereremo ancora P’ più affidabile di P, anche se, rigorosamente, P’ non è
corretto, mentre P lo è.
Questo esempio mostra che la correttezza di un programma e la capacità di
reagire a situazioni anormale o inaspettate sono tipiche proprietà che rientrano
nella nozione di affidabilità. Non intendiamo qui approfondire ulteriormente il
concetto di affidabilità, poiché risulta caratterizzato da molti aspetti informali
propri del campo dell’ingegneria del software. Si osservi che, almeno in linea di
principio, è possibile cercare di tradurre ogni proprietà che influisce
sull’affidabilità di un sistema in un’opportuna specifica formale, formalmente
dimostrabile. Il vero problema con cui ci scontriamo è però la difficoltà di
esprimere formalmente tutte le possibili proprietà del software.
Il resto di questa appendice introduce prima alcuni semplici elementi per
l’analisi formale di programmi e poi mostra in modo più formale rispetto al caso di
studio in Sezione 7.3.3 come si possano verificare proprietà per un sistema
specificato in logica.

7.A.1 Dimostratone delle proprietà dei programmi


Nella Sezione 7.2 abbiamo visto che la logica può essere un valido strumento per
definire precisamente che cosa deve essere in grado di fare un programma. In
particolare, abbiamo introdotto il classico approccio per specificare le
caratteristiche dei programmi (o frammenti di programmi) attraverso l’uso delle
precondizioni e delle postcondizioni, cioè proprietà che indicano rispettivamente
che cosa deve essere vero prima di eseguire un programma e quali risultati il
programma deve fornire quando valgono le ipotesi stabilite nella precondizione.
La struttura generale di questo schema di ragionamento ha la forma {Pre} P {Post}
e può essere vista come una specifica per un (frammento di) programma: P deve
essere tale che se Pre vale prima della sua esecuzione, allora Post vale dopo
l’esecuzione. Obiettivo di questa sezione è dimostrare la validità di tale specifica a
partire dalla semantica del programma P, ossia la correttezza di P rispetto alla
specifica, limitandoci però solo a semplici programmi e proprietà.
Il processo che ci accingiamo a presentare è fondato su una teoria formale che
contiene assiomi e regole di inferenza. La nostra specifica si riduce, quindi, ad un
enunciato da dimostrare all’interno della teoria. Per questa ragione l’approccio
proposto è spesso chiamato assiomatico. Esso viene talvolta anche chiamato
metodo di Hoare, in onore di uno dei pionieri di tale approccio alla semantica
formale e alla verifica dei programmi.
Presentiamo ora il sistema di verifica, esprimendolo come una teoria del
prim’ordine arricchita. Si osservi che in seguito analizzeremo solo parzialmente il
problema della verifica dei programmi, limitandoci a programmi espressi in un
sottoinsieme di SMALG, un linguaggio basato su lava, la cui definizione formale e
completa verrà data nel Capitolo 9. Qui ci limiteremo a considerare il frammento
282 Informatica teorica

descritto in Figura 7.A.1, che contiene solo variabili semplici, istruzioni di


assegnamento, istruzioni condizionali e cicli. Il lettore interessato a una trattazione
completa del metodo di Hoare può fare riferimento alle note bibliografiche. Oltre a
limitare i programmi, limiteremo anche le proprietà da verificare, considerando
solo formule non quantificate.
Le fbf del sistema di Hoare sono le fbf dell’aritmetica di Peano (introdotte
nella Sezione 2.4.1) e, in più, le stringhe del tipo {Pre}P{Post}, dove Pre e Post
sono fbf dell’aritmetica del prim’ordine e vengono anche chiamate asserzioni del
programma e P è un (frammento di) programma, ossia, nel caso del frammento di
SMALG considerato qui, una stringa derivabile dai non terminali (Program),
(StatList) e (Stat) della grammatica.
Oltre ai soliti assiomi e alle solite regole di inferenza dell’aritmetica del
prim’ordine, introduciamo anche il seguente schema di assioma e le seguenti
regole.

Assioma di assegnamento
Per ogni fbf Post, per ogni termine t e variabile x derivabili dai non terminali
(Expr) e (SimpleVar) della grammatica di SMALG, la fbf
Af. {Post '} x = t {Post } è un assioma.

Post x è equivalente alla notazione Post\t/x}, utilizzata nella Sezione 2.3.1 ad
indicare l’espressione ottenuta da Post sostituendo tutte le occorrenze libere di x
con il termine t. In questo contesto l’applicazione della regola A\ per derivare
Post x da Post viene chiamata sostituzione all’indietro. Infatti, informalmente,
l’assioma di assegnamento esprime il fatto che un predicato Post è valido dopo un
assegnamento se e solo se, prima della sua esecuzione, vale il predicato ottenuto
sostituendo tutte le occorrenze della variabile a sinistra del simbolo ‘=’ (x) con il
termine (t) a destra del simbolo. Un banale esempio di questo fatto è la proprietà
{3 > 0} x = 3 {x>0}.
Le regole di inferenza sono definite mediante la classica notazione di Hoare

In :---------------------
f
che significa “l’enunciato f è diretta conseguenza degli enunciati /b f2, ...,/, in
virtù della regola In”.
A voler essere rigorsi, bisogna osservare che ogni teoria del prim’ordine non
dovrebbe possedere nessuna regola di inferenza oltre a MP e Gen. Si può tuttavia
considerare In come una riformulazione dell’assioma /j a /2 a ... a /„ => f ,
che ci consente di derivare f da f\,f2, -..,fn mediante MP. Chiameremo quindi In
regola di inferenza, per coerenza con la letteratura e con il suo significato intuitivo.
283
La logica matematica in informatica

G = (VT, VN, P, (Program))


Vt= { if, else, while, {, },

0, 1, ..., 9,!,>,<,

= { (Program, (StatList), (Stat), (AssStat), (CondStat), (WhileStat),


(Expr), (Temi), (SimpleVar), (Ident), (Const), (Digit),
(Letter), (Cond), (RelOp)}
P= { (Program) —> { (StatList) }

(StatList) —> (Stat);(StatList) | s


(Stat) —> (AssStat) | (CondStat ) | (WhileStat)
(AssStat)—> (SimpleVar) = (Expr)
(SimpleVar) —> (Ident)
(Expr) —> (Terni) + (Terni) | (Terni) - (Terni) | (Terni) * (Terni) |
(Terni) / (Terni) | (Terni) % (Terni) | (Terni)
(Terni) —> (Var) | (Const)
(Const) —> (Digit) | (Digit)(Const)

(Digit)—> 0| 1 | 2 | ... | 9

(Ident) —> (Letter) | (Letter)(Ident)

(Letter) -^a|b|...|z|A|B|...|Z

(CondStat)—> if ((Cond)) {(StatList)} else{(StatList)} |

if ((Cond)) {(StatList)}

(WhileStat)—> while ((Cond) ) {(StatList)}

(Cond) —> (Expr)(RelOp)(Expr)

(RelOp)—> = | ! = | > | >= | < | <=

Figura 7.A.1 Grammatica di un frammento del linguaggio SMALG


284 Informatica teorica

Regola di composizione
Per ogni frammento di programma Si, S2 e S = Si, S2 e per ogni formula R,Q,T
rD . {R}SfQ},{R}S2{T}
{R}S{T}

Intuitivamente, la regola di composizione significa che, se una precondizione R
garantisce la validità della postcondizione Q dopo l’esecuzione di Si e la Q,
considerata come precondizione, garantisce la validità della postcondizione T dopo
l’esecuzione di S2, allora la validità di R garantisce la validità di T dopo che sono
stati eseguiti in sequenza Si e S2.
Ad esempio, se Si è x = f (x) e S2 è x = f2 (x), l’applicazione di Ai
consente di ottenere, per ogni R
{R^x)}x = /2(x){7?}
e
{^(/‘W)}x = /1(x){^(l)}
Successivamente, applicando IRi, otteniamo
{RxhWx))}x = fl(x);x = f2(x){R}
che, intuitivamente, significa che il frammento precedente consiste nella
composizione delle due funzioni/; e f.
La prossima regola non è strettamente legata alla semantica del linguaggio,
anche se si rivela spesso utile per facilitare le dimostrazioni.

Regola di conseguenza
Per ogni frammento di programma S e per ogni formula Ri, R, Q, Qi
R^ R,{R}S{Q},Q^ Q,
1K ~ ---------------------------------------
{RiWQ,}

Ad esempio, quindi
x>3aj>>4=> x + y > 7,{x + y > 7}x = x + y{x >7}, x > 7 => x > 5

{x > 3 a y > 4}x = x + y{x > 5}

Regola if-then-else
Per ogni frammento di programma Si e S2, per ogni condizione c derivabile da
(Cond)e per ogni formula R e Q
{R/\c}Sx{Q},{R f\^c}S2{Q}
lìx. :-------------------------------------------
{À}if(c)S1else S2{Q}
La logica matematica in informatica

{R a {Q}, R a -<c => Q


{R}±f(c)Sl{Q}

Il significato intuitivo di IR2.a e IR3b è abbastanza ovvio. Quando entriamo
nell’istruzione condizionale if (c) 51 else S2, la condizione c vale oppure no.
Quindi, se vogliamo assicurarci che la precondizione R garantisca la
postcondizione Q dopo l’esecuzione, dobbiamo stabilire che, se c è valida (e quindi
viene scelto il “ramo then”), la precondizione P a c garantisce Q dopo
l’esecuzione di 5b mentre, se c non è valida (e quindi viene scelto il ramo else), la
precondizione R a -.c garantisce Q dopo l’esecuzione di S2. Poiché non sappiamo a
priori se c sia valida o meno, entrambe le alternative devono essere verificate.
La regola IR3j, può essere considerata come un caso particolare di IR3a in cui
S2 è l’istruzione nulla.
Come esempio di applicazione della regola if-then-else, si supponga di voler
dimostrare che dopo l’istruzione
if (x > y) z = x; else z = y;
l’asserzione z > y a z > x risulti sempre valida. Ciò è specificato dalla formula
{TRUE} if (x > y) z = x; else z = y; {z > y a z > x}. Questo
teorema può essere dimostrato nel seguente modo
TRUE ax > y (cioè x > y) => (x > y a x > x)
{x > y a x > x} z = x {z>yAz>x}
Quindi per IR2
{x > y} z = x {z>yAz>x}
Inoltre,
-,(x > y) (y>yAy>x)
{y>yAy>x} z = y {z>yAz>x}
Quindi per IR2
{-.(x > y)} z = y {z>yAz>x}
Infine per IR3a otteniamo il risultato desiderato.
Rimane ora da considerare la regola per gestire istruzioni cicliche.

Regola while
Per ogni frammento di programma 5, per ogni condizione c e per ogni formula I
■ {^ac}5{7}
Zzi .
{7} while (c)S{7a—.e}

Si noti che nella regola while si richiede che l’asserzione 7, se risulta vera prima
dell’ingresso nel ciclo, continui a essere vera anche dopo l’esecuzione del suo
corpo. Infatti, se vale c, viene eseguito il corpo del ciclo e {7 a c}S{I} implica la
286 Informatica teorica

validità di I anche dopo l’esecuzione di S. Quindi I risulterà sempre valida dopo


ogni iterazione del ciclo. Per questo motivo I viene chiamata invariante del ciclo.
Allo scopo di chiarire meglio il funzionamento della regola appena vista e di
considerare l’applicazione delle regole che abbiamo introdotto, presentiamo il
seguente esempio conclusivo.

Esempio 7.A.1
Si consideri il seguente programma P, scritto in SMALG, senza considerare le
istruzioni di input e output:
{
x = 1;
j = i;
while(y >= j){
j = j + i;
x = x * z;
}
}
Il programma calcola l’espressione zy, dove si suppone che i valori di z e y siano
già presenti in memoria. Un’asserzione ovvia che rappresenta la correttezza di P è:
PA: {y > 0} P {x = zy}
Per dimostrare la correttezza di P rispetto a PA dobbiamo quindi dimostrare PA,
considerandola come un teorema all’interno della teoria formale descritta
precedentemente. PA può essere dedotta dimostrando i seguenti lemmi
{y > 0} x = 1 ; j = 1 ; {1}
{1} while (y >=j){j=j + l;x = x* z ; } {I a —>(y>=j)}
{/A^(y >= j)} => {x = zy}
Tali lemmi, grazie alle regole IRi e IR2, permettono di concludere PA.
Sfruttando poi la regola IR4, ci riduciamo ai problemi
{y>0}x = l;j = 1; {1}
{ÌAy>=j} j = j + 1 ; x = x * z ; {1}
{/a—,(y>=j)} => {x = zy}
che, se dimostrati, dimostrano anche PA.
Rimane quindi il problema di identificare Tinvariante I, per la cui definizione,
purtroppo, non esiste un algoritmo, ma si può solamente sfruttare il proprio intuito
e la propria conoscenza del comportamento del programma.
Nel nostro coaso, osserviamo che per ogni iterazione del ciclo la variabile j
viene incrementata e x viene moltiplicato per z. Di conseguenza, poiché inoltre la
condizione del ciclo limita j a y, deve esistere ima relazione tra i valori di x, y e j.
Si osservi che aH’intemo del ciclo il valore di j è sempre minore o uguale al valore
di y, tranne nell’ultima iterazione in cui, incrementando j, si ottiene j = y + 1,
valore che interromperà l’esecuzione del ciclo. Quindi y > j-1 è un’invariante nel
287
La logica matematica in informatica

ciclo. Inoltre, poiché x viene moltiplicata per z ad ogni iterazione, anche x = z-'1'1 è
un’invariante del ciclo. Possiamo quindi considerareI = y > j-1 a x = zj-1.
Dobbiamo quindi dimostrare che le seguenti asserzioni sono teoremi
{y > 0} x = l;j = 1; {y > j-1 a x = zj-1}
{y> j-1 Ax = zj'1Ay>=j} j=j +1; x = x * z ; {y > j-1 a x = zj-1}
{y > j-1 a x = z-H a —i(y>=j)} => {x = zy}
Tali dimostrazioni sono facilmente ottenibili applicando la tecnica di sostituzione
all’indietro e vengono lasciate al lettore come esercizio.

L’Esempio 7.A.1, per quanto semplice, ha sottolineato un punto importantissimo
nell’applicazione del metodo di Hoare. Tale metodo richiede una forte conoscenza
del comportamento del programma in esame e abilità da parte di chi lo applica ogni
qualvolta è necessario identificare un’invariante per i cicli presenti.
Inoltre, in questa sezione ci siamo limitati non solo allo studio di programmi
semplici che non contengono variabili indicizzate, ma anche all’analisi di
asserzioni prive di quantificatori. Il metodo di Hoare propone regole che
permettono anche di gestire tali programmi e asserzioni, e questo ovviamente
complica la prova della correttezza di un programma. Nuovamente, si invita il
lettore interessato ad analizzare la bibliografia consigliata per eventuali
approfondimenti.

7.A.2 Dimostrazione di proprietà di sistemi

Nella Sezione 7.3 abbiamo visto che la logica può essere un utile strumento per
formalizzare le caratteristiche di un qualsiasi sistema, senza entrare nei dettagli del
suo comportamento. Inoltre, specificare formalmente i vincoli del sistema rende
spesso facile identificare debolezze nella descrizione che porterebbero il sistema a
comportamenti non desiderati. Infine, il caso di studio (Sezione 7.3.3) ha mostrato
che a partire da una specifica formale del sistema sotto analisi è possibile,
formalizzando in logica le proprietà che vogliamo valgano nel sistema, verificare
se tali proprietà valgono effettivamente. Intuitivamente l’idea è di mostrare che le
proprietà specificate si possono dedurre dalla specifica del sistema, cioè sono
conseguenza logica della specifica stessa.
In questa sezione vogliamo analizzare attraverso alcuni esempi come sia
possibile provare formalmente proprietà a partire da una specifica logica,
deducendo le proprietà dalla specifica stessa. Questa sezione ha solo lo scopo di
convincere maggiormente il lettore delTutilità di dedicare tempo e impegno alla
specifica formale di un sistema, mostrando che, una volta che si possiede una
descrizione formale del sistema, è possibile provarne formalmente la correttezza.
Purtroppo bisogna però osservare che il procedimento che porta alla prova di
correttezza richiede in generale molte conoscenze matematiche e molta attenzione
da parte di chi sta analizzando la specifica.
288 Informatica teorica

Iniziamo analizzando in modo più formale il caso di studio riportato nella Sezione
7.3.3.

Esempio 7.A.2
Il caso di studio affrontato nella Sezione 7.3.3 descrive le specifiche di un semplice
passaggio a livello. In tale esempio si sono identificati i seguenti assiomi (che
riportiamo in modo schematico per comodità, commenti e spiegazioni sugli
assiomi si possono trovare nella Sezione 7.3.3):
- Vincoli sugli intervalli
(II) (8 „ = d/r ) a (8. = h/ ) a (8
V / V R max //V min ' ' Imar //Vmin ' max rrV . '
min
(12) (5. . = —)a(8t . = h/ ) a (8 . = -^±A)
x / V R min ' \ Imin / JZ ' mm TZ 7
max max max
- Vincoli sul comportamento del treno
(Tl) \f t(Ar(t) => + <tl + ))

(T2) Vt(En(t) => <tt <^-5^)))


(T3) VZ(W7 3t1(W17A(Z + 5Wn <t} <t + 5^ )))
(T4) VZ(W7 3t1(^r(Z1)A(Z-5imx < ^ < r - 5 ^ )))
(T5) V t(Jn(t) 3t^{En(t x) a < t) a —.3t2 (Ex(t z) a (^ < t2 < Z))))

- Vincoli sul comportamento della sbarra


(SI) Vt(Down(t) —AJp(t) ')

(S2) \ft(Ar(t) => + 5Rmn <t1 <t + 8rmx ) => Down(t J))
(S3) \ft(Down(t) => 3/\((* — 8^ < t-8^ ) a Ar(t J))

Siamo ovviamente interessati a verificare che valga il requisito fondamentale di


sicurezza, cioè che la sbarra sia chiusa quando il treno è all’intemo dell’incrocio.
Tale requisito è formalizzato dalla seguente formula:
V t(In(t) => Down(t) )
Per prima cosa, partendo da (T3) e dalla prima parte di (T5), considerando
l’implicazione da sinistra a destra, possiamo dedurre
(Pi) <t,<t))
Infatti, se riscriviamo (T3) come
En(tx)^> Bt2(Ex(t2)A.(tl + 8]mjn < t2 < ti + 8^)))
poiché In(t) implica
V t(In(t) => 3tj (Enfi x) a (tx < t))
otteniamo t < t2 e quindi < tx.
289
La logica matematica in informatica

Poi, sfruttando (T2), osserviamo che il conseguente di (PI) implica


(P2) 3t,(Ar(t,) a (LS„ -8, <t ))
Infine, grazie alla disuguaglianza
(P3) 5 R min S.™ + 8lm(„ = 8 max
e a (S2), otteniamo la tesi.
E possibile dimostrare altre proprietà del sistema in esame, come ad esempio
che il passaggio a livello non rimane sempre - e inutilmente - chiuso. La
dimostrazione di tale proprietà viene lasciata al lettore come esercizio.

Nell’Esempio 7.A.2 abbiamo visto come si può verificare la validità di una
proprietà a partire da un modello descritto in logica del sistema in esame. Il
passaggio a livello descritto, però, era già stato analizzato anche attraverso l’uso di
reti di Petri nel Capitolo 4. Questo ci ricorda che spesso possiamo avere una
specifica del sistema che vogliamo analizzare utilizzando formalismi diversi. E
quindi naturale chiedersi, se non possa tornare utile un approccio in cui diversi
modelli vengono utilizzati in combinazione tra loro sfruttandone eventuali
complementarità.
Il metodo di Hoare opera già in questa direzione. Infatti esso utilizza un
linguaggio di programmazione (SMALG nel nostro caso) per implementare
algoritmi e la JT^JZper esprimerne proprietà. Le regole e gli assiomi utilizzati, poi,
non sono altro che una definizione formale della semantica del linguaggio usato,
chiamata assiomatizzazione. In generale, tale approccio, che utilizza un modello
operazionale per descrivere un sistema e un modello descrittivo per definirne le
proprietà, usando un’assiomatizzazione o schema di traduzione per passare da un
modello all’altro, viene chiamato, in inglese, dual language approach. Nel
prossimo e ultimo esempio, considereremo tale approccio utilizzando come
modello operazionale le reti di Petri e come modello descrittivo ■ ' cercando di
mostrare come la logica possa essere un utile strumento per dimostrare proprietà
anche in questo contesto.

Esempio 7.A.3
Consideriamo le reti di Petri come strumento per descrivere sistemi (Sezione 4.5.2)
e, in particolare, le reti di Petri temporizzate, introdotte nella Sezione 4.5.3
(Definizione 4.33), ponendo però le seguenti limitazioni:

— Ciascun posto della rete può contenere al massimo un token (la rete è 1-
bounded);

— Da ciascun posto può entrare e uscire al più un arco;

— Non esistono cicli di transizioni che abbiano come minimo tempo di scatto 0.
290 Informatica teorica

Figura 7.A.2 Rappresentazioni di frammenti di reti di Petri temporizzate

Grazie a tali semplificazioni le nostre reti di Petri contengono solo frammenti come
quelli riportati in Figura 7.A.2. Si osservi che tale semplificazione permette
comunque di descrivere il problema del passaggio a livello, affrontato
nell’Esempio 4.30.
Per poter utilizzare un approccio basato sull’integrazione con la è
necessario introdurre gli assiomi che descrivono la semantica del nostro modello
(reti di Petri temporizzate con i limiti appena descritti). Gli assiomi di interesse
sono i seguenti:
(Ist) Il predicato Fire(r, t) che descrive lo scatto della transizione r al tempo t,
descrive un evento; formalmente
\/tVr(Fire(r, Z) => (<7 > 0 a VZi(Z - <7<ZivZi<Z + <7=> -^Fire(r, Z0))
(Mar) La marcatura di un posto P (Mar(P)), che ha come unica transizione
entrante s e come unica transizione uscente v (Figura 7.A.2(a)) è definita
come segue:
^f^P(Mar(P, t)o3d (d>0 a Fire(s, t - d) a
VZi(Z - <7 < Zi < Z => —Flre(v, Zi)))
Inoltre i seguenti assiomi formalizzano le relazioni di scatto per i frammenti in
Figura 7.A.2(a) e 7.A.2(b):
(LI.a) Fire(v, Z) => VZi(Z - mv < <t^> Mar(P, Zi))
(LS.a) VZi(Z - < Zi < Z => Mar(P, Zi)) =i> Fire(y, Z)
(LLb) Fìre(v, t) => VZi(Z - < Zi < Z => Mar(P, Zi) a Mar(Q, Zi))
(LS.b) VZi(Z - Mv < Zi < Z => Mar(P, Zi) a Mar(Q, Zi)) => Fire(y, t)
Sfruttando gli assiomi appena scritti e utilizzando il medesimo schema utilizzato
nell’Esempio 7.A.2 è possibile derivare alcuni lemmi, quali ad esempio,
considerando il frammento di Figura 7.A.2(a),
La logica matematica in informatica 291

\/t\/P(Mar(P, t)=>3d(d>0 Fire(s, t - d) /\d < Mv))


che è facilmente deducibile da (Mar) e (LS.a).
Dall’assiomatizzazione prodotta è possibile derivare anche ulteriori lemmi, che
possono essere utili per dimostrare teoremi espressi in FFFS' quando si possiede
una descrizione sotto forma di rete di Petri del sistema in analisi.
Come esercizio, si invita il lettore a provare, utilizzando l’assiomatizzazione
appena proposta, la proprietà di sicurezza (provata a partire da una descrizione in
logica nell’Esempio 7.A.2) per il passaggio a livello formalizzato come rete di Petri
nell’Esempio 4.30.
292 Informatica teorica

Note bibliografiche
L’applicazione della logica matematica in informatica è un argomento molto vasto.
Panoramiche abbastanza dettagliate sull’argomento possono essere trovate in
Burris (1997), Mordechai (2003) e Hunt e Ryan (2004).
Il lettore interessato poi ad analizzare logiche non trattate in questo libro può
dedicarsi allo studio della logica descrittiva, spesso usata in intelligenza artificiale,
e della logica modale e temporale, utilizzata generalmente in ingegneria del
software per specificare proprietà di sistemi. Baader et al. (2003) raccolgono i
contributi più significativi sulle logiche descrittive e sulle loro applicazioni.
Emerson (1990) propone una dettagliata introduzione della logica modale e in
particolare della logica temporale. Un altro possibile approfondimento
sull’argomento è proposto in Venema (2001).
Il lettore interessato ad analizzare tecniche di verifica per specifiche descritte
in logica matematica può concentrarsi sullo studio delle tecniche per le
dimostrazioni di teoremi (theorem proving), riferendosi, ad esempio, a Duffy
(1991), e su tecniche di verifica completamente automatiche, come il model
checking, di cui il libro Clarke et al. (1999) dà una descrizione esaustiva. Per una
lettura più concisa il lettore può riferirsi a Peled et al. (2009). Esistono molte altre
tecniche per l’analisi di specifiche e non è tra gli obiettivi di questo testo fornire
una bibliografìa completa.
Lo studio dell’analisi di precondizioni e postcondizioni può essere
approfondito analizzando le tecniche per verificare le postcondizioni a partire dalle
precondizioni, Hoare (1969), oppure studiando linguaggi ad hoc per particolari
linguaggi di programmazione, come, ad esempio, JML (Java Modeling Language)
per Java.
Il caso di studio del passaggio a livello è stato molto usato in letteratura come
“benchmark” per valutare formalismi adatti alla specifica, analisi e verifica di
sistemi critici in tempo reale. Heitmeyer e Mandrioli ( 1996) presenta una raccolta
di contributi di diversi autori che si “cimentano” con questo benchmark a diversi
livelli di approfondimento e sfruttando diversi formalismi. Infine si possono
approfondire le applicazioni a casi reali, ad esempio in Abrial et al. (1995).
Capitolo 8

LA RISOLUZIONE AUTOMATICA DEI PROBLEMI

Dopo aver esaminato nella Parte 2 di questo testo un significativo “campione” di


modelli atti a descrivere problemi di elaborazione delle informazioni e la loro
soluzione e dopo avere imparato a studiarne capacità e limiti dedichiamo questo
capitolo a studiare in modo sistematico quali problemi possano essere affrontati e
risolti mediante “macchine da calcolo”. Per inquadrare l’obiettivo nella sua
generalità iniziamo con riassumere alcune conclusioni derivanti da quanto abbiamo
appreso finora.
1. Automi e grammatiche, pur essendo modelli matematici, si possono
considerare dispositivi “meccanici” per la risoluzione di problemi. Spesso,
infatti, un problema matematico costituisce la formalizzazione di qualche
problema pratico non matematico. Un esempio tipico è la verifica
dell’appartenenza o meno di una stringa ad un determinato linguaggio. Questo
problema, prettamente matematico, non è altro che una riformulazione
formale di problemi pratici quali il riconoscimento di un comando in un
linguaggio di controllo per sistemi operativi oppure il riconoscimento delle
istruzioni nei programmi scritti in un determinato linguaggio di
programmazione oppure ancora l’individuazione di un virus in un messaggio
ricevuto tramite Internet ecc.
2. Alcuni formalismi sono più potenti di altri, ossia sono in grado di riconoscere
(o generare, o comunque definire) alcuni linguaggi che altri formalismi non
possono accettare, oppure riescono ad implementare traduzioni non
effettuabili da un altro formalismo ecc. Ad esempio, gli AP sono più potenti
degli AF e il non determinismo aggiunge potere espressivo agli AP.
3. Nessun formalismo, fra quelli considerati nella Parte 2, è più potente delle
MT, sia dal punto di vista del riconoscimento e traduzione di linguaggi, che da
quello della valutazione di funzioni. Alcuni formalismi, ad esempio gli automi
a pila doppia proposti nell’Esercizio 4.77, oppure le grammatiche non ristrette
hanno però la medesima potenza delle MT. Tali formalismi verranno d’ora in
poi chiamati formalismi massimi.
Le importanti nozioni richiamate sopra fanno sorgere spontaneamente alcune
domande.
a. I formalismi esaminati finora sono effettivamente adatti a rappresentare un
risolutore meccanico di problemi? Infatti, anche restringendo l’attenzione ai
problemi ben formalizzati, vi sono molti problemi che non sembrano rientrare
nella classe degli esempi precedenti. Alcuni esempi sono la risoluzione di
un’equazione differenziale, la ricerca del cammino minimo in un grafo,
l’aggiornamento di una base di dati per mezzo di una transazione, la
Informatica teorica

determinazione dell’allocazione ottima delle merci fra vari magazzini etc.


Apparentemente tutti questi problemi non hanno nulla a che vedere con
l’appartenenza di una parola ad un linguaggio.
b. La capacità di risolvere meccanicamente i problemi dipende dal modo in cui il
problema viene formalizzato? Questo problema è già stato affrontato almeno
una volta, nel corso del testo, e la risposta alla domanda è stata affermativa. Si
ricordi, ad esempio, che un TF è in grado di calcolare il successore di un
numero naturale quando quest’ultimo risulta codificato in base unaria, ma non
quando la codifica è in una base k, con k > 1 (si veda il Teorema 4.20).
c. Esistono formalismi di calcolo più potenti delle MT? Ad esempio, può esistere
(o potrà esistere nel futuro) un supercalcolatore in grado di effettuare quelle
elaborazioni che risultano impossibili per le MT?
d. Una volta che un problema è stato opportunamente formalizzato, è sempre
possibile risolverlo mediante una procedura meccanica?
Questo capitolo è dedicato all’analisi dei problemi appena esposti. In primo luogo
si illustrerà come gli strumenti formali attualmente disponibili siano adeguati a
descrivere ogni particolare problema da risolvere meccanicamente.
Successivamente si mostrerà come le MT e i formalismi equivalenti consentano di
affermare proprietà generali sulla potenza di un qualunque dispositivo di calcolo,
teorico o pratico. Infine si studieranno i limiti della risoluzione meccanica dei
problemi.

8.1 Formalizzazione del concetto di problema


Abbiamo già notato che molti problemi si possono opportunamente descrivere
come riconoscimento di linguaggi o come traduzione da un linguaggio ad un altro
o come calcolo di una determinata funzione. Osserviamo ora come ogni problema
matematicamente formalizzato sia descrivibile in una di queste forme, alla sola
condizione che il dominio del problema sia un insieme numerabile. In tal caso,
infatti, i suoi elementi si possono porre in corrispondenza biiettiva con elementi di
N, o di V*, dove V è un determinato alfabeto. Di conseguenza, il problema
originale si può riformulare come il problema del calcolo di una funzione
fi. N -> N, come una traduzione o come un riconoscimento di linguaggio in V*.
Ad esempio, la ricerca delle soluzioni di un sistema di equazioni del tipo
fljXl + <22X2 = Ci

b{x{ + Z)2X2 = C2

si può descrivere come il calcolo della funzione f da Z6 a Q2, dove Z indica


l’insieme dei numeri interi e Q l’insieme dei numeri razionali, definita come
fia{, a2, bi, b2, ci, c2) = (xi, x2)
dove x15 x2, se esistono, sono i numeri razionali che risolvono il sistema precedente.
La risoluzione automatica dei problemi 297

Notiamo poi che è possibile far corrispondere biiettivamente Z, e quindi Z6, con
N. Lo stesso vale per Q2, poiché ogni numero razionale si può descrivere come
coppia di interi: per ogni r e Q, r = m / n, per qualche m,n e Z. In questo modo il
problema precedente viene ridotto al problema del calcolo di un’opportuna
funzione/ N N.

ESERCIZI

8.1 Si descrivano i seguenti problemi come funzioni da N a N.


1. Trovare il massimo fra una sequenza di numeri interi.
2. Trovare, se esiste, il cammino che unisce due nodi di un grafo dato.
3. Riordinare un vettore di n interi.
4. Riordinare una sequenza di un qualunque numero di interi.
8.2 Si descrivano i seguenti problemi come problemi di riconoscimento di
linguaggi.
1. Stabilire se una determinata formula del calcolo proposizionale è una
tautologia.
2. Stabilire se un grafo è ciclico.
3. Stabilire se tre punti di un piano cartesiano giacciono sulla medesima
retta.

Bisogna a questo punto fare un’importante precisazione. Tutti i formalismi


matematici esaminati finora sono discreti; più precisamente, essi sono dotati di
domini matematici numerabili, definiti in modo finito. Si potrebbero trarre molte
considerazioni da questa osservazione; per il momento, notiamo soltanto che essa è
perfettamente in accordo con la tecnologia digitale dei moderni calcolatori. Si
ricordi inoltre che i formalismi continui, come i numeri reali, seguono storicamente
(e sono basati su) i numeri naturali.
Si potrebbe addirittura affermare che il pensiero umano e il comportamento
delle macchine inventate dagli esseri umani sono essenzialmente basati su modelli
discreti: il concetto di modelli “continui” è solo una potente astrazione: per poterla
trattare mediante strumenti di calcolo automatico, ad esempio per il calcolo
numerico della soluzione di un’equazione differenziale, occorre sempre
un’approssimazione discreta; più precisamente, i numeri reali o complessi che
garantiscono l’eventuale esistenza e unicità della soluzione di certe equazioni
devono essere approssimati mediante opportuni numeri razionali, che costituiscono
un insieme denso anche se non discreto (si veda il Capitolo 1); essi a loro volta
però sono messi in corrispondenza biunivoca con i numeri interi che essendo
discreti possono essere rappresentati mediante un’opportuna sequenza di bit, o di
caratteri appartenenti ad un alfabeto finito. Si può quindi concludere che ogni
problema può essere sempre codificato mediante qualcuno degli schemi precedenti.
298
Informatica teorica

Nella Sezione 4.3.3 è stato anche osservato che la traduzione di linguaggi e il


calcolo di funzioni sono facilmente riducibili l’uno all’altro. Anche il
riconoscimento di un linguaggio si può vedere come un problema di traduzione, e
viceversa. Infatti, il problema di stabilire se, per una data stringa x e un dato
linguaggio L, x g L si può anche impostare come la traduzione rL, dove rz(x) = 1 se
x g L, rL(x) = 0 se x g L. Viceversa, data la traduzione r: Fi* V2* si può
definire il linguaggio
Lt = {z | z = x$y, con x g Fi*, y g V2*, $ g (Fi u F2) e y = r(x)}

cioè il linguaggio delle stringhe formate da una stringa su V\*, seguita dalla sua
traduzione; la stringa e la sua traduzione sono separate dal simbolo speciale “$”.
Un dispositivo che riconosce ZT può essere usato come trasduttore che calcola
r; per ogni x, infatti, è possibile enumerare tutte le y g V2* e verificare se x$y g
o no: se ciò avviene g Zt), si può concludere che la stringa y è la traduzione
rispetto a r della stringa x, cioè r(x) = y. Si noti che questo processo ha termine se e
solo se t(x) risulta definita. Infatti, qualora per x non fosse definita una traduzione,
si proseguirebbe nell’enumerazione delle je V2*. senza mai trovare una stringa x$y
che appartiene a ZT.

ESERCIZI

8.3 II precedente processo di rappresentazione delle traduzioni come


accettazioni di linguaggi risulta facilmente implementabile dalle MT che
decidono L-t, cioè da quelle macchine che terminano sempre le loro
computazioni e forniscono sempre una risposta affermativa o negativa alla
domanda: “z appartiene a ZT?”. Si mostri che ciò può essere esteso alle MT
che sono in grado solo di accettare LT.
Avvertimento: questo esercizio può risultare difficile, per il momento. Se non
si è in grado di risolverlo ora, lo si rimandi a dopo la lettura della Sezione
8.8.
8.4 Si mostri che, se esiste una MT in grado di calcolare tl secondo la
definizione precedente, allora L può essere deciso. Se invece rL risulta
definita solo come rL(x) = 1 se e solo se x g L, lasciando aperta la
possibilità che rL sia indefinito per x g L, allora L può essere solo accettato.

Come annotazione finale, ricordando il Teorema 4.19, che dimostra che ogni MT è
equivalente ad un’opportuna MT avente un alfabeto con due soli simboli, si può
affermare che, diversamente dagli altri automi, la classe di problemi risolvibili
dalle MT risulta indipendente dall’alfabeto scelto per la rappresentazione, purché
contenga almeno due elementi.
Tutta la discussione svolta finora conduce ad una importante conclusione: le
MT, e i formalismi ad esse equivalenti, sono i risolutori meccanici di problemi più
La risoluzione automatica dei problemi 299

potenti fra quelli incontrati finora, indipendentemente dalla codifica del problema.
Questo fatto consente di concentrare l’attenzione, d’ora in poi, sulle MT intese
come risolutori di problemi, senza preoccuparsi della formalizzazione scelta per la
nozione di problema.

ESERCIZI

8.5 Si considerino le grammatiche come funzioni che calcolano formalismi, in


base alla seguente convenzione:
fi N -> Nè “calcolata” da una grammatica G se e solo se Z(G) = {cTtf^
| n g N}.
Si dimostri che
a. Una grammatica non contestuale può calcolare fin) = In.
b.* Una grammatica non contestuale non può calcolare fin) = tifi
c. Una grammatica non ristretta può calcolare ogni funzione calcolata da
una determinata MT.
8.6 * Si cambi la convenzione dell’Esercizio 8.5 nella seguente: fi. N N è
calcolata da G se e solo se L(G) = {x$y | x = codifica decimale di n,
y = codifica decimale diyfx)}. Si dimostri che la funzione identità non può
essere calcolata da grammatiche non contestuali.
8.7 Si dimostri che se una grammatica non contestuale può calcolare fin)
secondo la convenzione dell’Esercizio 8.5, allora una MT può calcolare fin)
secondo la convenzione dell’Esercizio 8.6.
8.8 Si mostri che le MT possono trasformare i numeri naturali codificati in base
unaria in numeri codificati in una qualsiasi base k > 1 e viceversa, mentre i
TF non possono.
8.9 Si risolva l'Esercizio 8.8 considerando i TP al posto dei TF.

8.2 Macchine di Turing, linguaggi di programmazione e tesi di Church (prima


parte)
Finora le MT e gli altri automi sono stati considerati come dispositivi di calcolo
astratti. Apparentemente, i veri calcolatori e i loro linguaggi di programmazione
non assomigliano molto ad automi astratti. Basta però qualche semplice
considerazione per rendersi conto di come, ad esempio, l’esecuzione di un qualsiasi
programma di calcolatore possa essere effettuata da un’opportuna MT. E
sufficiente infatti organizzare i nastri della MT al fine di memorizzare i dati su cui
il programma opera e simulare l’esecuzione di ogni istruzione Pascal attraverso una
sequenza di mosse della MT. Viceversa, è un semplice esercizio di
programmazione la costruzione di un programma che simuli una data MT, a patto
di poter ignorare le dimensioni finite della memoria della macchina su cui si
300
Informatica teorica

esegue il programma. Questi concetti si possono riassumere con i seguenti


enunciati.
Enunciato 8.1
Data una MT M è possibile costruire un programma Pascal (o un programma
FORTRAN o un programma C, Java ecc.) che simuli M, purché il calcolatore che
effettivamente esegue il programma disponga di una quantità di memoria
sufficientemente grande da non causare alcun overflow durante l’esecuzione.

Enunciato 8.2
Dato un programma Pascal (o FORTRAN, o C ecc.) è possibile costruire una MT
che calcoli la stessa funzione calcolata dal programma.

I dettagli della dimostrazione sono lasciati al lettore per esercizio.
In conclusione, si è dunque scoperto che le MT hanno la stessa potenza di
calcolo non solo delle grammatiche e delle funzioni ricorsive parziali, ma anche dei
più familiari programmi sviluppabili nei consueti linguaggi di programmazione ad
alto livello. È perfino possibile formulare il seguente enunciato, più forte e più
generale.
Enunciato 8.3
Tutti i formalismi noti per modellare dispositivi di calcolo discreti hanno, al più, la
medesima potenza delle MT.

L’enunciato precedente, insieme ad una profonda analisi delle capacità delle MT e
della computazione meccanica in generale, ha condotto Alonso Church a formulare
la sua tesi, che costituisce una pietra miliare della teoria della computazione.
Tesi di Church (prima parte)
Non esiste alcun formalismo, per modellare una determinata computazione
meccanica, che sia più potente delle MT e dei formalismi ad esse equivalenti1il
.

1 Si ricordi che in questo contesto per “potenza di calcolo” intendiamo la classe di problemi
risolvibile mediante un certo formalismo, ma non facciamo riferimento allo “sforzo”
impegato dalla macchina astratta per il calcolo della soluzione; abbiamo già notato ad
esempio che una MT a nastro singolo può aver bisogno di un notevole numero di mosse per
simulare una sola mossa della macchina a k nastri; ancor più significativa è la differenza tra
il meccanismo di scansione della memoria rigorosamente sequenziale tipica delle varie
famiglie di automi esaminate nel Capitolo 4 e l’accesso diretto alle celle di memoria che
caratterizza le architetture di calcolo che fanno riferimento al classico schema di von
Neumann. Queste osservazioni saranno approfonditamente riprese nel Capitolo 9 dedicato
appunto al “costo” per il calcolo della soluzione meccanica dei problemi.
La risoluzione automatica dei problemi 301

La tesi di Church è intrinsecamente non dimostrabile, poiché è basata sulla nozione


intuitiva di “calcolo meccanico”: essa è sostenuta solo dall’esperienza precedente e
dall’evidenza intuitiva e, in linea di principio, andrebbe verificata nuovamente ogni
volta che viene introdotto un nuovo modello di calcolo.
In base a tale enunciato, poiché non esistono formalismi più potenti delle MT,
possiamo dire che, se si riesce a dimostrare che un problema può essere risolto da
una MT, si può allora dedurre che il medesimo problema può essere risolto da un
qualunque modello matematico di calcolo, esistente o anche solo immaginabile,
con la stessa potenza delle MT. Viceversa, se si dimostra che un problema non può
essere risolto da una MT si può allora dedurre che il medesimo problema non può
essere risolto da un qualunque altro modello matematico di calcolo. La lezione è
quindi che è inutile cercare di risolvere meccanicamente ciò che non può essere
risolto daunaMT!

8.3 Algoritmi e tesi di Church (seconda parte)


Il concetto di “algoritmo” è probabilmente il concetto fondamentale
dell’informatica. Intuitivamente, per algoritmo si intende la procedura di
risoluzione di un problema mediante un dispositivo automatico di calcolo, come ad
esempio un calcolatore digitale. Gli algoritmi si possono anche intendere come un
metodo astratto di descrizione dei programmi, scritti in linguaggio assemblatore o
di alto livello, eseguibili da un calcolare: un tale programma è una sequenza di
comandi che, una volta eseguita dal calcolatore, può risolvere un determinato
problema.
Questi comandi dipendono dalla scelta del particolare calcolatore e del
linguaggio ad alto livello. Di conseguenza, i comandi per calcolare -Jx2 + y2 con
un errore minore di 0,01 per mezzo di un mainframe degli anni 60 differiranno
probabilmente da quelli che calcolano il medesimo risultato su di un personal
computer moderno.
Analogamente, un programma FORTRAN o lava per l’ordinamento di un
vettore è diverso da un programma Pascal che svolge la medesima funzione. E
intuitivamente evidente che programmi diversi, codificati in linguaggi diversi,
possono effettivamente implementare la “medesima soluzione”, indipendentemente
dalle differenze di codifica. Questa è per esempio la situazione che si verifica con i
tre programmi dati in Figura 8.1b, 8.le e 8.1d, scritti rispettivamente in Pascal
(P1SORT), in FORTRAN (P2SORT) e in lava (P3SORT): ognuno di essi codifica
l’algoritmo informalmente specificato mediante il linguaggio naturale in Figura
8.la.
I vantaggi derivanti dalla descrizione delle soluzioni dei problemi astraendo
dai dettagli specifici di un particolare linguaggio di programmazione (si veda, in
particolare, il caso di P2SORT) dovrebbero apparire evidenti, specialmente dal
punto di vista della leggibilità e della comprensibilità. Intuitivamente, è abbastanza
evidente che tutti e tre i programmi, P1SORT, P2SORT e P3SORT, implementano

I
302
Informatica teorica

il medesimo algoritmo, ma, a causa delle particolari rigidità dei diversi linguaggi,
nessuno dei tre implementa completamente la filosofia dell’algoritmo descritto in
Figura 8.1.a, come appare chiaro soprattutto nel caso di P2SORT.

ALGORITMO “ORDINAMENTO PER INSERZIONE DIRETTA”

Passo 1 : Si legge il vettore a, da ordinare (n è il numero dei suoi elementi)


Passo 2: Sia i una variabile che assume tutti i valori compresi fra 2 ed n. Per
ciascuno di questi valori si ripeta la seguente procedura.
2.1 Si spezzi a in due sottosequenze Si = ai, ...alA e s2 = ah ...an in
modo tale che Sj sia sempre ordinato (si noti che al passo iniziale ciò
è ovviamente vero in quanto i = 2 e quindi s, contiene un solo
elemento).
2.2 Si inserisca a, nel suo posto opportuno all’interno di sj. Ciò si
effettua mediante:
2.2.1 Memorizzazione di a, nella variabile temporanea x.
2.2.2 Scorrimento di tutti gli elementi a, maggiori di x, verso
destra, di una posizione, in modo tale da liberare un
elemento del vettore in cui si inserisce successivamente il
valore memorizzato in x.
Quest’ultimo passo si può specificare con maggior dettaglio nel modo seguente:
2.2.2.1 Si memorizzi temporaneamente x in a0.
2.2.2.2 Siaj una variabile corrente che parte daj = z - 1.
2.2.2.3 Si confronti x con aj e, se x > a,, si faccia scorrere a,
di una posizione verso destra, ossia si assegni a, a
fly+y. Si decrementi j di 1. •
2.2.2.4 Si ripeta il passo 2.2.2.3 fino a che x > aj. (Si noti
che il passo 2.2.2.3 verrà ripetuto al più i volte,
poiché x = a0).
2.22.5 Si inserisca x nella posizione j + 1.

(a)
La risoluzione automatica dei problemi 303

Program PI SOFT (input, output) {


constn =20;
vai ì,j,x'. integer,
a: array[0.. m] offrite ger;
begin
for i:=l to n do
reaàpt[i]),
for i:=2 to n do
begin
r:=a[4; o[0[]:=rj :=/-?;
while x < a[/] do
begin
a[/+lW]j:=?l;
end;
a|>l]:=r;
end:
for r=l to n do
wrjte(dr[j]);
end Pi SOFT
}

(b)
C P2SORT
PARAMETER (N = 20)
INTEGER X
INTEGER A(21)
DO 73 1 = 2, N + 1
READ(5,75)A(I)
73 CONTINUE
DO 77 1 = 3, N+ 1
X = A(I)
A(1) = X
J 0 1-1
78 IF(X.GE.A(J)) GO TO 79
A(J+1)=X
J = J-l
GO TO78
79 A(J+1)=X
77 CONTINUE
75 FORMAT(I5)
DO 74 J = 2,N+ 1
WRITE(6,75)AtJ)
74 CONTINUE
STOP
END
(c)
304 Informatica teorica

void P1SORT () throws NumberFormatException, IOException{


final int n = 20;
int a[] = new int[n]; ■
BufferedReader in =
new BufferedReader(new InputStreamReader (System.in)) ;
fot (int i=l; i<n; i++) a[i] = new Integer(in.readLine());
int temp, j;
fon (int i=2; i<n; i++){
t emp = a[i];
a[0] = temp; .
j = i-1; ,
while (temp < a[j]){
a [j+1] = a[j] ;
j—;
}
a[j+1] = temp;
}
fot (int i=l; i<n;i++)
System.out.printIn(a[i]) ;
}

(d)

Figura 8.1 Un algoritmo per l’ordinamento di vettori, (a) versione descritta in


linguaggio naturale; (b) versione codificata in Pascal; (c) versione codificata in
FORTRAN; (d) versione codificata in Java.
In conclusione, un algoritmo rappresenta la sequenza astratta di operazioni
elementari che risolvono un problema in modo meccanico, indipendentemente dal
particolare processore scelto, ossia dal particolare linguaggio di codifica.
Il concetto di algoritmo è però essenzialmente informale e ogni tentativo di
descriverlo formalmente sfocerebbe nella definizione di un nuovo meccanismo di
calcolo, astratto (matematico) o concreto, e del relativo linguaggio. I linguaggi
degli AF, degli AP e delle MT, nonché i linguaggi di alto livello, forniscono
ognuno una notazione diversa per tradurre, in codici formalmente definiti ed
eseguibili, gli algoritmi descritti in modo informale. La differenza principale fra gli
automi e i linguaggi di programmazione è che gli automi, nella loro essenziale
semplicità, sono più adatti alla dimostrazione matematica di proprietà mentre i
linguaggi di programmazione per calcolatore risultano più utili per realizzare
implementazioni efficienti e complesse.
Anche se il concetto generale di algoritmo è necessariamente informale, esso è
caratterizzato da diverse proprietà, che, pur definite a loro volta informalmente, ma
in modo preciso, ne individuano approfonditamente il significato:
1. Un algoritmo deve contenere una sequenza finita di istruzioni.
2. Ogni istruzione deve essere immediatamente eseguibile da qualche
procedimento meccanico di calcolo (processore), ossia deve esistere un agente
305
La risoluzione automatica dei problemi

che sia in grado di comprendere univocamente le istruzioni e di eseguirle


producendo risultati precisi e prevedibili.
3. Il processore è dotato di dispositivi di memoria in cui possono essere
immagazzinati i risultati intermedi.
4. La computazione è discreta, ossia 1’informazione è codificata in forma digitale
e la computazione procede attraverso passi discreti.
Le quattro precedenti caratteristiche corrispondono alla comune esperienza con i
moderni calcolatori digitali. Ulteriori ipotesi generali sugli algoritmi sono:
5. Gli algoritmi vengono eseguiti deterministicamente. Per quanto concerne gli
scopi di questo capitolo, i modelli di calcolo non deterministico e stocastico,
discussi nelle Sezioni 4.4 e 4.6, non vengono considerati per almeno due
motivi. Prima di tutto l’esperienza quotidiana mostra che, in pratica, i calcoli
meccanici vengono eseguiti deterministicamente. Poi si è già appreso che la
potenza delle MT non si può ulteriormente aumentare con l’introduzione del
non determinismo (si veda il Teorema 4.23).
6. Non esiste un limite finito sui dati di ingresso e di uscita. Ciò corrisponde al
fatto che un computer può ricevere in ingresso o emettere in uscita sequenze
di caratteri di lunghezza arbitraria, attraverso un terminale.
Corrispondentemente, tutti gli automi astratti hanno nastri di ingresso e di
uscita di lunghezza illimitata.
7. Non c’è limite alla quantità di memoria richiesta per effettuare i calcoli.
Questo è un punto cruciale: è noto che ogni calcolatore reale è dotato di un
numero limitato di registri di lunghezza finita e di una memoria di lavoro
limitata, per quanto grande possa essere. Anche se si aggiunge una memoria di
massa, la quantità totale di memoria disponibile non potrà mai essere infinita.
D’altra parte si è visto, nella Sezione 4.2.3, che per riconoscere il semplice
linguaggio {anbn \n> 1} la memoria limitata di un automa a stati finiti non è
sufficiente. La massima potenza di calcolo delle MT viene ottenuta grazie alla
loro memoria illimitata. Questa osservazione non deve però portare a
concludere che gli automi a stati finiti siano il solo modello computazionale
realistico. Infatti, fino a che i dispositivi di memoria non sono saturati, essi si
possono considerare come illimitati. Quindi, se la memoria principale di un
calcolatore non è mai riempita completamente durante un calcolo, si può
supporre che sia illimitata rispetto a quel calcolo. Il problema è che non
sempre risulta possibile prevedere la quantità di memoria necessaria per
effettuare un calcolo qualsiasi: per questa ragione, tutti i calcolatori reali sono
dotati di meccanismi di overflow. Tali meccanismi avvertono l’utente che, in
una determinata circostanza, il calcolo effettuato da un calcolatore non
corrisponde al calcolo ideale effettuabile da un automa astratto che modella il
calcolatore reale senza limiti di memoria.
Sebbene si possa supporre che risulti sempre possibile implementare
qualunque automa astratto, comprese le MT, aggiungendo nuova memoria
306 Informatica teorica

quando il calcolo lo richieda, si vedrà nel prossimo capitolo che vi sono molte
altre ragioni per cui parecchi calcoli che possono essere effettuati da un
automa astratto non possono venire implementati in pratica.
8. Non esiste limite al numero di passi discreti richiesti per effettuare un calcolo
e possono quindi aversi computazioni infinite2. Ciò si accorda perfettamente
con il fatto che le computazioni delle MT possono anche non fermarsi mai:
questo aspetto cruciale della teoria della computazione sarà approfondito nella
Sezione 8.6.

ESERCIZIO
8. 10 Si scriva un programma per calcolatore che simuli un AP che riconosce il
linguaggio L = {anbn\ n > 1}. Il programma deve anche riconoscere quando
non è in grado di simulare propriamente l’automa e avvertire l’utente in
corrispondenza di ciò.

Analizzato il significato del concetto di algoritmo, il suo ruolo nell’informatica e le


sue proprietà, si può ora riprendere in considerazione la tesi di Church da un punto
di vista più completo: la sua importanza non risiede solo nell’affermazione che tutti
i formalismi esistenti non sono più potenti delle MT, ma anche nell’affermazione
che nessun algoritmo è in grado di risolvere un problema che non può essere risolto
da una MT. E evidente, infatti, che se si riesce a descrivere un algoritmo per
risolvere un problema in modo preciso, seppur informale, allora si dovrebbe essere
in grado anche di progettare una MT che risolva lo stesso problema,
indipendentemente dalla lunghezza e tediosità di un simile procedimento. Si può
perciò enunciare la tesi di Church in una veste più generale.
Tesi di Church (seconda parte)
Ogni algoritmo per la soluzione automatica di un problema può essere codificato in
termini di una MT (o di un formalismo equivalente).

Nel seguito, quindi, i termini “algoritmico”, “meccanico”, “effettivo” verranno
usati come attributi tra loro sinonimi di un procedimento di calcolo. “Computabile”
e “risolvibile” saranno attributi di un problema per il quale esiste un algoritmo
risolutivo. Possiamo quindi formalizzare questi termini attraverso la seguente
definizione.

2 Altri autori impongono il requisito che gli algoritmi terminino dopo un numero finito di
passi discreti, qualunque sia il valore dei dati di ingresso ma ciò, come vedremo, limita le
capacità di calcolo dei formalismi.
307
La risoluzione automatica dei problemi

Definizione 8.1
Ogni funzione (o, equivalentemente, problema) per cui esiste una MT che la calcoli
(risolva) si dice computabile o calcolabile (o risolvibile). Un problema risolvibile
la cui risposta è booleana ed esiste per ogni valore del dominio di definizione (ossia
è formalizzato da una funzione calcolabile e totale) si dice decidibile.

La seconda formulazione della tesi di Church permette di affrontare la domanda d


della Sezione 8.1, cioè se una volta che un problema sia stato opportunamente
formalizzato, sia sempre possibile risolverlo mediante una procedura meccanica, in
piena generalità. Infatti, grazie alla seconda parte della tesi di Church, possiamo
affermare che è possibile studiare i limiti del calcolo automatico
indipendentemente dalla formalizzazione del problema e dal particolare modello
computazionale. Di questo argomento si occupa principalmente il resto di questo
capitolo.

8.4 Enumerazione delle macchine di Turing e macchine di Turing universali


Finora le MT sono state studiate in qualità di dispositivi che sono in grado di
risolvere un particolare problema. Il problema specifico che una MT può risolvere
è definito dalla funzione di transizione S e fa quindi parte della struttura della
macchina. Per questo motivo, le macchine di Turing si possono considerare come
calcolatori astratti, specializzati e non programmabili. Una volta caricato il
programma nella memoria di sola lettura (ROM) del calcolatore dedicato, la
macchina può eseguire solo quel programma.
Sorge a questo punto spontaneamente la seguente domanda: si può modellare
un calcolatore con una MT? Ossia, le MT possono modellare dispositivi generali di
risoluzione di problemi, in cui il problema da risolvere non sia codificato nella
struttura del dispositivo ma gli venga piuttosto fornito come ingresso?
Successivamente affronteremo la domanda fondamentale: “le MT possono
calcolare tutte le funzioni da N a N?” Poiché, come constateremo, la risposta è
negativa, si analizzerà il concetto di “problema irrisolvibile” nel senso specificato
nella sezione precedente, ossia di problema per cui non esiste una MT che lo
risolve, non per cui non esiste soluzione o non la si possa individuare in assoluto,
ma, in base alla tesi di Church, non sia possibile ricavarla mediante procedimento
algoritmico; sulla base di questa fondamentale risposta negativa introdurremo
successivamente altre importanti nozioni della teoria della computazione, ossia
delle potenzialità e limiti del calcolo automatico.
Per approfondire questi argomenti fondamentali, sono necessari alcuni
importanti risultati preliminari, primo fra cui l’enumerazione delle macchine di
Turing. La Sezione 8.4.1 è dedicata a questo punto. La Sezione 8.4.2 discute poi il
concetto di macchina di Turing universale, rispondendo alla precedente domanda
sulla possibilità di modellare uno strumento di calcolo “generai purpose” come i
Informatica teorica

normali calcolatori a programma memorizzato, mentre la successiva Sezione 8.5,


affronta l’argomento dei problemi irrisolvibili.

8.4.1 Enumerazione delle macchine di Turing


Dato un qualsiasi insieme S, S può essere enumerato algoritmicamente se è
possibile stabilire una biiezione fra S e l’insieme dei numeri naturali N, calcolabile
attraverso un algoritmo, o, equivalentemente, tramite una MT. Vediamo un
esempio di enumerazione algoritmica.

Esempio 8.1
Si consideri l’insieme {a, b}* delle stringhe sull’alfabeto {a, b}. Tale insieme è
algoritmicamente enumerabile. Infatti è possibile associare ad ogni numero
naturale una stringa su {a, b} e viceversa, in modo algoritmico.
Un semplice modo per ottenere lo scopo consiste nell’ordinare le stringhe per
lunghezza crescente e, a parità di lunghezza, assegnare un ordine tra gli elementi
dell’alfabeto su cui sono definite. Perciò, se poniamo a < b, otterremo il seguente
ordine tra le stringhe di {a, b}*: e < a < b < aa < ab < ba < bb < aaa < aab < aba <
abb < baa < ... In questo modo possiamo associare ad ogni stringa il numero
naturale che corrisponde alla sua posizione nell’ordinamento. Simmetricamente,
dato un numero naturale, possiamo ritrovare la stringa che corrisponde a quella
posizione. Per cui la biiezione sarà definita come segue: e <-> 0, a 1, b <-> 2, aa
<-> 3, ab <->4, ...
A questo punto è un facile esercizio di programmazione scrivere un
programma che, ricevendo in ingresso una stringa su {a, b}* ne calcoli il
corrispondente numero nell’ordinamento così definito e viceversa; sulla base della
tesi di Church questo algoritmo può poi essere “codificato” nel linguaggio di
qualsiasi macchina astratta che sia equipotente alle MT.

Analogamente a quanto fatto per l’insieme di stringhe {a, b}* nell’Esempio 8.1, è
possibile enumerare algoritmicamente T insieme delle MT. Per semplicità fissiamo
un alfabeto A e consideriamo l’insieme {MT^} delle MT a nastro singolo e senza
stati finali, aventi A come insieme di simboli. Le MT in {MT^} accettano una
stringa se e solo se arrivano in uno stato di halt e compiono infinite mosse
altrimenti.
Si osservi che le semplificazioni precedenti, cioè aver fissato l’alfabeto A,
aver considerato solo le macchine a nastro singolo e aver cambiato la condizione di
accettazione senza considerare gli stati finali, semplificano solamente la notazione,
ma non costituiscono una perdita di generalità in quanto un procedimento analogo
può essere applicato anche su altri tipi di MT; inoltre, qualsiasi problema e
qualsiasi algoritmo per la sua soluzione, se esiste, possono essere descritti mediante
un alfabeto fissato di almeno due caratteri e una MT a nastro singolo con la
condizione di accettazione descritta precedentemente.
La risoluzione automatica dei problemi 309

Mostriamo quindi come {MT^} possa essere enumerato, ossia come si possa
stabilire una biiezione effettiva «P: M <-> {MT4}. Osserviamo in primo luogo che
per ogni intero k, esiste un numero finito di MT, aventi A come insieme di simboli
ed esattamente di k stati. Infatti, il numero di modi diversi in cui si può definire la
funzione di transizione 8 fissato l’alfabeto e il numero di stati risulta finito, poiché
8 ha dominio e codominio finito.
Più in generale, data una funzione f: D R, con D e R finiti vi sono
esattamente |7?||/J| funzioni totali f diverse. Seguendo lo stesso ragionamento per le
MT, poiché la funzione di transizione 8 di un MT a nastro singolo è una funzione
parziale, definita come 8: Q x A->Qx A x {R, L, S}, esisteranno esattamente (1
+ 3 • |2| ■ [A|)lel MT aventi A come insieme dei simboli e Q come spazio degli
stati. Infatti 8 può assumere qualunque valore appartenente a Q XA X {R,L,S},
oppure può risultare indefinita, nel qual caso si scriverà ò(q,i) = ±. Se chiamiamo h
la cardinalità dell’insieme A, allora esistono (1 + 3 • h • k')h'k MT dotate di k stati.
Per poter enumerare le MT in {MT4} è ora necessario ordinare le
(1 + 3 • h • k)h 'k MT con k stati seguendo un preciso criterio sistematico, ad esempio
in base all’ordine lessicografico. Dapprima bisogna imporre un ordinamento
all’interno degli insiemi Q, A e {R, L, 5}, stabilendo, ad esempio, che:
< 7o < 1> $ = ao< ai < ... < ah-\, R< L < S
Successivamente, per q, q e Q, a, a e A m, m' e {R, L, S},si definisca:
- {q, a) < {q , a') se e solo se (q' < q') V (fiq = q) A (a < a')), ossia
{q, ) < {qi, ai), {qo, afi < (qh «I ), (q!, H ) < {qh a^ ecc.
- ± < (q, a, mfidq e Q, 'da g A, 'dm e {R, L, 5}
- (q, a, m) < (q , a , ni) se e solo se
(q < q) V ((q = q) A (a < a')) V ((q = q) A (a = a') A (m< m'fi
La Figura 8.2 chiarisce il significato dell’ordinamento lessicografico.

0 < x, x < k, 0 < y, y' < h se e solo se la linea punteggiata va da {x,y) a


310
Informatica teorica

Infine, si definisca M< M', dove Me M' sono due MT a singolo nastro e a. k stati,
se e solo se esiste una coppia {q,d) tale che 8(g, a) < 5\q, à) e per ogni
{q, a} < {q, a} 5(q, a) = ò'(q', a'). In altre parole, M precede M' nell’ordine
lessicografico se, nella prima coppia {q, a) in cui le relative funzioni di transizione
differiscono, il valore di 8 risulta minore del valore di 8'. Sulla base di questo
ordinamento si possono effettivamente costruire, per ogni k, la prima, la seconda,
..., la (1 + 3 • h • kf 4-esima MT con k stati.

Esempio 8.2
Seguendo la procedura appena introdotta, ordiniamo le MT a nastro singolo con
due stati Q = {q0, qi} sull’alfabeto A = {b , a, b}. Secondo l’ordine lessicografico
avremmo cheq0<ql, b <a<b eR<L<S. Inoltre,
- {qo, b ) < {q0, a) < (q0, b} < {qx, b ) < {qh a) < (q1} b\
poiché {q, a} < {q', a’} se e solo se (q' <q) V ((q = q'} A (a < a')), ossia
- ± < {qh s, ni), Vi g {0, 1}, Vs g {b , a, b}, \/m g {R, L, 5}
- {q, a, m) < {q’, a, m’) se e solo se
(? < ?') V (iq = q') A (a < af) V ((q = qf A (a = a’) A (m < m'f)
Poiché l’ordine tra due MT è dato dal valore della funzione di transizione nel primo
valore rispetto all’ordine fissato tra le coppie {q, a}, si otterrà che la prima

b a b b a b

9o {qi, b, s) {qi, b, E) 1 qo fh, b, S) {qx, b, 5) 1

1 1 1 qi 1 1 1

Mi Mi + l

b a b b A b

<4i, b, S) {qx, b, S} {go, n>, R) qo {qx, b, s) <qi, b, S) (qo, b , E)

41 1 1 1 qi 1 1 1

Mì + 2 M{+ 3

b a b b a b

4o {q\, b, S) {qx, b, 5) {qo, , s) qo {qx, b, s) <91, b, S) {qo, a, R)

41 1 1 1 qi 1 1 1

Mi + 4 Mi + 5

Figura 8.3 Ordinamento di MT a nastro singolo


La risoluzione automatica dei problemi 311

macchina nell’ordinamento è quella con la funzione di transizione sempre


indefinita, seguita dalla macchina in cui la funzione di transizione è sempre
indefinita tranne che per la coppia (qi, b), per cui vale (q0, b ,R), e così via.
La Figura 8.3 rappresenta parzialmente questo ordinamento, a partire da MT
con almeno due transizioni definite. Le MT sono rappresentate attraverso la forma
tabellare della loro funzione di transizione. L’ordinamento complessivo non può
essere riportato completamente per motivi di spazio. Si noti infatti, che il numero
di MT a nastro singolo a due stati su A è pari a la (1 + 3 • 3 • 2)3'2, cioè 196.

A questo punto si può ottenere una enumerazione di tutte le MT ragionando nel
modo seguente. Dapprima si enumerino tutte le MT ad un solo stato, secondo
l’ordine stabilito; poi si enumerino tutte le MT a due stati e così via. Come risultato
si ottiene una biiezione:

«f: N <-> {TM/}


La costruzione precedente può essere facilmente implementata da un algoritmo:
risulta quindi semplice sviluppare un programma, in un qualunque linguaggio di
programmazione, che emetta in uscita tutti gli elementi di {MT/} nel loro ordine
lessicografico. Un programma di questo tipo, necessariamente, non termina mai la
sua esecuzione, dato che l’insieme {MT^} è infinito. E però sempre possibile
trasformarlo in un programma (che termina sempre) che calcola c? e ossia in
un programma che riceve come ingresso un numero naturale n e produce come
uscita la descrizione della MT «-esima e viceversa.

ESERCIZI
8.11 Si scrivano due programmi Pascal o C o lava che, per ogni sottoinsieme A
dei caratteri ASCII, calcolino le seguenti funzioni:
N {MXJ
^'1: {MXJ N
8.12 Si illustri a grandi linee la definizione di una MT che enumeri tutti i membri
di {TMj}, per un determinato alfabeto A. Non è necessario specificare tutti i
dettagli della costruzione di tale macchina, ma bisogna mostrare di conoscere
effettivamente in che modo si possa realizzare.
8.13 Si mostri schematicamente un’enumerazione effettiva di tutte le MT
multinastro aventi un determinato alfabeto di ingresso I, di memoria T (e di
uscita O). Si noti che il numero di nastri di memoria non è fissato a priori.
8.14 Mediante un ragionamento analogo a quello usato per enumerare
algoritmicamente {MXJ si mostri come si possono enumerare tutti i
programmi di un qualsiasi linguaggio di programmazione.
312 Informatica teorica

Spesso, un’enumerazione calcolabile da una MT viene chiamata Gòdelizzazione, in


onore di Kurt Godei, il matematico che per primo applicò questa tecnica ottenendo
alcuni risultati fondamentali della logica matematica e della teoria della
computazione (si ricordi che nella Sezione 2.4.3 è stata illustrata l’originale
Gòdelizzazione delle entità aritmetiche). Il numero naturale biiettivamente
associato da tale enumerazione ad un oggetto formale, in questo caso ad ima MT, è
chiamato numero di Godei dell’oggetto. Nella precedente enumerazione (fi, ogni
MT è quindi biiettivamente identificata dal suo numero di Godei.
E noto che una MT M, sia a nastro singolo che multinastro, può essere
impiegata per calcolare una funzione fM\ D —> R, con D e R opportunamente
codificati nell’alfabeto di M, o per riconoscere un linguaggio LM su di un alfabeto
opportunamente codificato nell’alfabeto di M, purché l’alfabeto di M abbia una
cardinalità > 2. Quindi, dalla precedente enumerazione delle MT, si può dedurre
l’effettiva enumerazione di tutte le funzioni computabili che vanno da un
determinato dominio ad un determinato codominio, nonché un’effettiva
enumerazione di tutti i linguaggi accettabili su di un dato alfabeto. Per riassumerò,
si è quindi ottenuto il seguente enunciato fondamentale.
Enunciato 8.4
1. Per ogni alfabeto A, l’insieme {MT^} delle MT sull’alfabeto A può essere
enumerato algoritmicamente, ossia può essere sempre stabilita una biiezione
algoritmica N <-> {MTy)}-
2. Tutte le funzioni algoritmicamente computabili si possono enumerare
algoritmicamente.
3. Tutti i linguaggi algoritmicamente riconoscibili si possono enumerare
algoritmicamente.

Si noti che, sebbene sia una funzione biiettiva, le enumerazioni delle funzioni
computabili e dei linguaggi riconoscibili non sono funzioni biiettive, poiché si
verifica che molte (in realtà infinite) MT sono in grado di calcolare la medesima
funzione.
Per comodità, nel resto di questo capitolo si farà riferimento principalmente a
MT che si comportano come dispositivi che calcolano funzioni da N a N. Per ogni
funzione f, con fi D —> R, dove D e R sono diversi da N, si presupporrà
implicitamente la codifica, a sua vota algoritmica, di D e di R in termini di N.
Inoltre My indicherà la j-esima macchina di Turing nella enumerazione precedente,
ossia My = &(y), mentre fiy indicherà la funzione calcolata da My.
La risoluzione automatica dei problemi 313

ESERCIZIO
8.15 Si mostri che, per ogni x e y prefissati, la funzione g, definita come la
composizione fx° fy, ovvero g(w) = fx(fy(w')), è computabile, ossia esiste un
indice z tale che g sia fz.
Si mostri inoltre che non solo g è computabile, ma esiste anche una funzione
computabile h: N x N —> N tale che, per ogni x e y, il valore z = h(x, y) è
tale che g =fz. In altre parole, date le MT (o i relativi numeri di Godei) che
calcolano due funzioni prefissate, si può effettivamente costruire una MT (o
il suo numero di Godei relativo) che calcoli la loro composizione.
Si noti infine che g(w) è definita se e solo se ^(w) è definita e fiffwy) è
definita: in altre parole, sia la computazione di My con ingresso w che la
computazione di Mx con ingresso ffw) devono terminare per assicurare la
terminazione di Mz.
Suggerimento', si progetti a grandi linee un algoritmo che riceve in ingresso
una descrizione di Mx e di My, producendo in uscita la descrizione di una
MT, M. che “concatena” le rispettive computazioni. Dapprima M simula My
su ogni ingresso. Poi, se e quando My termina la sua computazione, M simula
il comportamento di Mx sul risultato prodotto dalla simulazione di My. Infine
si calcola il numero di Godei di M, ossia z. Poiché tutto ciò viene svolto da
un algoritmo, la funzione h risulta computabile da una determinata MT.

8.4.2 Macchine di Turing universali


Come già osservato, fino ad ora, le MT e i vari tipi di altri automi sono stati studiati
in qualità di dispositivi ognuno dei quali è costruito per risolvere un particolare
problema; in altre parole le MT sono state viste come calcolatori astratti,
specializzati e non programmabili. In questa sezione introdurremo il concetto di
macchina di Turing Universale (MTU), ossia una MT capace di modellare
dispositivi generali di risoluzione di problemi, in cui il problema da risolvere non
viene codificato nella struttura del dispositivo, ma gli viene piuttosto fornito come
ingresso, insieme con i dati su cui operare.
Possiamo definire una MTU come una MT che calcola la funzione g(y, x) =
fy(x), cioè è una MT che ricevuti in ingresso due numeri naturali y e x, che
rappresentano rispettivamente la funzione fy calcolata dalla MT My e l’ingresso x,
che è l’effettivo ingresso su cui deve operare My, calcola il valore della funzione fy
applicata a x. Ad essere precisi, la MTU così definita non sembra appartenere alla
famiglia {MT/(} considerata fino ad ora, in quanto le funzioni associate alle MT in
{MT/(} sono funzioni di una variabile, mentre g è una funzione di due variabili.
Questo problema è però facilmente risolvibile in quanto esiste una biiezione
effettiva tra N x N e N.
314 Informatica teorica

Vediamo ora come si possa costruire una MTU, a sua volta in modo algoritmico. Si
considerino l’enumerazione discussa nella Sezione 8.5.1: No {MT^} e la
suddetta funzione g: N x N o N definita da g(x, y) =fy(x). Come già osservato la
funzione g(x, y) si può anche considerare come una funzione g : N —> N, purché
sia definita una opportuna biiezione calcolabile algoritmicamente da N x N a N.
Allo scopo di semplificare la notazione, si userà lo stesso simbolo g anche per g .
Un esempio di biiezione da N x N a N è dato dalla funzione
d(x,y) = + y^x + y + + x (si veja ia Figura 4.41 della Sezione 4.3.4), che
2
permette di associare biunivocamente e algoritmicamente a una coppia di numeri
naturali un numero naturale. Graficamente, è come visitare le coppie di punti nel
piano in un ordine prefissato, dove la posizione di un punto nella visita rappresenta
il numero naturale associato alla coppia che identifica il punto.
Si osservi che g è una funzione MT-computabile, ossia esiste uni e N tale che fi -
g ed è possibile calcolarla mediante i passi seguenti.
Passo 7: Si scelga un alfabeto finito A per codificare i numeri naturali ed ogni
altra informazione possibile richiesta per la computazione. E già noto che
qualunque^, con (4| > 2, è adatto a questo scopo.
Passo 2\ Si traduca la rappresentazione di n in un’opportuna rappresentazione
della coppia {x, y) corrispondente a n. La rappresentazione decimale di n
può essere tradotta nelle due rappresentazioni decimali di x e di y,
separate da un simbolo $. Ad esempio, se si sceglie la biiezione
<7: N x N N, definita precedentemente, allora n = 18 è tradotto in
“3$2”. .
Passo 3: Si traduca il numero y in un’opportuna codifica della MT j-esima nella
enumerazione (ossia My).
Passo 4: Si simuli la computazione di My su x.
È già nota la realizzabilità dei passi 1 e 2. Si esamini ora più attentamente come si
possa codificare My. La Figura 8.4a mostra la porzione di nastro che codifica la
transizione definita da
ò(p, a) = {q, b, m)
La Figura 8.4b illustra il caso in cui ò(p,a) è indefinita. Nella Figura 8.4 i simboli
sopralineati (come p, q , ...) indicano le codifiche delle corrispondenti entità della
MT (ossiap, q, ...). I simboli speciali $ e # vengono usati solo come separatori e
non possono apparire in alcun altro luogo. In particolare, una coppia di $ racchiude
la porzione di nastro che codifica ciascun valore di ò. 1 valori di ò vengono fomiti
in corrispondenza agli elementi Q x A, dove Q ed A sono, rispettivamente,
l’insieme degli stati e Tinsieme di ingresso di My.

1
La risoluzione automatica dei problemi 315

$ p # a # q # b f m $

(a)

$ q # a » b $

(b)
Figura 8.4 Codifica di My: (a) ò(p, a) = {q, b, ni); (b) ò(p, a) è indefinita.
Il passo 3 è dunque MT-computabile, ossia esiste una MT in grado di tradurre il (o
la codifica del) numero naturale y nella precedente descrizione di My.
Infine, anche il passo 4 può essere effettuato da una MT, come viene ora
mostrato per una MT multinastro. La MT considerata, come mostra la Figura 8.5,
si serve di un nastro per memorizzare la descrizione di My, di un nastro per
simulare lo stato corrente di My, per ciascun passo della computazione, e di un
nastro per simulare il contenuto del nastro di My, sempre per ciascun passo di
computazione. Il terzo nastro è infinito su entrambi i lati, come il nastro di My.
La computazione da parte del “simulatore” MT (SMT) consiste in una
sequenza di macro-passi. Ciascun macro-passo simula l’esecuzione di un
corrispondente passo di computazione di My ed è costituito dalla seguente sequenza
di azioni, da mi. a m4.
mi. Inizializzazione del macro-passo:
- Le testine di Ti e di T2 vengono posizionate sulle prime celle dei nastri.
Sia qc il contenuto di T2.
- La testina di T3 è posizionata sulla prima locazione della porzione di
nastro corrispondente alla locazione in cui la testina di My verrebbe
posizionata nel caso reale.
- qmiT e lo stato corrente del SMT.
m2. Il SMT cerca, all’interno di Tb una porzione di nastro racchiuso fra due $
consecutive, in modo tale che il primo elemento sia qc e il secondo sia ac,
dove ac è la codifica del simbolo che verrebbe letto da My. Una simile
porzione di nastro viene sempre, inevitabilmente, trovata. Quando ciò
avviene, la macchina entra in un nuovo stato qpouND-
m3. Quando SMT entra in qpouND, se la porzione di Tj trovata indica una
transizione indefinita, allora SMT entra in un nuovo stato quAtr e si ferma,
come farebbe My. Altrimenti, SMT effettua le seguenti azioni:
- Sostituisce qc in T2 con il terzo elemento della porzione trovata in Tb
ossia con la descrizione del nuovo stato di My, ad esempio sia qN .
- Sostituisce ac in T3 con il quarto elemento della porzione trovata in Tb
ossia riscrive T3 come farebbe My con il suo nastro di ingresso.
J16 Informatica teorica

Dispositivo
di controllo

Figura 8.5 La simulazione di My mediante MT a 3 nastri.

- Sposta la testina di T3 come specificato dal quinto elemento della


porzione trovata di Tb ossia simula il movimento della testina di My.
Si noti che, se la codifica di ogni singolo simbolo di My è una stringa più
lunga di un singolo carattere, allora la testina di T3 può muoversi, durante
ogni macro-passo, attraverso molte celle.
m4. Dopo il passo m3, se SMT non è entrato nel nuovo stato Qhalt, si ritorna
nella medesima situazione descritta in mi. A questo punto, la mossa di My è
stata completamente simulata e il macro-passo completato; il SMT è pronto
per effettuare un altro macro-passo.

ESERCIZIO
8.16 Si specifichino in dettaglio i macro-passi di un SMT mediante una
descrizione formale completa della macchina.

Osserviamo che abbiamo mostrato che g è una funzione MT-computabile usando


un approccio costruttivo. Ciascun passo (da mi. a m4.) del procedimento può
inoltre essere effettuato da una MT : si può quindi definire un'unica MT in grado di
eseguire tutto il procedimento nel suo complesso. In questo modo si è dimostrato il
seguente enunciato:
Enunciato 8.5
Esiste e si può costruire una MT in grado di calcolare g(x, y) =fy(x) per ogni y e per
ogni*.

Una MT di questo tipo viene chiamata Macchina di Turing Universale (MTU),
poiché è in grado di simulare il comportamento di qualunque altra MT. La MTU
realizzata sulla base del procedimento precedente ha un alfabeto più vasto di quello
delle macchine che simula. Questa limitazione può però essere facilmente superata
317
La risoluzione automatica dei problemi

mediante un’ulteriore codifica dell’alfabeto della MTU. In questo modo, in {MT/},


si constata la presenza di un numero infinito di MTU.

ESERCIZIO
8.17 Si mostri come una MTU possa simulare se stessa.

L’Enunciato 8.5 dà una risposta affermativa all’interrogativo posto all’inizio della


Sezione 8.4. Mediante le MTU, è infatti possibile impiegare il formalismo delle
MT per la simulazione di calcolatori di tipo “generai purpose”: la codifica di My,
sul nastro Ti della MTU di Figura 8.5, corrisponde al programma di calcolo
eseguito dalla macchina. Si noti inoltre che lo stesso ragionamento si può applicare
anche a tutti i formalismi equivalenti alle MT. I programmi in qualsiasi linguaggio
di alto livello si possono enumerare in modo molto simile alle MT (si veda
l’Esercizio 8.14). E inoltre possibile scrivere un programma (un interprete) che
simuli l’esecuzione di qualunque altro programma codificato nello stesso o in
linguaggio equivalente, e cosi via.
In ultima analisi il procedimento descritto per la costruzione di una MTU altro
non è che la riformulazione, nell’ambito del più semplice e astratto modello di
calcolo, della classica “catena”: compilazione del programma sorgente
(corrispondente alla traduzione da y alla rappresentazione di My); memorizzazione
del programma oggetto (in questo caso in Ti); esecuzione del medesimo
(simulazione delle varie mosse di My).

8.5 Problemi (algoritmicamente) irrisolvibili


Torniamo ora a uno dei quesiti che ci eravamo posti nella Sezione 8.1 e in
particolare, quanti e quali siano i problemi risolvibili algoritmicamente.
Come si è visto nella Sezione 8.4.1, tutte le funzioni computabili// N —> N si
possono enumerare; questo significa che la cardinalità delle funzioni computabili è
pari a K 0. D’altra parte, è ben noto che la cardinalità della classe delle funzioni

su N, cioè = {f\fi N N}, è 2X°, anche chiamata cardinalità del continuo,


essendo la medesima cardinalità dell’insieme dei numeri reali (si veda la Sezione
1.1). Infatti, l’insieme \f\fi N N} contiene la classe delle funzioni da N in {0,
1}, cioè {f\f. N -> {0, 1}}, poiché {0, 1} c N. Quindi, poiché \{f\fi N N}| >
\{f\fi N {0, 1}}| = |p (N)| = 2X°, possiamo dedurre che la cardinalità della
classe è strettamente maggiore della cardinalità della classe delle funzioni
computabili; si può quindi concludere che “gran parte” delle funzioni di N non può
essere calcolata.
318 Informatica teorica

Questo risultato, al di là delle apparenze, non crea eccessivi problemi. Infatti,


sebbene la classe di tutte le funzioni /: N —> N abbia cardinalità maggiore rispetto
alla classe delle funzioni computabili, le sole funzioni interessanti da calcolare
sono, intuitivamente, quelle che si possono “definire”. Qual è, tuttavia, il
significato esatto del termine “definire”? Qualunque significato (sia formale che
informale) gli venga attribuito, quando si vuole definire una funzione, si usa, in
ultima analisi, un linguaggio (ossia un insieme di stringhe su qualche alfabeto) che
la esprima. Si potrà usare, a questo scopo, sia il calcolo dei predicati sia la lingua
italiana, i quali non sono altro che frasi appartenenti ad un linguaggio numerabile.
Si può quindi esprimere una funzione mediante la stringa fix) = (2x) + x2 oppure
con la frase “Moltiplica x per due, poi calcola il quadrato di x e infine somma i due
risultati”. Infatti, sia la lingua Italiana che Tinsieme delle espressioni aritmetiche
sono insiemi numerabili, essendo sottoinsiemi di un monoide libero su di un
determinato alfabeto finito. Si ricava quindi che la classe delle funzioni
denotabili da un linguaggio è a sua volta numerabile.
^2/ contiene dunque tutte le funzioni di interesse pratico. Ciò potrebbe
indurre a sperare che la classe coincida con la classe delle funzioni
computabili, così da poter affermare che tutti i problemi di interesse pratico sono
risolvibili algoritmicamente. Sfortunatamente questa speranza verrà delusa, come
ci accingiamo a constatare in modo approfondito nelle prossime sezioni.

8.5.1 II problema della terminazione del calcolo


Quando si scrive un programma, in un qualsiasi linguaggio eseguibile da un
calcolatore, ci sono diverse proprietà che si vorrebbero garantire, quali ad esempio
la correttezza sintattica, la capacità del programma di garantire certi risultati e
proprietà e la terminazione del programma, cioè la garanzia che, dato un qualsiasi
ingresso conforme al programma stesso, esso termini la sua computazione, o, come
si dice spesso in gergo, non “vada in loop3”. In questa sezione analizzeremo
quest’ultimo requisito di un programma, che ha un notevole interesse pratico.
Quando si scrive un programma, a seconda della natura del linguaggio
utilizzato, esso viene compilato e poi eseguito oppure interpretato. In entrambi i
casi, la macchina che si occupa della compilazione o dell’interpretazione è in grado
di controllare il rispetto di alcune proprietà, come ad esempio, la correttezza
sintattica del programma. Ma quali proprietà dell’esecuzione del programma

3 II termine “andare in loop” tuttavia è leggermente improprio: esso infatti suggerisce l’idea
che l’esecuzione del programma entri in un ciclo di operazioni che viene ripetuto in
maniera identica aH’infinito. Questa circostanza, tuttavia, non è l’unico modo in cui
potrebbe avvenire che l’esecuzione di un programma non termini (come risulterà
ulteriormente chiaro nel seguito); al contrario, se così fosse sarebbe possibile eliminarla
riconoscendo 1’esistenza di un tale ciclo durante l’esecuzione del programma e
interrompendo quindi la sua inutile ripetizione.
319
La risoluzione automatica dei problemi

vengono analizzate dal compilatore? Perché esso non è in grado di garantire a


priori la terminazione di un programma per un generico ingresso (come ogni lettore
ha probabilmente sperimentato nella propria attività di programmazione)?
Quando abbiamo studiato i diversi modelli di calcolo nel Capitolo 4, abbiamo
osservato che un AF termina la sua computazione per qualsiasi valore in ingresso.
Introducendo poi la pila ci siamo resi conto che gli AP, a differenza degli AF,
possono, per alcune stringhe in ingresso, entrare in un cammino formato da una
sequenza infinita di 8-mosse, e quindi “andare in loop”. Il Teorema 4.13, però,
garantisce la possibilità di costruire per ogni AP ciclico un AP equivalente, ma
privo di cicli infiniti. Queste osservazioni mostrano che è possibile, in alcuni casi,
garantire che una macchina termini la sua computazione qualsiasi sia l’ingresso.
Analogamente, se considerassimo un linguaggio di programmazione che non
permette di scrivere cicli né funzioni ricorsive, né istruzioni di salto, limitando però
il potere espressivo del linguaggio, potremmo garantire la terminazione di un
generico programma per un qualsiasi valore di ingresso. Ma cosa accade per le MT
o per programmi scritti in linguaggi di programmazione senza i vincoli suddetti?
Constateremo che purtroppo in questi casi non è possibile garantire a priori la
terminazione del programma per un generico valore di ingresso, né decidere
attraverso un algoritmo se ciò possa avvenire in corrispondenza di uno specifico
dato; in termini più generali, quindi il problema della terminazione del calcolo
automatico è in generale non decidibile.
Ovviamente questo problema è definibile (ne vedremo a breve una definizione
formale attraverso l’uso di una funzione) ed è di notevole interesse pratico, perché
sarebbe estremamente utile poter affermare, prima di eseguire un programma, che
esso non termina per un generico valore in ingresso, evitando inutili attese, che
comunque non portano a concludere né che il programma terminerà né che il
programma non terminerà. Abbiamo quindi anche constatato che ci sono problemi
definibili che non possono essere risolti algoritmicamente, cioè l’insieme dei
problemi definibili contiene strettamente l’insieme dei problemi risolvibili, anche
se entrambi sono numerabili e hanno quindi la stessa cardinalità.
Analizziamo adesso il problema della terminazione in modo formale
attraverso il seguente fondamentale teorema. Come al solito, si indica con My la
MT jy-esima, secondo l’enumerazione <o della Sezione 8.4.1, e con fy la funzione
calcolata da My.
Teorema 8.6

Nessuna MT può calcolare la funzione totale g: N x N —> {0, 1} definita nel


seguente modo:
320
Informatica teorica

g(x, y) = if fy(x) A ± (ossia My giunge in uno stato finale di arresto leggendo


una x in modo tale che fy(x) sia definita)
then 1
else 0

La dimostrazione del Teorema 8.6 sfrutta una tipica tecnica diagonale, utilizzata
anche nel 1891 da Cantor per dimostrare che, per ogni insieme A, |^4| < |p(^4) | (si
veda la Sezione 1.1). La nozione di “dimostrazione diagonale” non è formalmente
definita, ma si riferisce al seguente schema, che verrà utilizzato anche nella
Sezione 8.7.
L’obiettivo è mostrare che un’enumerazione di oggetti, di fatto successioni a
valori in un insieme dato S, di cardinalità almeno 2, non è completa, ovvero che
uno degli oggetti che si vorrebbe trovare in tale enumerazione non vi può in realtà
comparire. Di fatto un’enumerazione di successioni può essere rappresentata come
una tabella con un numero infinito di righe e colonne. L’elemento che non compare
in tale tabella viene individuato per assurdo considerando inizialmente la diagonale
d di tale tabella, ovvero dt è l’z-esimo elemento della z'-esima riga. Poiché S ha
almeno 2 elementi è possibile definire la successione d’tale che, per ogni z, d'. sia
diverso da <7,. Una tale successione non può essere letta in nessuna riga della
tabella, mostrando la non esaustività della tabella.
Cerchiamo di chiarire meglio la tecnica appena descritta mediante un esempio.
Successivamente la applicheremo alla dimostrazione del Teorema 8.6 e
analizzeremo le conseguenze del suo enunciato,

Esempio 8.3
Vogliamo dimostrare che la funzione g così definita:
g(x, y) = if^(x)=l then 1 else 0
non è computabile, ossia che non esiste una MT che calcola g.
A partire da g, definiamo la funzione h con un solo argomento nel seguente
modo: h(x) = if g(x, x) = 1 then 0 else 1: ci siamo quindi posizionati sulla
diagonale del dominio di g. Se la funzione g fosse computabile, allora lo sarebbe
certamente anche h, cioè esisterebbe un qualche indice xh che rappresenta l’indice
di una MT che calcola h, cioè h = fx .
Calcoliamo allora il valore di h(xh), usando la sua definizione:
h(xh) = if g(xh, xh) = 1 then 0 else 1
Sfruttando la definizione di g, otteniamo
7z(x*) = if fx (x*) = 1 then 0 else 1
Ricordando ora che fx =h, possiamo riscrivere h(xh) come
h(xh) = if /z(x/,) = 1 then 0 else 1, che mostra evidentemente una contraddizione.
La risoluzione automatica dei problemi 321

Siamo ora pronti ad applicare lo schema di dimostrazione diagonale al Teorema


8.6.
Dimostrazione del Teorema 8.6 ■
Si supponga, per assurdo, che g sia computabile. La funzione parziale h definita
come
h(x) = if g(x, y) = 0 then 1 else ±
risulta allora computabile. Infatti, una volta ottenuta una MT M in grado di
calcolare g, sono sufficienti solo semplici modifiche per adattarla al calcolo di h:
basta infatti trasformare il risultato 0 in 1 e forzare la nuova macchina a non
fermarsi mai ogni volta che Mproduce il risultato 1.
Sia x0 il numero di Godei di una MT M r che calcola h: ossia f = h. Allora,
per la definizione di /z, se A(x0) (= fx (x0)) è definita, 7z(x0) = 1 e gfx0, x0) = 0.
Tuttavia, per la definizione di g(x, y), ciò implica che / (x0) sia indefinito.
Se invece h(x0) risulta indefinito, g(x0, x0) = 1 che, a sua volta, implica che
f (xo) = h(xo) sia definito.
In entrambi i casi si ottiene una contraddizione, quindi g non è computabile.

Il Teorema 8.6 afferma che nessuna MT è in grado di decidere se una determinata
MT si fermerà per un dato valore di ingresso4. Come già osservato, questo è
ovviamente un problema ben definito, infatti nell’enunciato del teorema lo
abbiamo rappresentato per mezzo di una funzione, e la prova che non può essere
risolto algoritmicamente è quindi la conferma che l’insieme dei problemi definibili
contiene strettamente l’insieme dei problemi risolvibili. Infatti, se è vero che ogni
problema risolvibile è anche definibile, il Teorema 8.6 ci mostra che ci sono,
invece, problemi definibili che non sono risolvibili.
Analizziamo ora un enunciato, strettamente collegato al Teorema 8.6, che
limita ulteriormente la “capacità di decisione” delle MT.
Corollario 8.7
Nessuna MT può calcolare la funzione totale k definita da
k(x) = iffi(x) ± then 1 else 0

4 Si noti che il Teorema 8.6 afferma che nessuna MT può decidere se, per un dato valore di
ingresso, una MT si fermerà in uno stato finale. Tuttavia ogni MT può sempre essere
costruita in modo tale da fermarsi se e solo se viene raggiunto uno stato finale.
322
Informatica teorica

Dimostrazione
La dimostrazione segue la traccia della precedente dimostrazione del Teorema 8;6.
Dapprima si definisca:
h(x) = if A(x) = 0 then 1 else ±
Si osservi, quindi, che h è computabile se k è computabile. Infatti, una volta
ottenuta una MT M in grado di calcolare k, sono sufficienti solo semplici modifiche
per adattarla al calcolo di h:. Sia ora x0 il numero di Godei di una determinata MT,
M , che calcola h: ossia h = f , cioè A è la funzione calcolata dalla x0-esima
MT. Si verifica quindi facilmente, tuttavia, che A(x0) = 1 implica f (x0) = ±,
mentre h(x0) = ± implica h(x0) = 1, ossia f (x0) = A(x0) = 1 : in entrambi i casi si
ottiene quindi una contraddizione.

Osserviamo che il Corollario 8.7 è impropriamente citato come un corollario del
Teorema 8.6, ma più correttamente esso ne è un lemma, infatti esso costituisce il
cuore sella dimostrazione del Teorema 8.6. L’enunciato di questo lemma, di per sé,
non ha un particolare significato pratico, ma è tuttavia importante menzionarlo per
sottolineare che il suo enunciato non può essere ricavato come conseguenza
immediata del Teorema 8.6, il cui enunciato è più generale.
Si osservi infatti che se un problema è irrisolvibile, può accadere che un suo
caso particolare diventi risolvibile, mentre una sua generalizzazione è
necessariamente irrisolvibile. Al contrario, se un problema è risolvibile, può
accadere che una sua generalizzazione diventi irrisolvibile, mentre un suo caso
particolare rimane sicuramente risolvibile. Nel nostro caso, il Corollario 8.7 è
evidentemente un caso particolare del Teorema 8.6 e sarebbe quindi scorretto
ricavare la sua asserzione semplicemente come conseguenza dell’enunciato del
teorema principale: è al contrario necessario dimostrarlo ex novo, pur
ripercorrendo l’essenza della dimostrazione originaria.

ESERCIZIO
8.18 Seguendo la traccia delle precedenti dimostrazioni per assurdo, si dimostri
che non esiste alcuna MT in grado di calcolare la funzione g definita da:
g(x, y, z) = iff(y) = z then 1 else 0

8.5.2 II problema di riconoscere se una funzione è totale


Consideriamo ora un problema di estrema importanza pratica, strettamente
correlato al problema della terminazione. Nel Teorema 8.6 abbiamo dimostrato
La risoluzione automatica dei problemi 323

che, dato un generico programma e un generico ingresso, non è possibile decidere


algoritmicamente se il programma terminerà la sua esecuzione per l’ingresso dato.
Adesso invece analizziamo un problema simile, ma diverso. Infatti nel Teorema 8.6
si considerava la funzione
g(x, y) = if fy(x) A ± (ossia My giunge in uno stato finale di arresto leggendo
una x in modo tale che fy(x) sia definita)
then 1
else 0
mentre ora siamo interessati a sapere se la generica MT (il programma) y termina
per ogni valore in ingresso o no, cioè vogliamo sapere se la funzione fy è totale
(ossia Vx fy(x) ±) e computabile o no.
Si osservi che in certi casi è possibile stabilire Vz, se fy(z) ±, senza però essere in
grado di stabilire se fy è totale, anche se ovviamente trovare un valore z’ per cui
fy(z ’) = ± ci porterebbe a concludere che fy non è totale. Viceversa, in alcuni casi, si
può concludere che fy non è totale, senza però poter decidere se fy(z) ± per ogni
singolo z, anche se, nel caso fy(z) ±, questa circostanza sarebbe evidentemente
determinata semplicemente facendo eseguire My in corrispondenza del dato z,
trovandosi però impossibilitati a trarre la conclusione opposta se, dopo un tempo
finito di calcolo, l’esecuzione non fosse ancora terminata.
In generale, il problema di stabilire se una funzione sia totale o no è
indecidibile, come formalizzato dal seguente teorema.
Teorema 8.8
Non esiste alcuna MT in grado di calcolare la funzione k, definita da:
k(x) = if /) è totale (ossia Vx fy(x) ±) then 1 else 0

Dimostrazione
Una volta ancora, la dimostrazione procede per assurdo e in maniera diagonale,
anche se l’applicazione della tecnica dimostrativa richiede in questo caso un’analisi
più dettagliata. Si supponga dunque che k sia totale e computabile. Allora la
funzione g: N N, definita come:
g(x) = w, dove w è il numero di Godei della x-esima MT della enumerazione <£ che
calcola una funzione totale fw, risulta a sua volta totale e anche computabile. E
infatti possibile enumerare tutte le MT per mezzo di una MT. E quindi sufficiente
controllare, per ciascuna di esse, se la funzione calcolata è totale o meno, ottenendo
in questo modo una effettiva enumerazione di tutte le MT che calcolano funzioni
totali. Poiché, per ciascuna z, esiste sempre una z > z per cui f- risulta totale, anche
g(x) è totale.
Si consideri ora la funzione totale h definita da:
h(x) =fg(x)(x) + 1 =/w(x) +1
324 Informatica teorica

Poiché, per ogni x,f^ è totale e computabile, altrettanto risulta essere h. Essendo
poi g totale e strettamente monotona (ossia x' > x implica che g(x') > g(x)),
risulta definita sull’insieme W dei numeri di Godei delle MT che calcolano
funzioni totali e la sua computabilità discende ovviamente dalla computabilità di g.
Sia w0 il numero di Godei di una MT che calcola h. Poiché h è totale, g-1(w0) è
definita. Siax0 = g4w0); allora, per la definizione di Ir.

Kxq) = /g(lo) (%o) + 1=4, (*o) + 1

Tuttavia h è fw , da cui si ottiene /z(x0) = 4 (xo): una contraddizione.



Dal punto di vista dell’impatto pratico, il Teorema 8.8 risulta forse ancora più
significativo del Teorema 8.6; infatti esso afferma l’irrisolvibilità del problema di
decidere se un certo programma terminerà la sua esecuzione per qualsiasi dato di
ingresso, o al contrario, per qualche dato andrà in loop. Nel caso precedente,
invece, eravamo interessati al problema di sapere se un certo programma con certi
dati avrebbe terminato o meno la sua esecuzione.

ESERCIZI

8.19 * Si dimostri che nessuna MT può decidere se, per ogni x, la funzione
calcolata da Mx sia costante o no. Ossia, si dimostri che la funzione g definita
da
g(x) = if4 è costante then 1 else 0
non può essere calcolata da alcuna MT.
8.20 * Si mostri che non esiste alcuna MT in grado di calcolare una delle seguenti funzioni:
■ gi(x, y)= ify G 1 f then 1 else 0
(Si ricordi, dalla Sezione 1.1, che // indica l’immagine di f)
- g2(x, y) = if fx coincide con fy (ossia Mx e My calcolano la medesima
funzione) then 1 else 0
■ gs(x) = if 1 f è finito then 1 else 0
8.21 Si mostri che, per ogni z0 prefissato, non esiste alcuna MT in grado di
calcolare la funzione g definita da
g(x) = if zoe I f then 1 else 0

8.22 Per ogni x0 prefissato, si discuta se la funzione g definita da:

g(x) = if ze Df then 1 else 0

è una funzione computabile da una MT o no (si ricordi che Df denota il


dominio di definizione della funzione f).
La risoluzione automatica dei problemi -’z~>

8.23 Si risponda alla medesima domanda dell’Esercizio 2.22 sostituendo con


7/«0 • ■
Avvertimento-, se non si è riusciti a risolvere qualcuno degli esercizi precedenti, si
consiglia di ritornarvi dopo aver letto le Sezioni 8.7 e 8.8.

Analizziamo ora alcune conseguenze pratiche dei risultati fin qui ottenuti. In primo
luogo si è definitivamente constatata l’esistenza di problemi non risolvibili
meccanicamente (o algoritmicamente). Ciò non esclude però la possibilità di
trovare una soluzione per tali problemi: osserviamo infatti che, se le MT (e quindi
gli algoritmi) falliscono, si può sempre sperare neH’intuito e nell’esperienza umani.
Ad esempio, chiunque risponderebbe positivamente alla domanda se la funzione x*2
sia totale e negativamente alla medesima domanda riferita alla funzione 1/x;
qualche riflessione e conoscenza matematica in più sarebbe necessaria per le
2 .
funzioni. x log2(x), log (2')• Nessuno però è in grado di garantire una
a/x2 + 1 22 •
risposta per qualsiasi funzione gli venga sottoposta, proprio per la mancanza di un
procedimento algoritmico.
In secondo luogo, la portata delle affermazioni e delle tecniche di
dimostrazione impiegate è molto più ampia degli scarni risultati matematici finora
enunciati e dimostrati. Infatti, il Teorema 8.6 si può riformulare, in un contesto più
generale, come l’impossibilità di decidere meccanicamente se un dato algoritmo,
indipendentemente dal linguaggio di codifica, si fermerà, prima o poi, per un dato
valore di ingresso. Analogamente, il Teorema 8.8 assicura che nessun algoritmo
può decidere se un dato programma si fermerà per tutti i possibili valori di
ingresso: ecco perché un compilatore ci può segnalare se abbiamo omesso una
parentesi in un nostro programma, ma non ci può avvisare del fatto che il nostro
programma è a rischio di andare in loop o, peggio, vi andrà sicuramente qualsiasi
dato gli venga fornito in ingresso.
Si noti anche che le tecniche di dimostrazione impiegate non sono
strettamente limitate al formalismo delle MT, ma possono venire applicate
direttamente, ad esempio, ai programmi Pascal, una volta fissata un’opportuna
enumerazione algoritmica ed una convenzione che stabilisca quale problema viene
risolto dall’x-esimo programma dell’enumerazione.
Infine, la considerazione che le MT definiscono funzioni parziali, porta con sé
alcune conseguenze. Infatti, la funzione g usata nella dimostrazione del Teorema
8.6 non è computabile solo perché è totale. Se invece si usa la sua restrizione g,
definita da:
g'(x, y) = iffy(x) A ± then 1 else ±
326 Informatica teorica

è facile verificare che g' risulta computabile, essendo solo una piccola modifica
della funzione calcolata da una MTU.
Tutte queste osservazioni vengono approfondite nella sezione seguente.

8.6 Problemi risolvibili, problemi risolti e problemi di cui non si conosce la


soluzione
È ormai definitivamente nota l’esistenza di problemi irrisolvibili o,
equivalentemente, di funzioni non computabili. Per approfondire questi aspetti
della teoria della computabilità, bisogna tener conto dell’esistenza di problemi per i
quali non esiste alcuna dimostrazione della loro irrisolvibilità, ma che, tuttavia, non
siamo in grado, almeno al momento, di risolvere. Qualche esempio dovrebbe
chiarire meglio questa affermazione.
Si è già dimostrato che non esiste una MT in grado di decidere la terminazione
di una MT qualsiasi, per un qualunque dato di ingresso. Si supponga ora di avere a
che fare con una particolare MT, detta ad esempio M-, alla quale si desidera
fornire un dato di ingresso, ad esempio x . Sia f- la funzione calcolata da M-.
Chiaramente, può accadere di non essere in grado di stabilire se M- si fermerà
quando le viene fornito l’ingresso x . Si può dimostrare che effettivamente questo
problema è irrisolvibile, tanto quanto il problema più generale di stabilire se fy(x)
sia definita, o meno, per ogni (x, y)1 Ebbene, è inutile cercare una simile
dimostrazione perché semplicemente non esiste. Infatti, si supponga per assurdo
che questa dimostrazione esista. Allora, o f- ( x ) è definita o non lo è. Se fosse
definita, M- si fermerebbe ricevendo x in ingresso, ma, in questo caso, M-
stessa contraddirebbe l’affermazione dell’inesistenza di una MT in grado di
risolvere il problema. Per evitare questa contraddizione, bisogna concludere che
fy ( x ) non è definita; tuttavia di nuovo si sarebbe risolto il problema precedente.
In conclusione: supporre l’esistenza di una dimostrazione della irrisolvibilità del
problema posto porta ad una soluzione per il problema, ma non implica l’aver
trovato la soluzione stessa del problema!
In altre parole, il problema precedente può anche essere formalizzato come
una funzione h definita come:
hlyv) = if /- (x )^± then 1 else 0

La definizione mostra che h è la funzione costante 1 oppure la funzione costante 0:


h non dipende da w. il predicato “fy(x) è definita” non contiene alcuna variabile
(quindi è una fbf chiusa, si veda la Sezione 2.3) e può quindi risultare solo vero o
falso. Di conseguenza, la funzione h è computabile, poiché può consistere solo
nella funzione costante 1 o nella funzione costante 0, ciascuna delle quali è
computabile. Non è, però, noto come calcolare h, ossia non sappiamo quale delle
La risoluzione automatica dei problemi 327

due funzioni costanti sia h, anche se è evidente che essa è computabile perché lo
sono entrambe le possibilità.
Questo tipo di risultato ricorda le dimostrazioni non costruttive spesso
utilizzate in matematica, dove si dimostra che una soluzione esiste, ma non per
questo è possibile fornirla: in questo caso siamo in grado di affermare che la
funzione h(w) è calcolabile, ma non sappiamo come calcolarla. Questo mostra che
per certi problemi possiamo arrivare ad affermare che il problema è risolvibile,
cioè esiste una MT che lo risolve, ma non per questo siamo in grado di fornire tale
MT.
Allo scopo di chiarire questo punto piuttosto intricato, consideriamo altri
esempi di problemi la cui risoluzione consiste nel rispondere a una domanda, la cui
risposta è certamente una risposta affermativa o negativa “secca”: “Sì” o “No”
indipendentemente dal valore di eventuali variabili in ingresso. Esempi di questi
problemi sono domande del tipo “La partita a scacchi perfetta terminerà? Con la
vittoria del bianco, del nero, o in parità?” oppure “Il numero di molecole
IO10
• IO19 '
dell’universo è pari a 10 ?”. In entrambi questi esempi, come nel caso della
funzione h(w), sappiamo a priori che la risposta è “Sì” o “No”, anche se non
sappiamo quale sia.
Il problema della irrisolvibilità algoritmica può presentarsi solo quando si
devono prendere in considerazione infiniti casi possibili (argomenti e valori di una
funzione, stringhe di un linguaggio), ma non di fronte al calcolo di una funzione
costante, che è il tipo di funzione associata ai problemi descritti in precedenza.
Infatti, codificando la risposta “Sì” come 1 e la risposta “No” come 0, i problemi
precedenti sono associabili o alla funzione fsi (x) = 1, Vx, oppure fno (x) = 0, Vx.
Entrambe le funzioni sono banalmente calcolabili, quindi, anche se non fosse noto
quale delle due funzioni associare al problema, se queste sono le uniche possibili
soluzioni associabili, il problema risulta banalmente risolvibile, pur non essendo
nota la soluzione.
Più in generale, ogni volta che un problema è formalizzabile attraverso ima
funzione definita su un dominio finito, il problema è certamente decidibile. In tali
casi, infatti, la soluzione è rappresentata da una tabella finita. L’esistenza di tale
tabella, però, non garantisce di saperla costruire..
Per chiarire ulteriormente questo delicato aspetto della computabilità, si
considerino i seguenti esempi.

Esempio 8.4

Si consideri la funzione g: N —> {0, 1, ..., 9} definita come g(x) = x-esima cifra
nella rappresentazione decimale del numero reale rr. Ci si potrebbe chiedere se g
sia computabile, visto che rr non è un numero razionale. L’analisi numerica
fornisce diversi algoritmi per calcolare un’approssimazione di n per ogni valore
s > 0 del limite di errore. Esistono cioè algoritmi che calcolano
JZ6 Informatica teorica

un’approssimazione decimale di tt in modo tale che la x-esima cifra sia esatta, per
ogni intero x. La funzione g(x) risulta allora computabile: esiste infatti una MT My
che calcola una funzione fy tale che g =fy. Inoltre è effettivamente possibile trovare
una tale y.
Si consideri ora una nuova funzione gy.
gi(x) = if esiste una sequenza di esattamente x “5” consecutivi nell’espansione
decimale di n then 1 else 0
In questo caso, i precedenti metodi numerici non consentono di concludere che gì
sia computabile. Infatti, per ogni x prefissato, si. può cominciare a calcolare
l’espansione decimale, peraltro infinita, di 7t. In un dato istante possono verificarsi
solo due situazioni: o si è trovata una sequenza di esattamente x “5” consecutivi
oppure non la si è trovata. Nel primo caso si può concludere che gi(x) = 1 e fermare
la computazione, ma nell’altro caso non si può trarre nessuna conclusione. D’altra
parte ciò non consente di concludere che gì sia non computabile. Potrebbe
accadere, infatti, che uno scienziato ingegnoso scopra un algoritmo per calcolare
g7. Potrebbe perfino succedere che venga dimostrato un teorema del tipo “per ogni
dato x esistono sempre x ‘5’ ” consecutivi nell’espansione di tt”. In questo caso
gi(x) sarebbe identicamente uguale a 1.
Molto tempo fa, lo stesso tipo di incertezza esisteva per la funzione h definita da:
h(x) = if esiste una sequenza di esattamente x “5” consecutivi nell’espansione di
741/2339 then 1 else 0
che oggi è noto essere computabile.
Si modifichi ora leggermente il problema, considerando la seguente funzione g2.
g2(x) = if esiste una sequenza di almeno x “5” consecutivi nella espansione
decimale di it then 1 else 0
In questo caso g2 è certamente computabile, poiché ricade necessariamente in uno
dei due casi seguenti:
a. esiste un k tale che
g2(x) = if x < k then 1 else 0 (si veda la Figura 8.6a)
b. g2(x) = 1 (ossia g2 è identicamente uguale a 1) (si veda la Figura 8.6b)

Figura 8.6 Possibili rappresentazioni per la funzione g2.


La risoluzione automatica dei problemi 329

Ad esempio potrebbero esistere 13, 118, 30456 ‘5’ consecutivi e non altre sequenze
oltre tale limite: in tal caso si ricadrebbe nel caso (a) di figura con k = 30457;
oppure potrebbe valere la congettura precedente “per ogni dato x esistono sempre x
‘5’ ma non sussistono altre possibilità.
Nel caso (b), g2 è una funzione costante, quindi computabile. Anche nel caso
(a) essa è computabile, qualunque sia il valore di k. Il problema è che non è noto in
quale dei due casi ci si trovi, né, qualora g2 fosse del tipo 1, quale sia il valore
effettivo di k. È noto quindi che esiste un y tale che g2 =fy, ma non si sa quale sia.
Di nuovo, potrebbe accadere che, in futuro, si riesca effettivamente a trovare un y
opportuno. Finché ciò non accade, tuttavia, non bisogna cercare di dimostrare che
il problema di trovare un y siffatto sia meccanicamente insolubile.

Esempio 8.5
Vogliamo stabilire se dati due generici programmi Pi e P2, che calcolano un
polinomio di grado n fissato (ad esempio n = 324), è decidibile il problema di
decidere se Pi e P2 sono equivalenti. Questo è un caso particolare di un problema
che verificheremo essere indecidibile (si veda la Sezione 8.7.2), ossia stabilire se
due generici programmi Pi e P2, che calcolano due generiche funzioni computabili
/i sf2, siano equivalenti (cioè/i =f2) o no.
Si osservi però che un polinomio di grado n è individuato da n + 1 punti. Per
decidere l’equivalenza tra Pi e P2 è perciò sufficiente fornire in ingresso a entrambi
i programmi n + 1 valori diversi della variabile in ingresso: se (e solo se) i
corrispondenti valori prodotti da Pi e da P2 coincidono in tutti i casi i due
programmi sono equivalenti. In questo caso quindi il problema risulta risolvibile,
ma anche risolto, essendo stato trovato uno specifico algoritmo per la sua
soluzione.

Esempio 8.6
Consideriamo il problema di realizzare un archivio di 5MB mediante una tabella
hash, in cui i record hanno la chiave costituita da sequenze di al più 30 caratteri.
Qualora, nella definizione della funzione di hash h, si trovi ima funzione h
“perfetta”, cioè, tale per cui x y implica h(x) h(y), e non si riesca però ad
implementare un algoritmo per il calcolo di h, si può comunque essere certi che h
sia calcolabile. Infatti, poiché il dominio su cui è definita h è finito, h è certamente
calcolabile, anche se questa conclusione non aiuta a calcolarla.

Esempio 8.7
Il ben noto che l’ultimo teorema di Fermat afferma che non è possibile trovare
quattro numeri interi x, y, z, n, con n > 2, tali che xn + yn = z.
330
Informatica teorica

Ora, la funzione h data da


ù(w) = if l’ultimo teorema di Fermat è vero then 1 else 0
è certamente computabile, poiché corrisponde alla funzione costante 1 (ù(w) = 1
per tutti i w), oppure alla funzione costante 0 (ù(w) = 0 per tutti i w), entrambi
computabili. In tempi relativamente recenti l’ultimo teorema di Fermat è stato
dimostrato: ossia si è dimostrato che la funzione h suddetta è la prima delle due
alternative. Il problema da essa formalizzato è perciò non solo decidibile ma anche
deciso. Si noti tuttavia che fino a poco tempo fa il “teorema” era in realtà una
congettura di Fermat, ma non vi era certezza che essa fosse vera: ai tempi di
Fermat -e oltre- dunque, il problema era decidibile ma non -ancora- deciso.

ESERCIZIO
8.24 Le seguenti funzioni sono computabili?
1. /zi(x) = if la codifica decimale di x si presenta all’interno della codifica
decimale di n then 1 else 0
2. h2(x) = if ‘5’ si presenta dopo la x-esima cifra dell’espansione di n then
1 else 0
3. h3(x) = if ‘5’ si presenta prima della x-esima cifra dell’espansione di n
then 1 else 0
4. * h4(x, y) = if nell’espansione di n esiste una sequenza di h > x cifre
ripetute y volte then 1 else 0
Nel caso affermativo, è possibile trovare effettivamente un algoritmo che le
calcoli?

8.7 Ulteriori concetti fondamentali di teoria della computabilità


Gli argomenti trattati in questo capitolo vengono generalmente classificati
nell’ambito della “teoria della computazione” o “teoria della computabilità”. I
principali risultati raggiunti finora possono essere così sintetizzati:
- Molti problemi, pur matematicamente ben definiti non possono essere risolti
mediante procedimenti algoritmici; per trovarne una soluzione occorrerà, caso
per caso, fare ricorso ad altre tecniche, tipicamente umane, senza peraltro
garanzia di poterla trovare.
- D’altro canto esiste una categoria di problemi per i quali l’esistenza di un
procedimento algoritmico di soluzione è nota, ma non si è a conoscenza, al
momento, di un tale procedimento.
Proseguiamo ora con l’esame di altri fondamentali risultati della teoria della
computazione con lo scopo di mostrarne le importanti conseguenze pratiche e di
fornire al lettore una propria capacità autonoma di stabilire, almeno nei casi più
semplici, se un problema che gli si presenta sia risolvibile oppure se sia inutile
perdere tempo alla ricerca di un inesistente algoritmo di soluzione.
La risoluzione automatica dei problemi 331

8 .7.1 Insiemi ricorsivi e ricorsivamente enumeratali


Si è già osservato molte volte come la nozione intuitiva di problema si possa
formalizzare mediante diversi concetti matematici, a seconda del particolare punto
di vista che si desidera enfatizzare.
Si focalizza ora l’attenzione sul problema della decidibilità di un insieme.
Esso può essere espresso anche come il problema dell’appartenenza di un certo
elemento ad un insieme dato. Ad esempio, un tipico problema di appartenenza
consiste nello stabilire se una determinata stringa appartenga o meno ad un certo
linguaggio. Più in generale si noti che una qualsiasi proprietà di elementi di un
insieme può esser formalizzata come un suo sottoinsieme, ossia come un elemento
del suo insieme delle parti: l’essere un numero pari individua un sottoinsieme degli
interi; così come l’essere un quadrato perfetto; la proprietà di terminazione del
calcolo per ogni valore dei dati in ingresso, ampiamente discussa nella sezione
precedente individua un sottoinsieme dell’insieme di tutti i programmi ecc.
L’appartenenza di un elemento a un insieme è perciò un comodo modo di
formalizzare una notevole quantità di problemi.
Nel resto di questa sezione focalizzeremo lo studio sui sottoinsiemi di N, che
verranno convenzionalmente indicati con la lettera S, per cui, formalmente, dati x
e N e S cz N, stiamo affrontando il problema di rispondere alla domanda “x e ST\
Come ormai dovrebbe essere ben chiaro, questa formalizzazione del problema
della decidibilità di un insieme è del tutto generale perché applicabile a qualsiasi
insieme che si possa mettere in corrispondenza biuninvoca ed effettiva con N,
ossia praticamente tutti gli insiemi numerabili di interesse pratico.
Per affrontare meglio questo tipo di problemi, richiamiamo dalla Sezione
2.4.3 la definizione di funzione caratteristica di un insieme.
Definizione 8.2

La funzione caratteristica c$. N^{0, 1} di un insieme S è definita da:


cs(x) = if x e S then 1 else 0

La risolvibilità del problema di appartenenza ad un insieme, o meglio, la ricorsività
deH’insieme, dipende dalla computabilità della sua funzione caratteristica, come
formalizzato dalla seguente definizione.

Definizione 8.3
Un insieme S è ricorsivo (o decidibile) se, e solo se, la sua funzione caratteristica è
computabile.

Si noti che, per ogni insieme S, la sua funzione caratteristica cs è totale, infatti, dato
un qualsiasi elemento x e N, esso appartiene (cfx) = 1) o non appartiene (c^x) =
332
Informatica teorica

0) a S. Consideriamo ora una nozione “più debole” di insieme ricorsivo, cioè la


nozione di insieme ricorsivamente enumerabile.
Definizione 8.4 '
Un insieme S è ricorsivamente enumerabile (o semidecidibile) se e solo se è
l’insieme vuoto oppure è rimmagine di una funzione totale e computabile g$, ossia,

5= Igs = {x | 3y,y e N >\x = g^y)}



Gli insiemi decidibili devono il loro nome al fatto che, per essi, il problema della
appartenenza può essere risolto meccanicamente. Infatti, una MT o qualunque
algoritmo che sia in grado di implementare la loro funzione caratteristica, fornirà
necessariamente una risposta alla domanda x e S, per ogni x. Il termine “ricorsivo”,
trae origine ovviamente dal formalismo delle funzioni ricorsive. Nel caso degli
insiemi ricorsivamente enumerabili, è possibile soltanto enumerare i loro elementi
per mezzo di un algoritmo. Per ogni insieme S, ricorsivamente enumerabile, si può
quindi costruire una sequenza
Xo = gs(0)> *7 = gs( 1 ), x2 = gs(2), ■ ■ ■
tale che, se x e S, allora esiste un i per cui x = gs(i)- In questo caso, esaminando la
sequenza {x,}, si riesce prima o poi a trovare x, concludendo quindi che x e S. Se
invece x<tS, ovviamente non si troverà mai x in {x;}. Perciò, se per un qualsiasi i ,
risulta x <£ {gs(0 I 0 < i < i } non si può concludere né che x e S né che x £ S. Per
questa ragione, S viene anche detto semidecidibile. Informalmente, quindi, gli
insiemi semidecidibili sono quelli per cui è possibile rispondere alla domanda “x e
5?” se la risposta è affermativa, ma in caso negativo, non permettono di concludere
che la risposta è negativa, ma neppure che non lo è.
Presentiamo ora alcune proprietà essenziali degli insiemi ricorsivi e
ricorsivamente enumerabili. Esse potranno essere utilizzate per investigare le
proprietà di decidibilità, o semidecidibilità, di vari problemi.
Teorema 8.9
(a) Se S è ricorsivo, è anche ricorsivamente enumerabile.
(b) S è ricorsivo se, e solo se, sia S che S = N - S sono ricorsivamente
enumerabili.

Dimostrazione della parte (a)
Sia c$ la funzione caratteristica di S. Se S è vuoto, allora è ricorsivamente
enumerabile per definizione. Altrimenti, esiste almeno un elemento k e S.
Definiamo ora la funzione
g^x) = if c^x) = 1 then x else k
La risoluzione automatica dei problemi 333

g$(x) risulta chiaramente totale, computabile e tale che Ig = S.


Quindi, poiché S è l’immagine di una funzione totale e computabile, per la
Definizione 8.4, S risulta essere ricorsivamente enumerabile.

Si noti tuttavia che il ragionamento precedente porta ad affermare 1’esistenza di gs,
ma non fornisce una “dimostrazione costruttiva” di tale esistenza; infatti esso non
richiede né l’esistenza di un algoritmo per stabilire se 5 = 0, né l’esistenza di un
algoritmo per trovare un opportuno k^S. La dimostrazione è però completa perché
soddisfa comunque quanto richiesto dalla Definizione 8.4, ossia l’esistenza di g^x)
che è ben definita anche in assenza di un procedimento per calcolarla.
Dimostrazione della parte (b)
1. Dimostriamo che, se S e S sono ricorsivamente enumerabili, allora S è
ricorsivo. Se S è vuoto oppure coincide con N, l’enunciato è ovvio.
Altrimenti, poiché né S né S sono vuoti, esistono due funzioni totali
computabili gs e g- tali che S = I e S = I , cioè S = {g$(0), gs(l)> gs(2),

...} e S = {g-(0), g-(l), gy(2), •••}. Ovviamente, per costruzione, S u S =


N e S n S = 0. Quindi, Vx e N(3y | x = gs(y) v x = g-(y) a-i(3z, w |x=

gs(z) a x = g- (w))), cioè ogni x e N appartiene in modo esclusivo a S o a S .


Si consideri ora l’enumerazione:
■/ = fe(0), gs(0),gs(l), fjl),...}
Poiché N, siamo certi che Vx, x e ./e, sex compare in Sf in posizione
pari, allora non compare mai in posizione dispari e viceversa.
Si definisca allora cs come
cs(x) = if x = 5,e. / con z dispari then 1 else 0
Chiaramente, cs è ben definito, poiché x appare in ,9? solo in posizione pari o
solo in posizione dispari. Inoltre risulta totale e computabile, in quanto
l’enumerazione 2Z è effettiva (ossia può essere effettuata da qualche MT).
Basta infatti, dato x, cercare x in éZe, a seconda che si trovi in un posto dispari
o pari, è possibile concludere se x appartiene o meno a S.
2. Se S è ricorsivo allora anche S è ricorsivo.
Infatti
c- (x) = if cs(x) = 0 then 1 else 0
Quindi, in virtù della parte (a) del Teorema 8.9, sia S che S sono
ricorsivamente enumerabili.

Per ciascun insieme ricorsivamente enumerabile S, esiste una funzione computabile
gs tale che S = Igs. La cardinalità della classe degli insiemi ricorsivamente
334
Informatica teorica

enumerabili non risulta quindi superiore alla cardinalità della classe delle funzioni
computabili, ossia Ko. Come nel caso delle funzioni computabili, la maggior parte
dei sottoinsiemi di M non sono ricorsivamente enumerabili, poiché la cardinalità di
p(N) è 2X°. Il ragionamento corrisponde perfettamente all’analisi delle funzioni
computabili e non computabili.
Il prossimo risultato introduce un insieme non ricorsivamente enumerabile che
permette di approfondire ulteriormente il problema della terminazione del calcolo,
confermando la sensazione originaria che esso sia imprescindibilmente connesso al
raggiungimento della massima potenza di calcolo.
Teorema 8.10
Per ogni S, se
1. i e S implica che f sia totale e,
2. per ogni funzione f totale e computabile, esiste un i e S tale che f=f,
allora S non è ricorsivamente enumerabile.

Dimostrazione
Si supponga, per assurdo, che esista una funzione totale computabile gs tale che S =
Ig . Allora si può effettivamente enumerare i0 = gstty, ii = gs(l ), ... e perciò M ,
M , ... e è la funzione totale calcolata dalla MT M. .). Per
ipotesi, la sequenza {ft t , fi. , ...} contiene tutte le funzioni totali computabili.
Si definisca ora la funzione h nel modo seguente:
h(x) =Mx) + 1
Chiaramente h(x) risulta computabile e totale poiché, per ogni x, ftx(x) è definita e
computabile. Esiste quindi un i e S tale che ft, = h (per il quale, cioè,/?1, è identica a
h). Ciò implica, tuttavia, che y?,(z) = h(i) e h(i) + 1 per definizione: ancora
una volta, una contraddizione sulla “diagonale” <indice della MT, dato su cui essa
opera>.

Analizziamo l’impatto del Teorema 8.10 sulle capacità degli strumenti di calcolo.
Intuitivamente esso stabilisce che tutte le funzioni totali computabili non sono
ricorsivamente enumerabili (mentre le funzioni parziali computabili lo sono). Si
noti che l’insieme Stot = {x|/i è totale} soddisfa Tipotesi del Teorema 8.10, quindi
non è ricorsivamente enumerabile; tuttavia l’enunciato del teorema comprende
anche altri insiemi contenuti in Slol: infatti, un insieme soddisfa tale ipotesi se
contiene almeno un indice per ogni funzione computabile totale. Stot è dunque il
massimo insieme fra tutti quelli che soddisfano l’ipotesi del teorema.
La risoluzione automatica dei problemi 335

Il Teorema 8.10 rende particolarmente difficile l’obiettivo di definire formalismi


che definiscano tutte e sole le finizioni computabili totali. Infatti, un formalismo
che consentisse un’effettiva enumerazione di algoritmi per calcolare tutte le
funzioni computabili totali, permetterebbe anche un’effettiva enumerazione delle
MT che definiscono tutte le funzioni totali computabili. Ciò tuttavia contraddice il
Teorema 8.10. Al solito il riferimento alle MT non è essenziale per i nostri
ragionamenti che possono essere ugualmente applicati a qualsiasi altro formalismo
di calcolo come un linguaggio di programmazione: come abbiamo già osservato, il
passaggio chiave non è il particolare formalismo adottato, bensì la sua equivalenza
con le MT e la possibilità di enumerarne ricorsivamente gli elementi.
Possiamo quindi concludere che non esiste un formalismo in grado di definire
tutte e sole le funzioni calcolabili totali. Analizzando, infatti, i formalismi introdotti
nel Capitolo 4, possiamo osservare che gli AF definiscono funzioni totali, ma non
tutte, mentre le MT definiscono tutte le funzioni calcolabili, ma anche quelle non
totali. Se consideriamo i linguaggi di programmazione, come ad esempio il C,
possiamo osservare che questi ci permettono di programmare qualsiasi algoritmo,
ma anche quelli che non terminano. Potremmo invero restringere il C in modo che
esso contenga solo programmi che sono garantiti terminare a priori, ad esempio
restringendo opportunamente i tipi di istruzioni cicliche ammessi, ed eliminando il
go-to e la ricorsione; in tal caso però oltre ai programmi che potrebbero non
terminare per qualche dato di ingresso, rimarrebbero esclusi altri programmi che
invece terminano per ogni dato di ingresso.
Dal Teorema 8.10 otteniamo il seguente corollario, la cui dimostrazione viene
lasciata al lettore come esercizio.
Corollario 8.11
Le funzioni ricorsive primitive sono un sottoinsieme proprio delle funzioni
computabili totali.

Presentiamo ora un altro risultato negativo relativo alle funzioni computabili totali:
il teorema seguente dimostra che non è possibile “sbarazzarsi” delle funzioni non
totali introducendo qualche “trucco” matematico. In matematica infatti lo studio di
funzioni parziali spesso non riveste grande interesse perché è sempre possibile
estendere una funzione parziale definendola in modo convenzionale anche laddove
originariamente non definita. Ad esempio, se estendiamo una funzione parziale,
arricchendo N con il valore {±} oppure attribuendole un valore predefinito in N
ogni volta che essa risulta indefinita, trasformiamo la funzione da parziale a totale,
ma nel passaggio possiamo perdere la sua computabilità. Formalmente, una
funzione h è detta estensione di un’altra funzione g se, ogni volta che g(x) è
definita, così è anche h(x) e risulta h(x) = g(x).
336 Informatica teorica

Teorema 8.12
Non esiste una funzione totale e computabile h che sia un’estensione della seguente
funzione

g(x) = if/X*) * -L then/Xx) + 1 else 1


' ■
Dimostrazione
Come al solito, si supponga che l’enunciato del teorema sia falso. Allora h =f per
qualche i. Tuttavia, se h è totale, /z(z) è definita. Quindi /z(z) e /z(z) = g(z)
+ 1 : una contraddizione.

Il Teorema 8.12 mostra formalmente che non è possibile ricorrere al “trucco” di
estendere una funzione parziale in una totale. Tale estensione può infatti provocare
uno spiacevole effetto collaterale: una funzione computabile può diventare non
computabile.
Come è noto, l’insieme delle funzioni totali computabili è incluso strettamente
nell’insieme delle funzioni computabili, il quale, a sua volta, è incluso strettamente
nell’insieme di tutte le funzioni. Analogamente, gli insiemi ricorsivi sono inclusi
nell’insieme potenza di N (il Teorema 8.10 fornisce un esempio di un insieme non
ricorsivamente enumerabile). Si cercherà ora di stabilire se gli insiemi ricorsivi
siano propriamente inclusi negli insiemi ricorsivamente enumerabili. Viene in
aiuto, a questo proposito, il seguente teorema, che consente una visione più ampia
sugli insiemi ricorsivamente enumerabili.
Teorema 8.13
Un insieme 5 è ricorsivamente enumerabile se e solo se
(a) S = Dh per qualche funzione parziale computabile 7z; ossia 5 = {x| 7z(x)A±}
oppure
(b) S-Ig per qualche funzione parziale computabile g.

Dimostrazione della parte (a)
i. Parte relativa al “se ”
Se h(x) è indefinita per ogni x, allora S = 0 e quindi è ricorsivamente
enumerabile. Altrimenti, sia k un valore per cui h(K) risulti definito e si prenda
un z tale che h = f. Si definisca la funzione h mediante il seguente
procedimento.
1. Si stabilisca la (effettiva) biiezione d'. (N - {0}) X N —definita da:
,, , (x + y)(x + y - 1)
a (x, y) =------------------------- + x - 1
2

i
La risoluzione automatica dei problemi 337

2. Per un dato n, dapprima si calcoli l’unica coppia (x, y) tale che n = cl(x, y).
Successivamente si simulino non più di y mosse di M, con ingresso x. Se la
computazione termina entro s <y mosse, allora si ponga h (w) = x,
altrimenti si ponga h (ri) = k.
In virtù di questa definizione, h risulta chiaramente computabile, in quanto
la precedente procedura è effettiva e totale. Inoltre I~- Dh, per la
definizione di h .
ii. Parte relativa al “solo se ”
Se 5 = 0 allora sia h(x) indefinita per ogni x. Altrimenti 5 = Ig, dove g è una
funzione totale computabile.
Si definisca h per mezzo della seguente procedura. Per ogni x, si enumerino
g(0), g(l), ..., g(z), Se si trova un i tale che g(z) = x allora h(x) = 0.
Chiaramente h è computabile ma la procedura precedente può non terminare
mai, quindi h è parziale. Inoltre Dh = Ig.
Dimostrazione della parte (b)
i. Parte relativa al “se ”
È simile alla corrispondente di (a) ed è quindi lasciata al lettore per esercizio.
ii. Parte relativa al “solo se ”
La dimostrazione è banale, in quanto una funzione totale non è altro che un
caso particolare di funzione parziale.

Si noti che, come nel caso del Teorema 8.9, la dimostrazione del Teorema 8.13 non
è costruttiva poiché essa porta solo a concludere l’esistenza di una funzione h , che
garantisce la semidecidibilità di S, ma non fornisce un meccanismo per costruirla:
per ottenerlo sarebbe necessario un algoritmo per stabilire se h(x) è indefinita per
ogni x, ciò che non rientra tra le ipotesi del teorema.
Grazie al Teorema 8.13, si può far corrispondere una funzione
“semicaratteristica” ad ogni insieme 5 ricorsivamente enumerabile. La funzione c$
viene definita come:
Ci(x) = if x e 5 then 1 else 0 or ±
Per la precisione, la formula precedente non è una definizione della funzione
semicaratteristica, quanto piuttosto una sua proprietà. cs-(x) deve valere 1 se x è
contenuto in S, mentre può valere 0 o risultare indefinito se xeS. Quindi, gli
insiemi ricorsivamente enumerabili o semidecidibili hanno (almeno) una funzione
semicaratteristica computabile.
Giunti a questo punto, siamo in grado di mostrare un insieme semidecidibile
che non è decidibile:

K= {x|/(x)^L}
338 Informatica teorica

Infatti, la funzione h definita da h(x) =fix(x) è chiaramente computabile, quindi K =


Dh è semidecidibile. D’altra parte, la funzione caratteristica di S è
c.s(x) = iffx(x) ± then 1 else 0

che, come è noto dal Corollario 8.7, risulta non computabile.

ESERCIZI
8.25 Si mostri che i linguaggi accettati dalle MT sono insiemi ricorsivamente
enumerabili ma, generalmente, non sono ricorsivi. Per un suggerimento si
veda il Teorema 8.17.
8.26 Si mostri che, se S = Ig per qualche funzione g totale e monotona, allora S è
ricorsivo (g è monotona se x <y implica g(x) < g(y)).
8.27 L’affermazione dell’Esercizio 8.25 vale nel caso in cui g è debolmente
monotona (ossia x <y implica g(x) < g(y))?
8.28 L’affermazione dell’Esercizio 8.25 vale se g è monotona ma non totale
(ossia, se x < y e se g è definita sia per x che per y, vale g(x) < g(y))?

8.7. 21 teoremi di Kleene e di Rice


In questa sezione vengono presentati due teoremi fondamentali della teoria della
computazione: il Teorema di Kleene del punto fisso ed il Teorema di Rice. Per
quanto riguarda il primo di essi, non analizzeremo qui il suo significato intuitivo,
ma lo utilizzeremo semplicemente come un lemma per la dimostrazione del
teorema di Rice e come un utile esercizio (tecnico) per il lettore.
Teorema 8.14 (Teorema di Kleene del punto fisso)
Sia t una qualunque funzione totale e computabile. Allora è sempre possibile
trovare un intero p tale che

fp ~f«p)
La funzione fp è chiamata punto fisso di t. perché t trasforma (una macchina che
calcola) fp in (una macchina che calcola) fp stessa.

Dimostrazione*
Sia u un qualsiasi numero naturale. Si definisca una MT che realizza la seguente
procedura applicata al valore in ingresso x.
1. Calcola z =fu(u)
2. Quando la computazione di fu(u) si ferma, se ciò avviene, calcola fz(x), che non è
detto sia definito.
La risoluzione automatica dei problemi 339

Poiché la procedura precedente è effettiva, esiste una MT in grado di


implementarla. Inoltre è possibile costruire tale macchina e calcolare poi il suo
indice g(w) per ogni u, usando la solita enumerazione «F, introdotta nella Sezione
8.4.1. In questo modo si è definita una funzione g, che è totale (esiste, infatti, una
MT opportuna per ogni m, anche nel caso in cui ffu) non è definita) e computabile
(si può costruirla effettivamente). Di conseguenza, si è ottenuto una funzione
definita da:

fAu)(x) = if/„(w) -L then /A(u) (x) else 1

Si noti che g è totale, mentre f^uy non lo è necessariamente. Ora, sia t una
qualunque funzione computabile totale: poiché g è totale, la composizione t ° g è
totale e anche computabile (si veda TEsercizio 8.14).
Sia dunque v il numero di Godei di t ° g, cioè fv = t ° g. Utilizzando ora v al
posto di u nella costruzione precedente, si ottiene /^ =ffi(vy, poiché ffy) = t° g(v) è
definita. Quindi f^fx) =^(v)(x) per ogni x. Tuttavia/A(v) =/og(v). Quindi^ =f^V) e
g(v) è un punto fisso di t.

Sfruttando ora il Teorema 8.14, possiamo enunciare e dimostrare il Teorema di
Rice, che è un risultato fondamentale della teoria della computazione.
Teorema 8.15 (Teorema di Rice)
Sia F un insieme qualunque di funzioni computabili. L’insieme S = {x| /reF} degli
indici di MT che calcolano le funzioni di F è ricorsivo se e solo se F = 0 oppure F
è l’insieme di tutte le funzioni computabili.

Dimostrazione
Per assurdo, si supponga che 5 sia ricorsivo, che F # 0, e che F non coincida con
l’insieme di tutte le funzioni computabili.
Si consideri ora la funzione caratteristica cs di S.
cfx) = iffxeF then 1 else 0
Per ipotesi assurda, c$ è computabile.
Sempre per ipotesi, enumerando effettivamente tutte le MT Mh si può trovare il
primo i e S per cui
(«) fi e F
ed il primo j £ S per il quale
(P) fj^F.
Dunque la seguente funzione è anch’essa computabile e totale.
(y) cs (x) = iffx£ F then i else j
Ora, per il teorema di Kleene, esiste un x tale che:
(8) 4(r> = h
340 Informatica teorica

Supponiamo che cs (x ) = i; allora, per (y), segue che f- £ F. Tuttavia, per (5), si
ha che f- = f e, per (a), si ha che f g F; cioè una contraddizione. Si supponga
invece che cs (T ) =j; allora, per (y), risulta f- g F. Tuttavia, per (5), si ha che f- =
fi, e, per ([3), si ha che fiiF’, ottenendo di nuovo una contraddizione.

Il teorema di Rice ha un forte impatto pratico negativo; infatti, intuitivamente,
afferma che, in tutti i casi non banali, S non è decidibile. Ad esempio, si ponga F =
{g}, ossia F sia composto solo da una singola funzione g. Per il teorema di Rice,
non è possibile decidere se una data MT possa, o meno, calcolare g. Tuttavia, per la
tesi di Church, il risultato non è ristretto alle MT e al formalismo delle funzioni.
Non è quindi possibile stabilire, per mezzo di un algoritmo, se un dato algoritmo
sia in grado di risolvere un determinato problema, né se due programmi siano
equivalenti (ossia calcolino la medesima funzione). Si noti che quest’ultima
considerazione coincide con il risultato dell’Esercizio 8.19 (la funzione g2 non è
computabile). Un simile risultato non era facile da dimostrare senza l’aiuto del
teorema di Rice, mentre ora è immediato ricavarlo.
Come ulteriore esempio per illustrare la potenza del teorema di Rice nella
dimostrazione dell’irrisolvibilità dei problemi, si consideri l’Esercizio 8.18.
L’insieme delle funzioni costanti chiaramente soddisfa l’ipotesi del Teorema 8.15;
l’insieme dei loro indici non è perciò ricorsivo, da cui si ricava che la funzione g,
definita nell’Esercizio 8.18, non è computabile. Il lettore è invitato a paragonare
questa semplicissima dimostrazione con la sua dimostrazione e con la traccia di
soluzione fornita alla fine del capitolo.
Il grande impatto pratico del Teorema di Rice deriva dal fatto che il concetto
di sottoinsieme F di funzioni computabili è un’espressione formale del concetto
generale di proprietà di problemi risolvibili: una proprietà degli elementi di un
insieme è un sottoinsieme dell’insieme dato e una funzione computabile è una
formalizzazione del concetto di problema risolvibile; quindi il Teorema di Rice
afferma che non è possibile stabilire se un determinato algoritmo risolve un
problema pur risolvibile che goda di una qualsiasi proprietà non banale (le uniche
proprietà banali in questo caso sono l’appartenenza del problema a una categoria di
problemi inesistente o coincidente con tutti i problemi risolvibili).
Gli esempi citati precedentemente sono infatti solo alcuni degli innumerevoli
esempi di problemi la cui indecidibilità discende banalmente dal Teorema di Rice.
Allo scopo di comprendere meglio come applicare tale teorema nella pratica si
considerino anche i seguente esempi.

Esempio 8.8
Vogliamo analizzare la decidibilità del problema di stabilire se un generico
programma, scritto in un qualsiasi linguaggio di programmazione, calcoli la
funzione trigonometrica sin(x) con un’approssimazione migliore di IO-3.
La risoluzione automatica dei problemi 341

Sfruttando il Teorema di Rice, possiamo facilmente osservare che tale problema


non è decidibile. Infatti, una volta stabilita una qualsiasi convenzione per
rappresentare in forma approssimata i numeri reali, Tinsieme delle funzioni che
approssimano sin(x) ameno di 10 3 non è né Tinsieme vuoto né Tinsieme universo.
Quindi, in base al Teorema di Rice, non è decidibile se un programma calcoli una
funzione appartenente a tale insieme o no.

Esempio 8.9
Analizziamo ora la decidibilità del seguente problema: stabilire se, dati tre generici
programmi Pb P2, P3, che calcolano rispettivamente le funzioni f\, f2, fi, definite
sull’insieme dei naturali, risulta/ì(x) =fifx) +fi(x), Vx e N.
Per rispondere a tale quesito risulta conveniente esaminare un caso particolare,
fissando J2 ed/b ad esempio, pari alla funzione identità: fi(x) =fi(x) = xVx e N. Il
problema diventa allora decidere se /i(x) = 2x, Vx e N. Applichiamo quindi il
Teorema di Rice: sia F un insieme di funzioni computabili che contiene la sola
funzione fi e sia S l’insieme degli indici delle MT che calcolano funzioni in F. Se
x0 sia il numero di Godei della MT che implementa Pi, il problema si traduce allora
nel chiedersi se xo e S. Tale problema è certamente indecidibile, perché F 0
(contiene almeno/1) e F non è l’insieme di tutte le funzioni computabili (ad es./(x)
= x2 non appartiene ad F).
Poiché se anche vengono fissatef2 sfi il problema di stabilire se/i(x) =ffx) +
fi(x), Vx e N, è indecidibile, a maggior ragione lo sarà il caso con f2 e fi
“incognite”. Infatti, se il problema iniziale fosse decidibile, lo sarebbe anche il
problema di stabilire se fa(x) = 2x, Vx e N, essendo un caso particolare del
problema dato. E bene sottolineare che il problema in questione non è stabilire se
una funzione fa coincida con la funzione 2x, bensì se un determinato algoritmo,
codificato in Pb calcoli 2x.

ESERCIZI

8.29 Si dimostri la irrisolvibilità del problema di stabilire se un dato programma


(MT ecc.) sia in grado, o meno, di calcolare ima funzione ricorsiva primitiva.
8.30 Si dimostri l’irrisolvibilità del problema di stabilire se il dominio di
definizione di una funzione computabile sia, o meno, finito.
8.31 Si dica, giustificando la risposta, quali delle seguenti affermazioni sono vere:
- Il problema di stabilire se due programmi C calcolano la stessa funzione
è decidibile.
- Il problema di stabilire se due programmi C, di cui è nota a priori la
terminazione per ogni valore dei dati in ingresso, calcolano due funzioni
diverse è semidecidibile.
342
Informatica teorica

- Il problema di stabilire se le funzioni calcolate da due programmi C, di


cui è noto a priori che hanno identico dominio di definizione, siano
diverse è semidecidibile.

8.8 Riflessioni sulla irrisolvibilità dei problemi


Generalmente, il primo passo nello studio dell’informatica consiste nel progetto di
algoritmi per semplici problemi: lo studente impara l’arte di progettare algoritmi
cercando di trovare opportune soluzioni per il calcolo di semplici elaborazioni
numeriche o simboliche.
Bisogna però precisare che l’attività di progetto di algoritmi può anche
risultare, in alcuni casi, un processo senza fine. Infatti, finché un programmatore
affronta problemi come l’ordinamento o la ricerca aH’intemo di un vettore, noi
sappiamo (anche se il programmatore potrebbe non saperlo!) che, se non riesce a
trovare alcuna soluzione algoritmica, ciò è attribuibile unicamente alla sua scarsa
esperienza. Tuttavia, se il programmatore stesse affrontando il problema di stabilire
se due qualunque programmi Pascal calcolano la medesima funzione, noi sappiamo
(ma il programmatore potrebbe non saperlo!) che il suo lavoro non avrebbe alcuna
speranza di riuscita.
Ora, se si è già a conoscenza della risolvibilità o irrisolvibilità di un problema,
si può decidere immediatamente se sia un obiettivo ragionevole cercare un
algoritmo per risolverlo. Tuttavia, quando si sta affrontando un nuovo problema
(ossia ci si trova nella medesima situazione del programmatore inesperto), c’è
sempre il rischio di perdersi in un lavoro senza prospettive di riuscita, cercando di
trovare ima soluzione algoritmica per un problema indecidibile. In questi casi, è
essenziale stabilire se il problema sia risolvibile o meno. Questo, a sua volta, è un
problema irrisolvibile (ossia non si riuscirà mai a stabilire meccanicamente se un
problema sia decidibile o meno). Bisogna contare solo sul proprio intuito e sulla
propria esperienza. In conclusione, la risoluzione di problemi passa spesso
attraverso il seguente processo iterativo:
1. Si cerca di trovare un algoritmo per risolvere il problema dato.
2. Dopo un certo tempo, se non si riesce a trovare un algoritmo opportuno, si può
sospettare che un simile algoritmo non esista.
3. In tal caso si cerca di dimostrare la sua irrisolvibilità.
I passi 1., 2. e 3. vengono ripetuti fino a che non si trova un algoritmo opportuno,
oppure si riesce a dimostrare che il problema è irrisolvibile, oppure si è stufi e si
decide di smettere (si noti che l’ultima alternativa è essenziale per poter
sopravvivere).
Il programmatore esperto spesso riesce a trovare un algoritmo per risolvere un
problema sulla base della propria esperienza precedente, sempre che esista un
simile algoritmo e che il problema non sia troppo difficile. Analogamente, esistono
La risoluzione automatica dei problemi 343

alcuni schemi di dimostrazione generali e ampiamente applicabili che si possono


usare per dimostrare l’irrisolvibilità di un problema.
Il lettore attento avrà probabilmente già identificato un certo numero di
schemi di dimostrazione generali che sono stati usati estesamente nelle
dimostrazioni di indecidibilità nelle precedenti sezioni. Si è già commentata la
potenza del teorema di Rice, come strumento per le dimostrazioni di irrisolvibilità.
Altri schemi classici di dimostrazione si possono classificare come dimostrazioni
diagonali introdotte nella Sezione 8.5. Se esistono difficoltà nella computazione di
una funzione g(x,y), esse appaiono con più probabilità sulla diagonale (ossia
quando x = y). Ad esempio, si sono viste dimostrazioni diagonali per il Teorema
8.6, il Corollario 8.7 e il Teorema 8.8 ecc.
I metodi di dimostrazione diagonali sono i più classici per la dimostrazione
dell’irrisolvibilità in maniera diretta. La dimostrazione è sempre una reductio ad
absurdunr. se il problema fosse risolvibile, sorgerebbero alcune contraddizioni, e
ciò è probabile che si verifichi sulla diagonale. La dimostrazione in modo diretto
della irrisolvibilità dei problemi irrisolvibili può però trasformarsi in un’impresa
ciclopica in quanto la letteratura e la pratica presentano una quantità
incredibilmente grande di problemi irrisolvibili. Occorre perciò dotarsi di altri
strumenti per stabilire l’eventuali irrisolvibilità di un problema senza dover ogni
volta “ripartire daccapo”.
Ad esempio, traiamo spunto dal fatto che la verifica meccanica della
terminazione di un programma è impossibile: abbiamo già osservato che in linea di
principio questa conclusione può essere raggiunta applicando lo stesso
ragionamento utilizzato per la dimostrazione del Teorema 8.6: enumerare
algoritmicamente tutti i programmi del linguaggio e tutti i possibili dati che si
possono fornire in ingresso a tali programmi (ben più riccamente strutturati delle
MT e dei loro dati); considerare la “funzione universale” dell’v-esimo programma
con ingresso l’x-esimo dato ecc. Tuttavia, è molto più semplice giungere alla stessa
conclusione basandosi sul fatto che ogni MT può essere simulata da un programma
per calcolatore e viceversa, e quindi lo possibilità di decidere la terminazione di un
programma implicherebbe anche la possibilità di decidere la terminazione del
calcolo di una MT.
Il metodo di dimostrazione indiretto usato in questo caso non è quindi una
ripetizione, magari concettualmente non originale ma sicuramente eccessivamente
dettagliata e inutilmente noiosa dell’originaria tecnica diagonale, ma introduce
invece una tecnica importante e molto potente, consistente nel dimostrare che
l’esistenza di una soluzione per il problema sotto esame implicherebbe la
risolvibilità di un altro problema di cui è nota l’irrisolvibilità. Più in generale, la
tecnica di riduzione cerca di ricondurre la soluzione di un problema alla soluzione
di un altro problema la cui risolvibilità o irrisolvibilità sia già nota (o più
facilmente dimostrabile). Se si riesce a trovare un metodo algoritmico per costruire
una soluzione del problema P partendo da una soluzione del problema P', si può
dedurre che:
344 Informatica teorica

1. Se P' è risolvibile, lo è anche P.


2. Se P è irrisolvibile, lo è anche P’.
Qualche esempio, come al solito, aiuterà a chiarire la tecnica di riduzione dei
problemi.

Esempio 8.10
Si considerino il Teorema 8.6 ed il Corollario 8.7. Essi affermano, mediante
dimostrazioni indipendenti, sebbene simili, che le due funzioni g e k,
rispettivamente definite da g(x, y) = iffy(x)^E then 1 else 0 e da k(x) = iffx(x) ±
then 1 else 0, non sono computabili. Intuitivamente, la prima affermazione sembra
più generale della seconda. Non può quindi accadere di saper risolvere il problema
generale se non si è in grado di risolvere quello particolare. Formalmente, ciò
significa che sarebbe possibile derivare facilmente un algoritmo in grado di
calcolare k, partendo da un algoritmo che calcola g (ossia k viene ridotto a g
semplicemente ponendo y = x).
Questo fatto rende la dimostrazione del primo teorema una conseguenza banale del
secondo. Dapprima bisogna dimostrare il Corollario 8.7; successivamente si
deduce il Teorema 8.6 osservando che la computabilità di g implicherebbe la
computabilità di k. In un certo senso, come quindi anticipato nella Sezione 8.5.1, il
Corollario 8.7 diventa il teorema (o lemma) principale.
• ■
Il prossimo risultato mostra l’indecidibilità del problema di stabilire se l’immagine
di fx, per qualche x, sia vuota. Questo risultato viene ottenuto riducendo il problema
originario a quello di stabilire se, fissato un numero z0, dato un generico x, z0 e p.
Quest’ultimo, a sua volta, è un problema indecidibile (si veda l’Esercizio 8.20).
Teorema 8.16
Il problema di stabilire, per un generico x, se 11. = 0 è indecidibile.

Dimostrazione
Si scelga un qualunque valore prefissato z0. Per un generico x, si calcoli un y tale
che fx(z) Zo implichi fy(z) = E,fx(z) = z0 implichi fy(z) = z0. Chiaramente,^ risulta
computabile ed y può essere effettivamente calcolato sulla base di una MT che
calcola^. Quindi I = 0 se e solo se z0 £ I f : se si fosse in grado di risolvere il
primo problema si potrebbe risolvere anche il secondo.

Si noti che il Teorema 8.16 si potrebbe anche dedurre come semplice conseguenza
del teorema di Rice (Teorema 8.15). Infatti, l’insieme delle funzioni computabili
totalmente indefinite non è né vuoto né coincidente con l’insieme di tutte le
funzioni computabili (in realtà è un “singleton”).
La risoluzione automatica dei problemi

Esempio 8.11
Si consideri il problema di decidere se durante l’esecuzione di un generico
programma P si acceda ad una variabile non inizializzata (trattasi di un ben noto
caso di “errore a run time”). Dimostriamo che tale problema non è decidibile,
riducendo ad esso il problema della terminazione di P come segue. Dato un
generico P ’ che riceve in ingresso generici dati D, costruisco un P come segue:
begin
var x, y: ...
P’;
y := x
end
avendo cura di usare identificatori x e y che non sono usati in P
L’assegnamento y : = x produce un acesso ad x che non è inizializzata perché x
non compare in P’. Quindi l’accesso ad una variabile non inizializzata accade in P
se e solo se P ’ termina. Allora se fosse possibile decidere il problema dell’accesso
ad una variabile non inizializzata, saprei decidere anche il problema della
terminazione del calcolo, che è assurdo.

Consideriamo ora un esempio che ci permette di riflettere su come si applica
correttamente la tecnica di riduzione, sottolineando un tipico errore in cui si può
cadere utilizzandola.

Esempio 8.12
Si consideri il problema di stabilire se, dato un generico sottoprogramma P, che ha
un parametro par di tipo intero e ritorna un intero, e dato un valore in input x, vale
P(x) = x.
Si consideri ora il seguente tentativo di dimostrazione di indecidibilità del
problema in questione, che fa uso della tecnica di riduzione: “A partire da un
generico sottoprogramma P che ha un intero come parametro e restituisce un
intero, costruiamo un sottoprogramma P' (utilizzando una sintassi ispirata a
quella del Pascal) che, a fronte di un input x, si comporta nel seguente modo:
procedure P'(x:integer)
begin
if (P(x) <> x)
while(1)
write("loop");
end.
Il sottoprogramma P' quindi termina su input x se e solo se P (x) = x. Poiché il
problema di stabilire se un programma C termina a fronte di un generico input non
è decidibile, allora non è decidibile neanche il problema di stabilire se P (x) =
346 Informatica teorica

Tale “dimostrazione” non è corretta e rappresenta un tipico esempio di cattiva


applicazione della tecnica di riduzione, infatti, parte dal “problema ignoto” P e lo
riduce ad un problema noto (ed indecidibile) P ', che non è quindi di aiuto per la
dimostrazione di indecidibilità di P.
Una corretta applicazione della tecnica di riduzione è invece la seguente. Dato
un generico sottoprogramma P' si costruisca il seguente sottoprogramma P:
function P(par: integer): integer
begin
P' (par) ;
result := par;
end.
Questo programma è tale che P ( x ) = x se e solo se P ’ termina la sua
computazione sull'input x. Questo problema è indecidibile, quindi lo deve essere
anche quello originario, o si ha una contraddizione.

La tecnica presentata nell’Esempio 8.11 e nella versione corretta deH’Esempio 8.12
può essere applicata in modo generale per dimostrare T indecidibilità di molte altre
proprietà dei programmi durante la loro esecuzione, quali ad esempio la presenza
di indici di array fuori dai limiti, la divisione per 0, .. .tutti tipici casi di errori a run
time.
Si osservi inoltre che verificare la presenza di errori nei programmi durante la
loro esecuzione è un problema non decidibile, ma semidecidibile.
Concettualmente5, infatti, se vogliamo verificare la presenza di un certo errore
durante l’esecuzione di un programma P, possiamo eseguire P sui diversi ingressi e
se si manifesta Terrore che stiamo cercando, possiamo rispondere positivamente
alla domanda iniziale. Siccome però Tinsieme dei possibili dati del programma è
infinito, qualsiasi esecuzione corretta del programma su un numero finito di casi,
non permette di concludere che il programma è esente da errori: otteniamo quindi
la conferma formale della celebre affermazione di E.W. Dijkstra (già citata
nell’appendice del Capitolo 7) “la verifica di un programma mediante testingpotrà
solo dimostrare la presenza di errori, mai la corretteza, ossia l’assenza di errori.”
Occorre però una precisazione relativamente alla procedura suddetta per
rilevare la presenza di errori: che cosa accade se, eseguendo P su un dato x la
computazione di P non termina? Per evitare questo tipo di problema è conveniente
procedere nel seguente modo, ripercorrendo lo schema di dimostrazione del
Teroma 8.13 (parte (a), punto 2). Cosideriamo una matrice (infinita) in cui il
numero di riga rappresenta il numero di passi eseguiti durante l’esecuzione di P e
le colonne i possibili ingressi. La matrice viene quindi (costruita e) visitata
diagonalmente in modo da evitare di perdersi in computazioni che non terminano e
da trovare un errore qualora esso si verifichi.

5 In questo caso non ci preoccupiamo di “quanto sforzo di testing” dovrebbe essere


esercitato per mettere in pratica questa procedura.
La risoluzione automatica dei problemi 347

Si osservi che, poiché verificare la presenza di un dato errore durante l’esecuzione


di un programma P è, in generale, un problema solo semidecidibile, il suo
complemento, cioè verificare l’assenza del medesimo errore (ovvero la correttezza
di un programma rispetto all’errore dato) non solo non è decidibile, ma non è
neanche semidecidibile. Infatti, il Teorema 8.9 stabilisce che se un problema e il
suo complemento sono entrambi semidecidibili, allora sono anche entrambi
decidibili.
Questo modo di procedere può essere utilizzato in modo sistematico per
dimostrare che un problema che non è decidibile non è neanche semidecidibile,
mostrando che è invece semidecidibile il suo complemento. Per chiarire meglio il
funzionamento di questo schema di ragionamento, consideriamo il seguente
esempio.

Esempio 8.13
Analizziamo la decidibilità del problema di stabilire se, data una generica MT M, il
linguaggio riconosciuto da M è vuoto. Tale problema non è decidibile per il
teorema di Rice; infatti l’insieme delle MT che riconoscono il linguaggio vuoto
non è l’insieme vuoto, né è Tinsieme di tutte le MT.
Rimane ora da analizzarne la semidecidibilità, ricordando che, poiché il
problema non è decidibile, al più uno tra esso e il suo complemento può essere
semidecidibile. In questo caso, il problema non è neppure semidecidibile, in quanto
è semidecidibile il suo complemento, cioè il problema di stabilire se, data una
generica MT M, il linguaggio riconosciuto da M contiene almeno una stringa.
Infatti, enumerando opportunamente le stringhe in ingresso, con la solita tecnica
diagonale è possibile simulare le esecuzioni della MT in modo che, se una stringa
viene accettata, prima o poi questa viene trovata; in caso contrario, la
computazione prosegue all’infinito.

Concludendo, siamo ora in possesso di un potente insieme di strumenti per la
dimostrazione dell’irrisolvibilità dei problemi, e precisamente le dimostrazioni
diagonali, la riduzione dei problemi, ed un insieme di enunciati fondamentali come
il teorema di Rice. Siamo anche in grado di distinguere tra i vari problemi
indecidibili quelli semidecidibili da quelli che non godono neanche di questa
parziale proprietà: il caso <presenza-assenza> degli errori a run-time dei
programmi ne è uno tra gli esempi più significativi.

ESERCIZIO
8.32 Si dimostri Tirrisolvibilità dei problemi proposti negli esercizi dal 8.17 al
8.22, usando la tecnica di riduzione dei problemi e/o applicando il teorema di
Rice.
348 Informatica teorica

Giova infine rammentare (si veda la Sezione 8.5.1) che un caso particolare di
scorretta inversione della tecnica di riduzione di problemi è l’uso improprio della
<specializzazione-generalizzazione>: così come in realtà il Corollario 8.7 è in
realtà un lemma del teorema principale, alla stessa maniera, non è detto che
restringendo un problema indecidibile a qualche caso particolare, esso rimanga
tale.
Ad esempio, è ora ben chiaro che è stato possibile determinare
algoritmicamente se un programma termina, per un; dato valore di ingresso.
Abbiamo tuttavia osservato che se si restringe l’attenzione alla classe dei
programmi (ad esempio in linguaggio Pascal) che non impiegano le istruzioni
goto, while e repeat, né le chiamate ricorsive di procedure, allora è possibile
garantire la terminazione di tali programmi per qualunque valore di ingresso. Si
possono costruire programmi con queste caratteristiche per la risoluzione di molti
problemi utili e non banali. Ad esempio, molti problemi numerici sono risolvibili
da algoritmi che impiegano solo i cicli for. Analogamente, è possibile garantire a
priori che le funzioni ricorsive primitive sono funzioni totali.
In generale quindi, un problema irrisolvibile può diventare risolvibile, in modo
più o meno banale, restringendolo a opportuni casi particolari: la dimostrazione di
indecidibilità di un problema non deve perciò essere considerata un ostacolo
insormontabile e comportare necessariamente la rinuncia ad affrontarne la
soluzione con l’ausilio del calcolo automatico. Il problema del riconoscimento del
linguaggio fornisce, a questo proposito, alcuni esempi interessanti, che verranno
passati in rassegna nella prossima sezione.

8.9 Problemi risolvibili e irrisolvibili relativi ai linguaggi


In questa sezione sezione vengono analizzati molti problemi, risolvibili e
irrisolvibili, relativi ai linguaggi. Si considerino dapprima le MT come
riconoscitori di linguaggi. E immediato derivare il seguente teorema.
Teorema 8.17
Non si può decidere se x e L(M) per una qualsiasi MT M ed una qualsiasi stringa
x e VT*.

Dimostrazione
La dimostrazione del Teorema 8.17 è immediata, una volta ricordate le annotazioni
sulla codifica dei problemi illustrate precedentemente. Infatti, si supponga che il
problema x e L(M) sia decidibile. Sia {Mt} un’enumerazione effettiva delle MT
che riconoscono linguaggi in VT* e sia {x,} un’enumerazione effettiva delle
stringhe di VT*. Si osservi che non vi è perdita di generalità nel supporre che
ciascuna M, si arresti, ricevendo in ingresso x, solo se x e Infatti, è facile
restringere l’enumerazione {M,} alle MT che non si fermano mai se x £ L(M-).
La risoluzione automatica dei problemi 349

Quindi, decidere se o meno x, e L(Mj) equivale a decidere o meno se Mj si arresta


ricevendo la stringa di ingresso xt. In ogni caso, la soluzione non può essere decisa
da alcuna MT.

ESERCIZI

8.33 Si dia una dimostrazione diagonale diretta del Teorema 2.17, considerando il
linguaggio L = {x,| x, e L(M)}.
8.34 Si mostri che i seguenti problemi sono indecidibili.
1. Data una MT M, L(M) = 0?
2. Data una MT M, L(M) = VT*?
3. Data una grammatica G ed una stringa x e VT*, x e L(G)1
4. Data una grammatica G, L(G) = 0?
5. Data una coppia di grammatiche Gb G2, L(G)) c L(G2)?

Si osservi ora che i linguaggi riconosciuti dagli AF sono ricorsivi, al contrario dei
più generali linguaggi riconosciuti dalle MT. Infatti, per ogni stringa x e per ogni
AF A, è banale decidere se ò*(q, x) e F. Basta infatti far funzionare A fino a che 8
non risulta più definita (caso (a)) oppure la lettura di x viene completata (caso (b)).
La procedura di decisione si ferma con risposta affermativa nel caso (b), se A è in
uno stato finale. Altrimenti la risposta è negativa. Si ottiene quindi il seguente
enunciato.
Enunciato 8.18
I linguaggi accettati dagli AF sono ricorsivi (ossia, per gli AF, il problema
dell’appartenenza è decidibile).

ESERCIZIO
8.35 Si mostri che i linguaggi riconosciuti dagli AP sono ricorsivi. Suggerimento'.
si sfrutti il Teorema 8.11.

Come si è visto, anche il problema di stabilire se un dato linguaggio è vuoto risulta


indecidibile, se si considerano i linguaggi accettati da una qualunque MT
(Esercizio 8.34.1). Questo problema diventa decidibile quando si restringe
l’attenzione ai linguaggi regolari.
Teorema 8.19
È decidibile il problema di stabilire se, per un dato AF A, L(A) = 0.

La dimostrazione del teorema è una banale conseguenza del Teorema 4.1.
350
Informatica teorica

Si presenta ora un problema irrisolvibile relativo ai linguaggi formali, mediante un


esempio, peraltro non banale, della tecnica di riduzione.
Teorema 8.20*
Date due grammatiche non contestuali Gì e G2, non si può decidere se
Z(Gi) n Z(G2) = 0 per ogni coppia Gb G2.

Il Teorema 8.20 verrà dimostrato evidenziando come resistenza di un algoritmo in
grado di risolvere il problema precedente, implicherebbe la possibilità di trovare un
algoritmo per stabilire se L(M) = 0, per una qualsiasi MT M. Quest’ultimo
problema, a sua volta, è irrisolvibile (si veda l’Esercizio 8.34.1).
Per la dimostrazione di questo teorema è necessario premettere qualche
definizione ausiliaria.
Definizione 8.5
Sia M una MT a nastro singolo che accetti il linguaggio L(M) c VT*. Utia
computazione valida di Mè la stringa W]$w2A$W3$ w/ $... in cui:
- indica il riflesso della stringa w,-.
- $ £ A, dove A è l’alfabeto di M e VT c A, tale che, per ogni i, w, è
un’opportuna codifica di una configurazione di M, definita nel modo seguente:
se Ci = (q, xfix2) allora = Xi#r2. In breve, le stringhe w, vengono chiamate
configurazioni.
- Wi è una configurazione iniziale (ossia W/ = qyc, x e VT*).
- w„ è una configurazione di accettazione (ossia e A * • F • A*).
- W;1- mWì+i per ogni i, con 1 < i < n.

Quindi, per una generica stringa x g Vt* una computazione valida di M esiste con
Wi = q&c se e solo se x g L(M).
La dimostrazione del Teorema 8.20 si basa essenzialmente sul seguente lemma, il
quale afferma intuitivamente che la relazione di transizione fra due MT può essere
descritta da una grammatica non contestuale.
Lemma 8.21
Data una MT Afa nastro singolo, i linguaggi:
L = {x| x = y$zK, y z}, Lr = {x| x = j/$z, y *-M z}

sono non contestuali.



Dimostrazione del Lemma 8.21
Descriviamo una grammatica G, non contestuale, che genera L. La grammatica per
Lr si ottiene modificando G in modo molto semplice.
G è la 4-upla (A o Q o {$}, {5, T}, P, S), dove P si costruisce nel modo seguente:
La risoluzione automatica dei problemi 351

- Per ogni a e A, S aSa, I~>aTA sono contenuti in P.


- Per ogni q e Q e a e A.
o Se b(q, a) = {q , d, R) allora S qaTq'd appartiene a P.
o Se ò(q, a) = (q', d, S) allora S —> qaTaq’ appartiene a P.
o Se 8(^, a) = {q , a, L) allora per ogni b&A, S bqaTdbq' appartiene a
P.
- Z —> $ appartiene a P.
Per esempio, se una mossa di M consistesse in {q,abGb) *-M (q,dbc), G
presenterebbe la seguente derivazione:

S =$g dSa =$G abqH> Tcbq’a =>G abqH Scbq'a


La derivazione dell’esempio precedente dovrebbe bastare a convincere che
L(G) = L senza ulteriori spiegazioni.

Dimostrazione del Teorema 8.20
Si definiscano i linguaggi L2, L2 c (A* • Q-A*$)* nel modo seguente. Una stringa
x = x, $x2...xffl$, x,■ g A* • Q• A* è inLx (rispettivamente inZ2) se, e solo se:
- Xi >-M x,R +1 per valori dispari di i, 1 < i < m
- xlR'-MXi+\ per valori pari di i, 1< i < m
- x appartiene a Z2 solo se xi è una configurazione iniziale di M (ossia xi = qau,
ueVT*).
- se m è dispari (o, rispettivamente, pari) x appartiene a L\ (a Z2, rispettivamente)
solo se xm è una configurazione di accettazione (ossia xmeA* -F-A*).
Chiaramente, Lx n Z2 è l’insieme delle computazioni valide di M. poiché
x = xl$x2...x,w$ appartiene a Li n L2 se e solo se xi '-m xr '-m x3 ... Si noti che
Zi = (Z$)* ■ ({a} u/U-F-xP-S) e Z2 = qQ-• $ • (ZA$)* • ({a} u A* • F • A* •$),
dove L e LR sono i linguaggi del Lemma 8.21. Una volta ottenute le grammatiche
non contestuali che generano L e LR, è un semplice esercizio costruire due
grammatiche non contestuali G\, G2 in grado di generare rispettivamente Zi e Z2.
In conclusione, si è ottenuto un algoritmo che, per ogni MT M, costruisce due
grammatiche non contestuali Gì e G2 tali che Z(Gj) nZ(G2) è l’insieme delle
computazioni valide di M. Tuttavia, L(M) = 0 se e solo se l’insieme delle
computazioni valide di M è vuoto. Quindi, se il problema di stabilire se
Z(Gi) n Z(G2) = 0 fosse decidibile, lo sarebbe anche il problema di stabilire se
L(M) = 0: una contraddizione.
352
Informatica teorica

ESERCIZIO
8.36 Si dia il procedimento di costruzione di AP che riconoscono L e LR, secondo
la definizione del Lemma 8.21.

Tabella 8.1 Un riassunto dei problemi classici di decidibilità nella teoria dei
linguaggi formali.

Problema

Classe di linguaggio —— L— 0 L\ — L2
Accettati da MT I I I I
Regolari D D D D
Liberi da contesto D* D* I* I
Accettati da AP D D* I

D: decidibile
I: indecidibile

La Tabella 8.1 riassume e completa i risultati circa la decidibilità dei problemi


riguardanti i linguaggi formali analizzati in questa sezione. I risultati qui elencati
che non sono stati esplicitamente dimostrati, vengono lasciati al lettore come
esercizio, con l’avvertimento che il simbolo * contraddistingue gli esercizi che
richiedono una conoscenza approfondita della teoria dei linguaggi.

8.10 *La riducibilità dei problemi e i gradi di irrisolvibilità


Come si è visto, la riduzione dei problemi costituisce una tecnica molto potente per
stabilire la risolvibilità o irrisolvibilità di un determinato problema P. Inoltre, una
volta determinata la risolvibilità, questo metodo fornisce un’effettiva metodologia
per ottenere la soluzione, almeno in linea di principio. In questa sezione
approfomdiamo questa tecnica e le sue implicazioni. Si comprenderà in seguito, nel
Capitolo 9, come la medesima tecnica sia in grado di fornire risultati interessanti
anche al di fuori dell’ambito della determinazione della risolvibilità dei problemi.
Fra le diverse formalizzazioni possibili del concetto di problema, ne viene qui
scelta una basata sugli insiemi di interi. Un problema P verrà rappresentato da un
insieme SP. in questo modo la risolvibilità di P corrisponderà alla ricorsività di SP,
mentre la parziale risolvibilità di P corrisponderà alla enumerabilità ricorsiva di SP
e la irrisolvibilità di P corrisponderà alla non enumerabilità ricorsiva di SP.

6 Questo problema è rimasto aperto a lungo e solo recentemente è stato dimostrato


decidibile. Esso rappresenta perciò una vera sfida intellettuale “aggirabile” facendo ricorso
alle note bibliografiche.
La risoluzione automatica dei problemi 353

Prima di addentrarsi in una trattazione formale della nozione di riducibilità di un


problema, si analizzino, sulla base dell’intuito personale, i seguenti esempi. Per
semplicità, nel seguito si userà la notazione abbreviata Dx e Ix al posto di Df el f ,
rispettivamente, ossia per indicare il dominio di definizione e l’immagine della
funzione calcolata dalla x-esima MT.

Esempio 8.14
Si considerino i due insiemi
Si = {x| Dx è infinito}
52 = {x| Dx = M (ossia fx è totale)}
Come è noto dal Teorema 8.10, 52 non è ricorsivamente enumerabile. Si può
facilmente intuire che, in qualche modo, Si e S-, sono riducibili l’uno all’altro.
Infatti, si supponga di avere a disposizione un algoritmo per verificare se, per ogni
x,fx è totale (ossia, si supponga che S2 sia ricorsivo). Allora, per verificare se Dx è
infinito, si proceda nel modo seguente: si definisca una funzione h, totale e
computabile, tale che, per y = h(x'), Dx = Ix e tale che zi A z2 implichi /,(zi)A^(z2).
Ciò si può ottenere mediante una costruzione analoga a quella impiegata per h
nella dimostrazione del Teorema 8.13. In questo modo Dx risulta finito se e solo se
fy è totale.
D’altra parte, si supponga che esista un algoritmo per verificare se Dx è finito,
per ogni x. Allora, per verificare sefx è totale, si definisca una funzione h, totale e
computabile, tale che, per;y = h(x):
fy(z) = iffx(wy£l. per ogni w < z then 1 else ±
Quindi fx risulta totale se e solo se Dy è infinito. Poiché è effettivamente possibile
ottenere una simile funzione h, totale e computabile, ciò significa che si è riusciti
ad ottenere una procedura per decidere se /) è totale.
La costruzione precedente consente una dimostrazione della indecidibilità di
51, dopo aver stabilito l’indecidibilità di S2, ma anche il viceversa. La
dimostrazione è una reductio ad absurdum.
C’è un altro interessante punto di vista per interpretare questo risultato. Si
immagini di migliorare una MT con un dispositivo non algoritmico che sia in grado
di stabilire l’appartenenza ad 5b Si chiami tale dispositivo “oracolo’’'. Si può allora
impiegare una MT di questo tipo per risolvere anche il problema della
appartenenza di S2. Infatti, una tale MT potrebbe rispondere alla domanda
“x e S2?” nel modo seguente: dapprima si calcolerebbe y secondo la precedente
procedura; poi chiederebbe all’oracolo se y e 5i e uscirebbe la risposta per
risolvere il problema originario. Viceversa, un oracolo in grado di decidere
l’appartenenza di S2 si potrebbe usare anche per decidere l’appartenenza per 5b Si
può dunque affermare che lo stesso oracolo può essere impiegato per decidere sia
5i che 52.
354
Informatica teorica

Esempio 8.15
Si considerino i due insiemi:

5] = {x| Dx è finito}

Si può ridurre K a S,. Infatti, si supponga che un oracolo sia in grado di decidere il
problema di appartenenza per S). Il problema di appartenenza per K può essere
risolto mediante la seguente procedura. Si definisca, come al solito, una funzione
totale e computabile h tale che, per_y = /z(x):
fy(w) = if Mx, con ingresso x, non si ferma in meno di w mosse then 1 else ±
Quindi fx(x) risulta definita se e solo se Dy è finito.
Si vedrà in seguito che, in questo caso, la riduzione del problema non si può
applicare nell’altra direzione. Ciò conduce ad affermare, intuitivamente, che il
problema dell’appartenenza per K è “più risolvibile” del problema
dell’appartenenza per 5,. In breve: K è “più risolvibile” di S1. Simmetricamente, si
può affermare che è “meno risolvibile” di K. Infatti, si noti che K è
ricorsivamente enumerabile, sebbene non ricorsivo, mentre non lo è (si dimostri
quest’ultima affermazione per esercizio). Per usare altre parole, si potrebbe usare
un’oracolo in grado di decidere l’appartenenza per , anche per decidere
l’appartenenza per K, ma non viceversa. .

L’esempio precedente suggerisce l’opportunità di definire una misura del “grado di
(ir)risolvibilità”. Questa nozione viene qui presentata formalmente solo in uno dei
modi possibili, senza alcuna pretesa di completezza. Siano A e B due sottoinsiemi
di N e si definisca = N - ^4, B = N - B.
Definizione 8.6
A è riducibile a B (si indica A <r B) se e solo se esiste una finizione t, totale e
computabile, tale che, per ogni x, x e A se e solo se t(x) e B.

Ecco alcune proprietà della relazione <r.

Teorema 8.22
1. <r è riflessiva e transitiva.
2. A <rB implica A <r B .
3. Se A <rB e B è ricorsivo, allora A è ricorsivo.
La risoluzione automatica dei problemi 355

4. Se A <r B e B è ricorsivamente enumeratale, allora A è ricorsivamente


enumeratale.

Dimostrazione
1. e 2. sono esercizi banali.
3. Sia t una funzione totale e computabile tale che x e A se e solo se t(x) e B,
(ossia A = f (B)). Siano cA e cB le funzioni caratteristiche di A e di B. Allora cA
= cB°t. Quindi, se B è ricorsivo, lo è anche cA.
4. Di nuovo, A <rB significa che A = fl(B), con t computabile e totale. Ora, se B
è ricorsivamente enumerabile, allora è il dominio di definizione di qualche
funzione computabile fx (ossia B = Dx). Tuttavia A = P(B') è il dominio di
definizione di /( ° t che è computabile.

ESERCIZIO
8. 37 Si riesaminino gli Esempi 8.3 e 8.4 alla luce del Teorema 8.22. Nel caso
dell’Esempio 8.3, si dimostri che 5) <r S2 e che S2 <r S\. Nel caso
dell’Esempio 8.4 si dimostri che K <r s{, ma —(Si <rK). In entrambi i casi,
si usi h per definire <r.

Definizione 8.7
A=rB significa che A <rB eB <rA.

Ovviamente =r è una relazione di equivalenza. Si possono così definire le classi di
equivalenza di =r come gradi di irrisolvibilità. Si noti che ogni grado che contiene
un insieme ricorsivo, contiene tutti e soli gli insiemi ricorsivi. Si può quindi parlare
del “grado ricorsivo”. Inoltre, ogni grado che contiene un insieme ricorsivamente
enumerabile, contiene solo insiemi ricorsivamente enumerabili (si può dimostrare
che vi sono diversi gradi ricorsivamente enumerabili).
Molte domande sorgono naturalmente quando si introduce l’ordinamento
parziale in una struttura matematica. Ad esempio,
- L’ordinamento è un ordinamento totale?
- L’ordinamento è un reticolo?
- Se A <rB e A 7= B esiste una o più C “fra A e'B”, tale che A <r C <rB7 (come
al solito <r significa <r A
Queste ed altre domande non hanno un interesse puramente matematico. Ad
esempio, se <r fosse un ordinamento totale, ciò implicherebbe che, dati due
problemi qualsiasi Pi e P2, può accadere che Pi sia più difficile di P2 o viceversa,
oppure che entrambi presentino un’uguale difficoltà. Una trattazione algebrica
356 Informatica teorica

completa di questi argomenti va al di là degli scopi di questo testo. Si elencano qui


semplicemente alcuni enunciati scelti fra quelli che descrivono le proprietà più
semplici e più importanti.
Teorema 8.23
1. Esistono due insiemi non ricorsivi AeB che non sono paragonabili rispetto a <r.
2. <r definisce un reticolo superiore (ossia due insiemi qualunque hanno un
minimo limite superiore). Inoltre, se tali insiemi sono ricorsivamente
enumerabili il loro minimo limite superiore è ricorsivamente enumerabile.

Dimostrazione del punto 1
L’insieme K dell’Esempio 8.15 e il suo complemento K non sono confrontabili. In
generale, si noti che, per ogni A, A =rA oppure A e A sono non comparabili.
Infatti, per il Teorema 8.22(2) A <rA implicherebbe A <rA. Poiché K è
ricorsivamente enumerabile, mentre K non lo è (altrimenti entrambi sarebbero
ricorsivi), K <r K è falso (si veda il Teorema 8.22(4)). Ciò esclude K =r K .
Perciò Ke K non sono confrontabili.
Dimostrazione del punto 2
Per ogni A e B si definisca A © B come
{x| (x = 2z, ze.A)\(x = 2z + \,z^B)}.
Ora, A <rA © B per mezzo di./(x) = 2x e B <rA © B per mezzo di Z(x) = 2x + 1.
Inoltre, se A <rC per mezzo di t e B <r C per mezzo di g, allora A © B <r C per
mezzo di una funzione h definita da /z(2x) = t(x), h(2x + 1) = g(x). Quindi ©
soddisfa gli assiomi dell’operazione in un reticolo.
Inoltre si supponga che A = 1^, B = dove gA, gB sono funzioni totali e
computabili (il caso in cui A do B sono vuoti è lasciato al lettore per esercizio).
Allora A © B = Ig, dove

g(x) = if x è pari then 2gA(x/2) else 2gB(x/2 + 1) + 1.



Ovviamente il Teorema 8.23 si applica altrettanto bene ai gradi di irrisolvibilità,
ossia alle classi di equivalenza di =r.
Definizione 8.8 (Insiemi completi)
Un insieme S è completo rispetto a <r (S si dice r-completo) se e solo se
1. S è ricorsivamente enumerabile e
2. A ricorsivamente enumerabile implicai <rS.
La risoluzione automatica dei problemi 357

Quindi un eventuale insieme C, r-completo, risulta massimo rispetto a <„ fra gli
insiemi ricorsivamente enumerabili. Intuitivamente, un insieme di questo tipo si
potrebbe chiamare “il problema semidecidibile più difficile”, poiché, una volta
ottenuta la soluzione di C, ad esempio mediante una macchina con oracolo, la
soluzione di ogni altro problema semidecidibile si potrebbe ottenere per riduzione
meccanica alla soluzione di C. Si noti anche che, se Q e C2 sono due insiemi r-
completi, allora Q =r C2. Se dunque esiste un grado massimo di irrisolvibilità, esso
è unico e si indica con [C]= .
Nel Capitolo 9 si comprenderà come la nozione di completezza abbia una
portata molto ampia e si possa applicare ad altri importanti aspetti della teoria della
computazione.
Il prossimo teorema afferma l’esistenza di insiemi r-completi.
Teorema 8.24
L’insieme H= {(x, x e Dy} è r-completo7.

Dimostrazione
H è l’insieme {(x,_y)| tale che My, con ingresso x si arresta in meno di z passi}.
Si può dimostrare che H è ricorsivamente enumerabile, mediante un ragionamento
simile a quello impiegato nella dimostrazione del Teorema 8.13.
Sia A un qualunque insieme ricorsivamente enumerabile. Allora A = D y per
qualche jo- Quindi x e A se e solo se (x,y0) e H e A <rHpex mezzo della funzione
f definita da/fx) = (x, yG\

Poiché si può anche dimostrare che H <rK, si ha K =r H e anche K è r-completo.

ESERCIZI

8.38 A = {x| Dx è finito} e B = {x| Ix è finito} sono confrontabili?


8.39 Si mostri che C = {x| Z)x A 0 } è r-completo.
8.40 Se A è ricorsivo mentre B non lo è, A e B sono confrontabili?

7 Ovviamente H può essere codificato come un sottoinsieme di N, nel solito modo.


338 Informatica teorica

RIASSUNTO DEL CAPITOLO


Questo capitolo è stato dedicato all’analisi del concetto di risolvibilità meccanica di
un problema.
Dapprima, rifacendosi al Capitolo 4, si è ricordato come, indipendentemente
dal modo in cui un problema viene formalizzato, le MT, le grammatiche non
ristrette, le funzioni ricorsive ed altri formalismi sono almeno altrettanto potenti di
ogni altro modello computazionale conosciuto. Questo fatto, insieme ad
un’approfondita analisi della nozione intuitiva di algoritmo, ha consentito di
derivare la tesi di Church che stabilisce che le MT, come ogni altro formalismo
equivalente, costituiscono un’adeguata formalizzazione della nozione di dispositivo
meccanico di calcolo.
Si è poi introdotta la nozione di MT universale: una macchina che è in grado di
simulare la computazione di ogni altra MT, compresa se stessa. Si è quindi arrivati
a constatare l’esistenza di problemi meccanicamente irrisolvibili: dapprima è stata
impiegata una tecnica di dimostrazione diretta di tipo diagonale; successivamente
si è impiegata la tecnica di riduzione della soluzione di un problema alla soluzione
di un altro di cui è già nota T irrisolvibilità.
Le Sezioni dalla 8.5 fino alla 8.9 hanno presentato una grande varietà di
problemi risolvibili e irrisolvibili e dovrebbero aver aiutato il lettore a sviluppare la
capacità di comprendere da solo se un problema sia, o meno, meccanicamente
risolvibile. Sono stati introdotti due teoremi fondamentali: il teorema di Kleene ed
il teorema di Rice. Il secondo è stato studiato in qualità di strumento, per altro
molto potente, per la dimostrazione dell’irrisolvibilità dei problemi.
Le Sezioni 8.10 e 8.11, riservate ad un pubblico più esperto, hanno introdotto
infine alcuni risultati di decidibilità e indecidibilità per i linguaggi e le nozioni di
riducibilità, di gradi di irrisolvibilità e di completezza di un problema.

ALTRI ESERCIZI

8.41 Si dimostri che non si può decidere se, per un dato programma Pascal P e per
una data istruzione S, contenuta in P, esiste qualche dato di ingresso tale che
il flusso dell’esecuzione di P raggiunga, prima o poi, l’istruzione S.
8.42 Talvolta, in letteratura, la classe delle funzioni ricorsive totali viene definita
come la classe delle funzioni definite mediante funzioni base, composizione
e ricorsione e l’operatore totale p, ossia l’operatore p definito come nella
Sezione 2.4.2 (p(g(xi.. ,xm y) = 0)), con il vincolo che esista sempre una y
che soddisfi p.
Si può dimostrare, in modo non banale, che la classe delle funzioni ricorsive
totali coincide con la classe delle funzioni ricorsive parziali che sono totali.
D’altra parte, la classe delle funzioni ricorsive parziali coincide con la classe
delle funzioni calcolabili da una MT (si veda l’appendice 4.A). Questo
risultato contraddice il Teorema 8.10? Perché? Perché no? Si ricordi che le
La risoluzione automatica dei problemi

funzioni ricorsive primitive sono totali e computabili, ma non coprono


l’intera classe delle funzioni totali e computabili.
8.41 * Si mostri che l’insieme dei teoremi di A (l’assiomatizzazione dell’aritmetica
data nella Sezione 2.4) è ricorsivamente enumerabile ma non ricorsivo.
8.42 Problema della Corrispondenza di Post (PCP)
Siano Si = {xi, ...,xk},S2 = {yi, ■■■,yj} due insiemi finiti di stringhe su di un
alfabeto VT. Il PCP chiede se esiste una sequenza finita di interi {fi, fi, ..., im}
lm = y.ll y.l2 ...y,‘m .
tali che x.‘1 x ‘2 ...x *
Per esempio, con Si = {xi = 1, x2 = 1011, x3 = 110} e S2 = {yi = 111, _y2 = 10,
y3 = 0}, la sequenza 2,1,1,3 fornisce una soluzione al PCP come
X2X1X1X3 =jfiPrm = 101111110
Si mostri che il PCP è irrisolvibile.
8.43 (Per il lettore che conosce un po’ di storia della matematica). Il decimo
problema di Hilbert pone la seguente domanda.
“Sia P(xi, ..., xn) un polinomio con variabili xb ..., xn e coefficienti interi. È
possibile decidere se un polinomio dato P abbia radici intere (ossia soluzioni
diP(xb ...,x„) = 0)?”.
Che cosa si può dire ora sulla decidibilità del problema di Hilbert? Cosa si
sarebbe potuto dire, negli anni 40, circa la decidibilità del medesimo
problema?
Si noti che la decidibilità del decimo problema di Hilbert non coincide con la
decidibilità dell’esistenza di radici intere per un qualunque polinomio P, ma
con la decidibilità della decidibilità dell’esistenza di tali soluzioni.

Soluzioni schematiche di esercizi scelti


8.17 Si definisca la funzione h come
h(x) = iffx(x) = x then x + 1 else x.
Se g è computabile, lo è anche h. Sia h = f . Si consideri il caso /z(x0) = if
/ (xo) = xo then x0 + 1 else xo e si trovi la solita contraddizione.
8.18 Si segua la traccia seguente:
a. Si consideri la solita funzione k definita da k(x) = fx(x). Si aggiunga la
“variabile fittizia” y e si definisca k' mediante k'(x, y) = iffx(x)^L then
1 else ± per ogni y.
b. Per ogni x, si consideri la funzione f, data da
fw(ì) = k\x,y)
dove w = h(x), h essendo una funzione computabile, poiché lo è k'. fw è
una funzione costante se e solo se fx(x) risulta definita.
c. Si supponga che, per assurdo, la funzione g, data da g(x) = if fx è
costante then 1 else 0, sia computabile. Allora g(w) = g(h(x)) = iffx(x) è
30U Informatica teorica

definita then 1 else 0; ossia, la composizione g° h, definita da g° h(x) =


g(/i(x)), è una funzione computabile di x.
d. A questo punto, la dimostrazione procede come nel Corollario 8.7.
8.21 Chiaramente, per qualche valore di x0, la risposta è affermativa. Infatti, se x0
è il numero di Godei della funzione costante h, h(x) = z per ogni x, allora g è
la funzione costante g(z) = 1. D’altra parte, sia x0 il numero di Godei della
funzione h definita da h(x) = if f/x)^± then fx(x) + 1 else ±. In tal caso g
non è computabile. Infatti, si supponga per assurdo che g sia computabile.
Allora la funzione g definita da g (z) = if ze D f then/Xx) else 0, sarebbe
anch’essa computabile.
Tuttavia g coincide con: if /z(z)^± then h(z) else 0. Quindi g è una
estensione totale di h, il che contraddice il Teorema 8.12.
8.23 La funzione h4 è la stessa funzione di/(x> y) = 1 per ogni x, y. Infatti, in una
sequenza di 10r 'y cifre, deve necessariamente esistere una stringa di almeno x
cifre ripetute almeno y volte.
Dimostrazione della decidibilità del problema L = 0 per i linguaggi non
contestuali (si veda la Tabella 8.1).
Si consideri una grammatica non contestuale G. L(G) 0 significa che
esiste una derivazione S=> G x, xel)*. Tuttavia, se esiste una simile
derivazione, allora esiste anche una derivazione senza autoinclusioni (ossia
che non è decomponibile come S=> uAv^> uyAzv^> uywzv, si veda
l’esercizio 1.63). Le derivazioni prive di autoinclusioni sono di lunghezza
non maggiore di l =X';I+1, dove VN è l’alfabeto non terminale di G e k è la
massima lunghezza delle parti destre delle produzioni di G. Quindi
L(G) 0 se e solo se S=>hG x per qualche h < l, x e VT*. Chiaramente, un
algoritmo può enumerare tutte le derivazioni di G di lunghezza non maggiore
di l e ciò completa la dimostrazione.
8.40 L’apparente contraddizione è risolta notando che il p-operatore totale non è
necessariamente computabile. In altre parole, il vincolo che esista una y tale
da soddisfare p, non è decidibile.
8.41 È molto semplice fornire un algoritmo che enumeri tutti i teoremi di A. Allo
scopo di mostrare che l’insieme dei teoremi di A non è ricorsivo, basta
comprendere che la fbf V: fy(f(x) =y) rappresenta il fatto che la funzione fè
definita in x. Per l’Enunciato 1.9, se fè una funzione ricorsiva parziale, V è
un teorema di A se e solo se il risultato di cui sopra è vero. Se i teoremi di A
fossero un insieme ricorsivo, si potrebbe effettivamente verificare se, o
meno, una qualunque funzione ricorsiva parziale f sia definita per ogni valore
di x. Tuttavia ciò è un assurdo, poiché le funzioni ricorsive parziali
coincidono con le funzioni computabili (si veda l’Appendice 4.A).
8.43 II decimo problema di Hilbert. E noto ormai che la risposta al problema è
negativa (ossia che è irrisolvibile il problema di stabilire se un dato
polinomio P abbia radici intere o meno). La dimostrazione originale di

à
La risoluzione automatica dei problemi 301

Matijasevic è riportata da Davis e Hersh (1973). Invece, nel 1940, la


situazione era la medesima che per l’ultimo teorema di Fermat (ossia era
noto che la risposta poteva essere o affermativa o negativa ma nessun essere
umano né alcuna macchina era in grado di darla).

Note bibliografiche

La potenza dei formalismi ed i risultati relativi alla indecidibilità costituiscono il


fondamento dei testi sugli automi e sulla teoria della computabilità. Quindi, la
teoria trattata in questo capitolo si può reperire anche in altri testi sugli automi, sui
linguaggi formali e sulla teoria della computazione. In particolare si menzionano
Hopcroft e Ullman (1979, 2001, 2006), Rogers (1963), Harrison (1978), Minsky
(1967) e Lewis e Papadimitriou (1981).
Coloro che, per primi, hanno formulato i principali risultati relativi alla
risolvibilità e alla irrisolvibilità sono Godei (1930, 1934), Church (1936), Turing
(1936, 1937), Markov (1954), Shannon (1938, 1956), Kleene (1952, 1967), Post
(1936, 1947) e Rice (1953).
La prima analisi dei problemi di irrisolvibilità nella teoria dei linguaggi
formali è dovuta a Bar-Hillel, Perles e Shamir (1961). Alcuni documenti chiave sui
gradi di irrisolvibilità sono Dekker (1955), Dekker e Myhill (1958) e Kleene e Post
(1954). Il recente risultato sulla decidibilità dell’equivalenza dei linguaggi
riconoscibili da automi a pila è stato presentato da Stirling (2001).
L’esposizione degli enunciati fondamentali della teoria della computazione
contenuta in questo capitolo, compreso l’Esempio 8.1, è principalmente fondata su
Rogers (1963), Knuth (1969) e Minsky (1967). Fra questi testi, quello di Rogers è
particolarmente adatto per il lettore interessato ad un ulteriore approfondimento e
comprensione della computabilità.
L’ultimo teorema di Fermat è stato ampiamente descritto in letteratura, anche
in testi divulgativi, ad esempio da Singh (1998). Il teorema è stato dimostrato nella
sua versione più generale da Wiles (1995). Il decimo problema di Hilbert può
essere approfondito in Matiyasevich (1993).
Capitolo 9

LA COMPLESSITÀ DEL CALCOLO

Nel capitolo precedente abbiamo mostrato che esistono problemi che, pur essendo
ben definiti e di interesse sia concettuale che pratico, non sono, purtroppo,
risolvibili, cioè problemi per i quali non esiste una MT in grado di risolverli, o
equivalentemente, problemi per i quali non è possibile scrivere un programma
eseguibile da un calcolatore che li risolva. La risolvibilità di un problema non è
però l’unico elemento da tenere in considerazione quando si affronta un problema
in cerca della sua soluzione. Infatti, poiché i calcoli, come molti altri servizi,
costano sotto diversi punti di vista, può capitare che un problema che sia
teoricamente risolvibile, non lo sia poi in pratica.
Si supponga di avere accesso al più potente calcolatore del mondo; anche in
questo caso rimarrebbe ugualmente un prezzo da pagare nella risoluzione di un
problema dato: il tempo impiegato dal programma per produrre la soluzione
richiesta. Appare chiaro che, se non è possibile ottenere una soluzione di un
problema entro un “ragionevole” intervallo di tempo, il problema diviene
praticamente intrattabile, anche se teoricamente risolvibile. Consideriamo, ad
esempio, il problema di stabilire il risultato di una “perfetta” partita a scacchi, ossia
di una partita in cui entrambi i giocatori giocano al meglio: tale problema è
certamente decidibile, infatti vi è solo un numero finito di stati sulla scacchiera.
Quindi, partendo dalla configurazione iniziale della scacchiera, un algoritmo ideale
potrebbe enumerare tutte le configurazioni raggiungibili e stabilire la sequenza che
verrebbe scelta da due giocatori “perfetti”. Si può tuttavia dimostrare che il
calcolatore più veloce che esista non completerebbe l’esecuzione di questo
algoritmo nel tempo di vita stimato dell’universo. Di conseguenza, il problema pur
risolvibile della “perfetta gara di scacchi” è intrattabile, ossia, pur essendo
disponibile un algoritmo per la sua soluzione, l’esecuzione di tale algoritmo
richiederebbe un tempo inaccettabile.
L’intrattabilità costituisce una limitazione notevole per le applicazioni
pratiche. Sfortunatamente, infatti, esistono molti problemi interessanti che, pur
essendo risolvibili, si rivelano praticamente intrattabili.
Questo capitolo è dedicato al concetto di intrattabilità. Prima di addentrarsi in
una rigorosa analisi dell’argomento, è necessario porre le fondamenta per questo
nuovo concetto ed illustrare ulteriormente le sue motivazioni di base.
Il concetto di “intrattabilità” è ovviamente correlato con quello di
“complessità”: un problema è intrattabile quando la sua complessità è troppo
elevata. Informalmente, la complessità può essere vista come la misura del prezzo
che bisogna pagare per risolvere il problema. Quindi il nostro obiettivo è definire la
complessità in modo formale e mostrare, fornendo adeguati strumenti, in che modo
si possa effettuare l’analisi di complessità.
364 Informatica teorica

Le risorse che si utilizzano nel risolvere un problema sono principalmente due: lo


spazio, cioè la memoria necessaria all’algoritmo che risolve il problema, e il tempo
richiesto per produrre la soluzione; di conseguenza, la complessità si può spezzare
in due componenti: la complessità spaziale e la complessità temporale.
La soluzione proposta per un dato problema occupa le risorse in due modi; vi è
una componente costante, che non dipende cioè dai dati in ingresso (ad esempio le
varibili semplici e le costanti di un programma se si pensa allo spazio) e una
componente che varia con i dati in ingresso, o meglio che è funzione della
dimensione dei dati in ingresso. Ovviamente questa seconda componente è di
maggior interesse, ma richiede un’analisi più approfondita rispetto alla prima.
Inoltre, quando si analizza il tempo necessario per produrre una soluzione, ci
si può riferire sia al tempo di compilazione che al tempo di esecuzione della
soluzione. Si osservi però che il tempo di compilazione si può considerare come
una costante in quanto non dipende dai dati fomiti in ingresso e non è quindi
interessante per i nostri scopi. In seguito quindi, quando analizzeremo il tempo
richiesto da una soluzione ci riferiremo sempre al tempo di esecuzione.
Se analizzando il tempo ci riferiamo solo al tempo di esecuzione, esistono due
modi per analizzarlo: attraverso valutazioni empiriche oppure “contando” il
numero di istruzioni che vengono eseguite (ed eventualmente quali e su che dati).
Il primo tipo di analisi consiste nel misurare il tempo impiegato per eseguire la
soluzione proposta scegliendo opportunamente i dati da fornire in ingresso. Per
quanto ragionevole sembri l’approccio, i risultati che produce sono fortemente
legati alla macchina su cui vengono fatti gli esperimenti e non è di interesse per i
nostri scopi, che si ripropongono di fornire un’analisi della soluzione indipendente
dalla macchina scelta e dalla velocità della CPU.
Si osservi inoltre che le due risorse, per quanto apparentemente scorrelate,
sono in realtà legate una all’altra. Infatti, ogni qual volta si provi a ridurre il
consumo di una delle due, si rischia di aumentare l’utilizzo dell’altra. Se, ad
esempio, si cerca di minimizzare lo spazio necessario per produrre una soluzione,
si possono usare strutture dati più sofisticate o, semplicemente, una codifica più
compatta; questi approcci, però, se da un lato permettono di minimizzare lo spazio
richiesto, dall’altro potrebbero richiedere più tempo di gestione. Diventa quindi
fondamentale, quando si propone una soluzione, fissare gli obiettivi che si
vogliono raggiungere anche in termini di risorse utilizzate, bilanciando bene il
consumo di tempo e spazio.
Finora si è suddiviso il costo di un programma in due componenti: la
complessità spaziale e la complessità temporale. Questi non sono, tuttavia, gli unici
costi di un programma: essi si presentano unicamente all’atto dell’esecuzione del
programma, ignorando quindi i costi dovuti allo sviluppo e alla manutenzione del
software. Ovviamente, analizzando la complessità di una soluzione, da un punto di
vista ingegneristico e aziendale, possono essere presi in considerazione altri
parametri, quale, ad esempio, le risorse umane e di tempo, necessarie per giungere
alla soluzione.
La complessità del calcolo 365

In generale perciò compito dell’ingegneria del software è ottenere una misura


complessiva della qualità del software, ossia dei benefici da esso ottenibili e di tutti
i relativi costi in modo da individuare il modo migliore per ottenere la soluzione di
un problema mediante un adeguato strumento di calcolo. E però facilmente
intuibile che la costruzione di un siffatto quadro completo del rapporto costi­
benefici della soluzione di un determinato problema comporta anche valutazioni di
carattere soggettivo e difficilmente formalizzabile. Quanto può essere “gradita”
una certa interfaccia uomo-macchina? È più conveniente assegnare un compito a
un progettista esperto ma costoso oppure a un novizio a un più basso salario?
Questo tipo di valutazioni esula però dagli scopi di questo testo e viene
lasciato alla letteratura del settore dell’ingegneria del software. In questo capitolo
invece ci occuperemo esclusivamente del costo dell’esecuzione del software in
termini delle risorse fisiche necessarie e della loro valutazione mediante strumenti
matematici.
In particolare, nella prossima sezione cercheremo di formalizzare il concetto di
complessità, individuando gli aspetti fondamentali e di carattere generale di tale
misura e fornendo stumenti matematici utili nel resto del capitolo. Nelle Sezioni
9.2 e 9.3 analizzeremo la complessità per due diversi modelli computazionali: le
MT e la macchina RAM. Inoltre, nella Sezione 9.3 forniremo strumenti per lo
studio della complessità di programmi scritti in linguaggio di alto-livello. I risultati
ottenuti per i diversi modelli verranno riassunti e confrontati nella Sezione 9.4.
Infine, le Sezioni 9.5, 9.6 e 9.7 tratteranno argomenti più avanzati, quali gerarchie,
riducibilità e completezza.

9.1 Introduzione alla complessità computazionale


In questa sezione studieremo il significato del termine complessità. Inizialmente,
attraverso l’aiuto di alcuni esempi, analizzeremo informalmente che cosa si intende
per consumo delle risorse, concentrandoci, come già anticipato, su spazio e tempo.
Poi cercheremo di sistematizzare alcune osservazioni ricavate dall’analisi dei primi
esempi e infine forniremo alcuni strumenti matematici che saranno utili in seguito
per lo studio della complessità.

Esempio 9.1
Si consideri la seguente funzione, che risolve il problema dell’appartenenza di un
intero ad un insieme. I parametri della funzione sono x (un insieme rappresentato
da un array) e y (l’elemento di cui bisogna decidere l’appartenenza all’insieme).
La funzione restituisce 1 se y appartiene a x, 0 altrimenti.
int ricerca (int x[N], int y){
\\N è una costante
for(int i = 0; i < N; i++)
if(x[i] == y)
return 1;
366 Informatica teorica

else
i = i + 1;
return 0;
}
Cerchiamo di analizzare intuitivamente sia la memoria necessaria (complessità
spaziale) che il tempo richiesto (complessità temporale) dalla funzione ricerca
per fornire la soluzione.
Dapprima si noti che il numero di celle di memoria e il numero di secondi
richiesti per eseguire la procedura dipendono daH’insieme x. Nel caso della
complessità spaziale, si può dire che il numero totale di celle richieste 'i/' è dato
dalla cardinalità dell’insieme x (ossia N) e da un numero costante k di celle, che
non dipende dalla cardinalità di x, cioè .9? = |x| + k. Intuitivamente, k corrisponde
al numero di celle usate per memorizzare tutte le altre variabili (i nel nostro
esempio) e il codice oggetto.
L’analisi della complessità temporale è leggermente più elaborata. Il numero
totale di secondi .^richiesti per eseguire la funzione ricerca si può esprimere
come: dT = + 57, dove .57 è il tempo speso fuori dal ciclo f or, mentre ,57 è il
tempo impiegato al suo interno. .57 è un valore costante (ad esempio /?), che non
dipende dall’insieme x, mentre .55 dipende da esso; si può quindi scrivere
= h + ,57(x)
Come si può notare, il tempo richiesto dalla funzione ricerca è molto
variabile e dipende dalla presenza o meno dell’elemento cercato nell’insieme x e,
nel caso di appartenenza, dalla sua posizione. Nel caso migliore, la procedura trova
un elemento uguale a y nella prima posizione del vettore: in questo modo il ciclo
viene eseguito una sola volta, in quanto la funzione termina restituendo il valore 1
(ramo “then” dell’if). Nel caso peggiore, cioè quando y non appartiene a x, la
funzione scandisce tutto il vettore prima di giungere ad una conclusione
sull’appartenenza. In questo modo il ciclo viene ripetuto |x| volte.
Che cosa si può dire del caso medio, cioè quanto tempo è necessario per
fornire la soluzione in media? Per rispondere a questa domanda, è necessario
conoscere statisticamente la distribuzione dei dati in ingresso. Sia p la probabilità
che la funzione ricerca venga chiamata con un valore y appartenente all’insieme
x. Si supponga inoltre che tutti i valori di questo tipo abbiano uguale probabilità di
comparire nelle chiamate a funzione. Allora (1 ~p) è la probabilità che y non sia

nell’insieme, mentre è la probabilità che il ciclo venga eseguito z volte, per


Ix I
qualsiasi i con 1 < z < |x|, giungendo alla conclusione che y è contenuto nella
posizione z. Se si suppone che il tempo speso nel ciclo da ciascuna iterazione sia
una costante r, si possono ricavare le seguenti formule che esprimono la
complessità temporale:
Caso ottimo: .57 =h + r
Caso pessimo: .5^ = h + r ■ |x|
367
La complessità del calcolo

Caso medio: ■-/m = (1 - p) • r-1 x | + — • r • i=


Ix| ;=i

= (1- P)-r-\ x I +-■ p r -(I x | +1) =


2

= 5-1 x | + — • p • r
2

dove s = r------p • r
2

Analizziamo ora un’altra soluzione del problema dell’appartenenza. Questa volta,.
però, supponiamo che gli elementi dell’insieme x siano memorizzati in un array
ordinato.

Esempio 9.2
Si supponga di dover nuovamente risolvere il problema dell’appartenenza per un
insieme, rappresentato da un array ordinato in ordine ascendente. Per risolvere
questo problema, si potrebbe impiegare la procedura ricerca, descritta
nell’Esempio 9.1, anche se, in questo modo, non sfrutteremmo il fatto che
l’insieme risulta già ordinato. Un’altra soluzione applicabile in questa situazione è
il ben noto algoritmo di ricerca binaria, ricerca_binaria.
int ricerca_binaria (int x[N]; int y) {
\\N è una costante
int i, j, k;
i = 0; j = N - 1;
while (ì j){
Wl'insieme dei candidati va x[i] a x[j] ; si
Wprende l'elemento centrale}
k = (i + j)/2;
if x[k] == y
return 1
else Wdimezza l'insieme dei candidati
if y < x[k]
' j = k - 1
else
i = k + 1
}
return 0 ;
}
368 Informatica teorica

Osserviamo che, nell’algoritmo proposto, la condizione di uscita dal ciclo while,


i > j, significa che non vi sono più candidati di cui controllare l’appartenenza
aH’insieme.
Per brevità analizziamo la complessità della funzione proposta solo nel caso
pessimo, si lasciano al lettore come esercizio lo studio del caso ottimo e medio
(supponendo la stessa distribuzione considerata nell’Esempio 9.1). Ciascuna
iterazione di ricerca_binaria esamina un elemento di x, dimezzando ogni
volta l’insieme dei candidati. Nel caso pessimo, il ciclo termina quando si è rimasti
con un insieme di candidati vuoto. Il ciclo, quindi, può essere ripetuto al più
[log |x|] + 1 volte. Supponendo allora che ciascuna iterazione impieghi il
medesimo tempo u e che v sia il tempo speso prima di entrare nel ciclo, si ottiene
JT'P = v + w([log |x|] + 1) con |x| > 1.

Si intuisce dunque facilmente che, nel caso in cui l’insieme x sia ordinato, la
funzione ricerca_binaria è migliore della funzione ricerca. Conviene però
ricavare tale risultato mediante un’analisi precisa del comportamento delle due
funzioni nel caso pessimo (lasciando al lettore, nell'Esercizio 9.1 il compito di
ottenere un risultato analogo nel caso medio).
In linea di principio, per confrontare compiutamente le due funzioni 3^' p e
3^, bisognerebbe conoscere i valori h, r, u, e v; tuttavia, essendo essi costanti che
non dipendono da |x|, si può sempre trovare un valore c tale che dT' p(|x|) < .^(|x|)
per tutti gli insiemi x per i quali |x| > c. Perciò, per dimensioni di x
sufficientemente grandi il tempo di esecuzione di ricerca_binaria sarà sempre
(notevolmente) inferiore a quello di ricerca indipendentemente dal valore delle
costanti h, r, u, e v. In termini più precisamente matematici, vi è un ordine di
grandezza di differenza fra <^(|x|) e dT’ p(|x|) poiché

In conclusione, la funzione ricerca_binaria ha una migliore complessità


temporale della funzione ricerca nella maggior parte dei casi, ossia in tutti i casi
escluso eventualmente un numero finito di essi.

ESERCIZIO
9.1 Si analizzi la complessità temporale di ricerca_binaria per il caso
medio, sotto le medesime ipotesi di probabilità viste per la funzione
ricerca nell’Esempio 9.1.
Suggerimento: si noti che, se y e x, la probabilità di trovare y nella prima
iterazione del ciclo è l/[x[. La probabilità di trovare y nella seconda
iterazione è (1 - l/|x))-l/[|x|/2] e così via. Si noti anche che, per x
sufficientemente grande, anche la complessità del caso pessimo di
369
La complessità del calcolo

ricerca_binaria risulta di gran lunga migliore di quella del caso medio


della funzione ricerca.

9.1.1 Come definire con precisione la complessità


Gli Esempi 9.1 e 9.2 hanno introdotto alcuni importanti spunti per valutare e
confrontare la complessità degli algoritmi; in questa sezione generalizzeremo e
approfondiremo queste prime osservazioni.
Prima di tutto, gli esempi visti hanno già anticipato che la complessità
temporale è normalmente più critica e rilevante della complessità spaziale; per
questo motivo essa sarà oggetto di maggior attenzione nel resto del capitolo.
Appare inoltre evidente la necessità di definire l’unità di misura da impiegare
nell’analisi di complessità. Rianalizzando gli Esempi 9.1 e 9.2, bisognerebbe
quindi specificare l’esatto valore delle costanti contenute nelle formule di
complessità presentate, il che richiede una precisa conoscenza del costo di ciascuna
istruzione del programma.
Guardando agli esempi precedenti, si può anche osservare, come per altro
evidenziato nell’introduzione del capitolo, che la complessità dipende dalla
“dimensione” e, molto spesso, anche dai particolari valori assunti dai dati in
ingresso. Questa osservazione può essere estesa a molti casi pratici e sottolinea la
necessità di effettuare un’analisi di complessità per i casi pessimo, medio e ottimo,
in funzione delle dimensioni dei dati di ingresso. Più formalmente, tali casi
rappresentano la scelta di ingressi per cui viene eseguito il massimo numero di
istruzioni nel programma, il comportamento del programma in relazione alla
possibile distribuzione di ingressi e la scelta di input per cui viene eseguito il minor
numero di istruzioni, rispettivamente. Nel resto di questo capitolo ci concentreremo
sul caso pessimo. Infatti il caso ottimo non è particolarmente significativo e il caso
medio richiede una conoscenza sulla distribuzione dei dati in ingresso, che non è
sempre nota. Il caso pessimo, invece, spesso più semplice da individuare, è anche
particolarmente significativo perché fornisce i peggiori risultati ottenibili dalla
soluzione proposta in termini di consumo delle risorse e quindi, se ritenuto un
costo accettabile, garantisce l’accettabilità anche in tutti gli altri casi, proprietà
particolarmente importante nel caso di sistemi critici in cui un comportamento
inadeguato ai requisiti, anche se eccezionale, potrebbe avere conseguenze
disastrose.
L’analisi dei casi pessimo, medio e, occasionalmente, ottimo, sono dunque un
importante strumento di astrazione che permette di formalizzare la complessità
della soluzione di un problema in funzione della dimensione dei dati invece che di
tutti i possibili valori di ingresso.
A sua volta, però, la dimensione (e quindi la complessità) dipende dalla
particolare codifica dei valori in ingresso. Ad esempio, sono necessari n simboli
per codificare il numero intero n in base unaria, mentre ne occorrono Llogt «J + 1
370 Informatica teorica

per codificarlo in base k con k > 1. Bisogna quindi prestare attenzione alla reale
dimensione dell’ingresso nella soluzione considerata.
A differenza di quanto visto nel Capitolo 8 per la risolvibilità, la complessità
non è collegata solo al problema che si vuole affrontare, ma dipende dall’algoritmo
scelto per risolverlo. Come si è visto nell’Esempio 9.2, lo stesso problema (ricerca
di un elemento in un insieme ordinato) può essere risolto, con differenti
complessità, da due algoritmi diversi (ricerca e ricerca_binaria).
Finché si ragiona in termini di risolvibilità, un problema formalizzato mediante
una determinata codifica (ad esempio come un problema di traduzione) può essere
riformulato in modo equivalente - ossia senza cambiarne la proprietà di
risolvibilità o meno - cambiando tipo di formalizzazione tipo (ad esempio come
riconoscimento di un linguaggio o calcolo di una funzione il cui dominio è
l’insieme dei numeri interi). Per esempio, la traduzione y = r(x) può essere
codificata come il riconoscimento di x$y ($ £ x, $ £ y). In generale, però, questa
equivalenza non vale per la complessità temporale. Infatti, si supponga che una
computazione deterministica effettui la traduzione y = r(x) nel tempo t(x). Si può
allora decidere se x$y e LT = {zi$z2| z2 = fi^i)} nello stesso tempo t(x). Ciò non
vale, tuttavia, sull’altro versante. Se il problema x$y e L può essere deciso nel
tempo t(x$y) e se si usa l’algoritmo che riconosce L per calcolare y = r(x) bisogna
allora enumerare tutti i possibili {y,}, ad esempio in ordine crescente di lunghezza.
Per ciascun si richiede un tempo z‘(x$yI) per decidere se x$y,eZT. Quindi, il
tempo totale necessario al calcolo di r(x), nel caso pessimo, è dato da:

I y, l±frO)|
Questo ragionamento, ovviamente, non esclude la possibilità che un algoritmo più
efficiente riesca a calcolare r(x) in un tempo inferiore. In questo capitolo ci si
riferirà principalmente al problema della complessità delle decisioni, anche se, in
molti casi, verranno presi in considerazione altri tipi di problemi.
Infine, le stime di complessità dipendono dalla scelta del modello di calcolo,
come verrà mostrato nel resto di questo capitolo; verranno in particolare studiate le
macchine di Turing e macchine ad alto e basso livello che corrispondono, in una
certa misura, ai linguaggi di programmazione “tipo - Java” e “tipo - assembler”.
Questa osservazione esclude di poter ottenere per la complessità un risultato
equivalente alla Tesi di Church per la risolvibilità, in quanto la complessità
dipende dal modello scelto per la soluzione. Vedremo però che, anche se le
soluzioni proposte per modelli diversi portano spesso a complessità diverse, queste
possono essere correlate tra di loro in modo sistematico.

9.1.2 Comportamento asintotico


Consideriamo nuovamente il problema della ricerca di un elemento all’interno di
un insieme ordinato. Precedentemente, si è giunti alla conclusione che, nel caso
peggiore, l’algoritmo ricerca_binaria presenta una complessità temporale
371
La complessità del calcolo

migliore, rispetto all’algoritmo ricerca. 11 motivo fondamentale risiede nel fatto


che, esclusi i valori di |x| minori di una costante c indipendente da |x|, il numero di
secondi richiesti per l’esecuzione di ricerca_binaria risulta molto inferiore al
numero di secondi richiesti da ricerca. Vi è anzi un ordine di grandezza di
differenza fra i due, come mostra il seguente limite:

Ciò deriva dal fatto che il comportamento asintotico (ossia il comportamento per x
tendente all’infinito) di è dell’ordine di log2(|x|), mentre V 'p è dell’ordine di
|x|.
La notazione dell’ordine di grandezza di una funzione, nota sotto il nome di
notazione theta-grande, sottolinea i fattori dominanti che influenzano la crescita
della complessità, in funzione della dimensione dell’ingresso e per questo motivo
risulta particolarmente utile per lo studio della complessità di una soluzione.
Prima di analizzare formalmente la notazione theta-grande, introduciamo altre
due notazioni che possono risultare utili nella sua definizione: la notazione o-
grande e la notazione omega-grande.

Definzizone 9.1
Siano gef due funzioni aventi come dominio i numeri naturali e come codominio i
numeri reali positivi, R+. La funzione g è in O(/) se e solo se esistono due numeri
positivi c e «o tali che per ogni n > n0, g(n) < cflji). Alternativamente possiamo dire
che O(/) è il seguente insieme di funzioni O(/) = {g: N -à K+ | 3 c, h0 >0 e
V n>n0, gfn)<cfin)}.

Definizione 9.2
Siano gef due funzioni aventi come dominio i numeri naturali e come codominio i
numeri reali positivi, R+. La funzione g è in Q(/) se e solo se esistono due numeri
positivi ce H0 tali che per ogni n > n0, cfln) < g(n). Alternativamente possiamo dire
che Q(/) è il seguente insieme di funzioni Q(/) = {g: N —> IR+ | 3 c, n0 > 0 e
Vn>«0, c/(n)<g(n)}.

La notazione o-grande rappresenta quindi un limite superiore per la funzione data,


mentre la notazione omega-grande rapprenta un limite inferiore. Si osservi che,
data una funzione g, esistono più funzioni f tali che g e O(f) (analogamente per
Q). Poiché le due notazioni rappresentano rispettivamente un limite superiore ed
inferiore, noi saremo interessati al minimo limite superiore e al massimo limite
372 Informatica teorica

inferiore rispettivamente. Analizziamo ora l’uso di queste notazioni in pratica


tramite il seguente esempio.

Esempio 9.3
Consideriamo la funzioni g(n) = n2 + 12w + 35. Tale funzione è in O(ù2), ma anche
in O(n2) ed è in Q(m2), ma anche in (fin) e in Q(log n). Osserviamo che, fra tutti i
limiti superiori ed inferiori che si possono trovare per fin), sono di particolare
interesse il minimo dei primi e il massimo dei secondi, che in questo caso
coincidono con la funzione fin) = n2.

Nonostante la somiglianza dei termini, il limite superiore della complessità di un
algoritmo e il caso pessimo non devono essere confusi. Infatti, mentre il primo
rappresenta una funzione che limita superiormente la complessità ottenuta per la
dimensione dell’ingresso che tende all’infinito, il secondo si riferisce
all’assegnamento dei valori in ingresso, che, tra i possibili assegnamenti per la
stessa dimensione dei dati, porta il programma a svolgere più operazioni rispetto a
tutti gli altri possibili assegnamenti.
Ora che abbiamo definito i limiti inferiore e superiore per una funzione data,
possiamo introdurre anche la notazione theta-grande in due modi distinti, ma
equivalenti, mediante le seguenti definizioni.

Definizione 9.3
Sianogef due funzioni aventi come dominio inumeri naturali e come codominio i
numeri reali positivi. La funzione g è in 0(/) se e solo se esistono tre numeri
positivi cb c2 e «o tali che per ogni n > n0, c1 fin) < g(n) e g(n) < c2 fin).
Alternativamente possiamo dire che ®(f) è il seguente insieme di funzioni Q(f) =
{g: N -> IR+ | 3 ci, c2, «o >0 e V n> n0, c^fin) < g(n) e g(n) < c2fin)}.

Definizione 9.4
Siano gef due funzioni aventi come dominio i numeri naturali e come codominio i
numeri reali. La funzione g è ®(f) se, e solo se, si può trovare una costante reale
c > 0 tale che
r g(«)
lim ----------= c
;^.co f (ri)

Se consideriamo quindi la funzione giri) = n2 + 12n + 35, dell’Esempio 9.3, essa è


®(n2), analogamente a fin) = 2n2 + 365n log n.
373
La complessità del calcolo

ESERCIZIO
9.2 Si dimostri formalmente che le Definizioni 9.3 e 9.4, che introducono la
relazione theta-grande, sono tra loro equivalenti.

Si osservi che dalla Definizione 9.3 si può facilmente dedurre (usando le


Definizioni 9.1 e 9.2) che fè in 0(g) se e solo se/è in O(g) e/è in £ì(g).
Si noti che 0, al pari di O e £ì, è una relazione binaria fra funzioni definite sui
numeri naturali. Per compatibilità con la letteratura corrente, useremo la notazione
g è in 0(f) o g è 0(f) o g e 0(f) al posto della notazione convenzionale per le
relazioni g©f.
Le notazioni qui introdotte godono delle seguenti proprietà, che non vengono
qui provate, ma sono lasciate al lettore per esercizio.

Enunciato 9.1
Transitività:
- se fin) = &(g(ri)) e g(ri) = ©(h(n)) allora fin) = 0(7z(m));
- se fin) = ©(fin)) e fin) = ©(h(n)) allora/(«) = O(7?(w));
- se fin) = £ì (g(«)) e fin) = ©(h(n)) allora fin) = ©(h(n)).
Riflessività
- fin) = ©(fin));
- fin) = ©(fin));
- fin) = © (fin)).
Simmetria: fin) = ©(g(n)) se e solo se g(n) = ©(fin)).
Simmetria trasposta: fin) = ©(fin)) se e solo se fin) = ©(fin)).

In particolare, perciò, vale la seguente proprietà della notazione theta-grande.

Enunciato 9.2
La relazione 0 è una relazione di equivalenza.

Per ogni classe di equivalenza definita dalla relazione 0 sull’insieme delle funzioni
definite sui numeri naturali, è opportuno scegliere, come rappresentate dell’ordine,
una funzione caratterizzata da una formula semplice. Ad esempio, ci si riferisce a
©(nk) piuttosto che a ©(ai/ik + a*-xnk~1 + ... + cz0)-
Le notazioni introdotte per l’analisi asintotica della complessità sono
molteplici. Per una discussione approfondita delle molte notazioni alternative si
faccia riferimento alle note bibliografiche.
374 Informatica teorica

ESERCIZI
9.3 Siano date le funzioni/, con z = 1, 5, definite nel modo seguente: ffrì) =
m2*,/2(m) = M2-log2M,/3(«) = «MogioW, /4(m) = n2Jn ,f5(ri) = \sin(n)-n6\. Si
stabilisca sef è ®(f) oppure ®(^) < ®(f), per ogni ij = 1,5.
9.4 Si dimostri che le relazioni < e > sono ordinamenti parziali sull’insieme
delle funzioni definite sui numeri naturali. Come conseguenza si ottiene un
ordinamento sulle classi di equivalenza [/]0.

La notazione theta-grande tiene dunque conto dei fattori dominanti che influenzano
la crescita della funzione di complessità. Si può affermare, ad esempio, che un
algoritmo con complessità temporale 0(m2) sia peggiore di un algoritmo con
complessità temporale 0(wlog m): il comportamento asintotico del secondo è
infatti decisamente migliore di quello del primo. Si noti che, poiché la relazione
theta-grande non induce un ordinamento totale sulle sue classi di equivalenza, può
accadere che due algoritmi abbiano complessità non confrontabili.
Si osservi che però la notazione theta-grande non distingue fra due funzioni
appartenenti alla medesima classe di complessità. Tuttavia, che dire di 0(m2) e di
0(1 06m2)? Come si può metterle nella medesima classe di equivalenza quando è
evidente che la seconda è sempre IO6 volte peggio della prima? Il lettore troverà
una risposta rigorosa a questa domanda nel seguito di questo capitolo, quando
verrà presentato il teorema dell’accelerazione lineare (Teorema 9.12). Tale teorema
assicura che è sempre possibile accelerare di un fattore costante qualunque
computazione. Ad esempio, partendo da una macchina che risolve un problema in
un tempo 106m2, è possibile ottenere, almeno in linea di principio, una macchina
equivalente che esegua il medesimo calcolo in un tempo m2. La nuova macchina
richiederà ovviamente più “risorse” della precedente. Il fattore dominante della
complessità corrisponde quindi al ritmo di crescita, mentre i fattori costanti si
possono ignorare. Come ulteriore conseguenza, si può anche parlare di fattori di
complessità logaritmici senza preoccuparsi della base del logaritmo in questione.
E’ noto infatti che, per ogni bi, ò2 > 1 e per ogni funzione f
Dog 6i°/]0 =[log

9.2 Analisi di complessità per mezzo di automi


Siamo ora in grado di affrontare l’analisi di complessità facendo riferimento a vari
modelli di calcolo. Inizieremo, in questa sezione, con gli automi; successivamente
introdurremo alcuni modelli che riflettono più da vicino il comportamento dei
calcolatori reali e si valuterà la complessità usando modelli più realistici.
La complessità del calcolo 375

9.2.1 La complessità della risoluzione dei problemi mediante le macchine di


Turing
Nei Capitoli 4 e 8, le MT (deterministiche) sono state presentate come il principale
modello computazionale; ci sembra quindi ragionevole cominciare l’analisi
formale della complessità computazionale da questo modello. In particolare, ci
concentreremo sulle MT multinastro.
Iniziamo la nostra analisi con la complessità temporale, che fornisce il tempo
impiegato da una MT per risolvere un problema. Tale tempo è chiaramente legato
al numero di passi (transizioni) effettuate durante la computazione. Per come è
stata definita la funzione di transizione delle MT nel Capitolo 4, ogni transizione
comporta T ingresso in un nuovo stato, la stampa dei simboli sui nastri e il
movimento delle testine; poiché queste operazioni non dipendono dalla dimensione
delTingresso, ma dal numero di nastri, si può supporre che una transizione richieda
un tempo costante per l’esecuzione. Si può dunque assumere la singola transizione
della MT come unità di misura e, di conseguenza, valutarne la complessità
temporale come il numero totale di transizioni effettuate dalla macchina prima di
raggiungere una configurazione di arresto. Formalmente, quindi, possiamo definire
la complessità temporale di una MT come segue.

Definizione 9.5
Sia Muna MT deterministica a k nastri e sia x e /*. Sia co '-m Ci *~m Cz l- ••• *~m cr
una computazione, ossia una sequenza di transizioni di M tale che c0 = (qo, 1x, TZ0,
..., TZ0) e e; = {qi, x^y,, ayT^i, ..., a^Tp^). cr è una configurazione di arresto di M,
se ne esiste una. Allora, la funzione complessità temporale TM, di M, è definita
come:
7m(x) = if la computazione è finita then r else oo
La definizione vale anche nel caso di una MT a k nastri con uscita, con solo
qualche differenza formale di scarsa importanza.

La complessità temporale viene quindi definita come una funzione che fornisce
l’esatto numero di passi richiesti da una MT per raggiungere una configurazione di
arresto, se esiste, a partire dalla configurazione iniziale, per una qualsiasi stringa di
ingresso x prefissata.
Analogamente, si può definire la complessità spaziale come il massimo
numero di celle del nastro di memoria utilizzate.

Definizione 9.6
Siano M, x, c0, ..., cr definiti come nella Definizione 9.5. La funzione complessità
spaziale SM di Avviene allora definita come:
376 Informatica teorica

k
SM <x)= ay \+l\i = l,...,r}
J=1

Notiamo che la definizione di Sm(x) prende in considerazione solo i nastri di
memoria, ignorando sia il nastro d’ingresso 17 che il nastro d’uscita Tq. Inoltre, è
importante osservare che Sm(x) può risultare finito anche se la computazione di M
non termina mai.
Al fine di capire meglio come si analizza la complessità delle MT,
consideriamo il seguente esempio.

Esempio 9.4
Si consideri una MT M che riconosce il linguaggio L = {wcwfl| M
riconosce tale linguaggio controllando l’uguaglianza dell’elemento z-esimo con
l’elemento (n - i +l)-esimo della stringa di ingresso, dove n è la lunghezza della
stringa e i = 1, 2, ... . Se tutte le verifiche di uguaglianza danno un risultato
positivo, la stringa viene accettata quando i = n - i + 1 e l’z-esimo elemento è una
c.
Escluso il nastro d’ingresso (il nastro Tz), a sola lettura, vi sono altri quattro
nastri, organizzati nel modo seguente:
Nastro T;: contiene una copia della stringa di ingresso.
Nastro T2: contiene il valore di z, codificato in forma unaria.
Nastro T5: contiene il numero di celle del nastro di ingresso non ancora
esaminate.
Nastro T4: contiene una copia dell’z-esimo elemento della stringa di ingresso.
La Figura 9.1 fornisce una descrizione formale di M, usando una
rappresentazione grafica condensata (già introdotta nel Capitolo 4), che consente di
rappresentare gli archi multipli mediante un unico arco dotato di un’unica etichetta.
La seguente descrizione dettagliata del comportamento di M si riferisce alla
descrizione formale mostrata in Figura 9.1.

«,<»,X,Y,ZO>I
e,<Zo,x,Y,zo>/
< »,X, Y,Z0>, < S,L,S,S,S>
<Z0.A,Y,Zq> ,<S,fì,L,L,S>

<Zo,Zo,Zo,Zo>, <S,R,R,XS>

(a)
La complessità del calcolo 377

D,<c,X,A,Zq>I
<c.X,V,Z0>,<S,S,S,L,S>
b,<z,Zo,Y,Zol>
<z,Z0, Y,Za>,< S.S.RS.S >

93

<y,X,g,y >, cS,RS,L,S>


H,<z,A,Y,ZqI
<z,A, Y,Z0>.<S,R,L,S,S>

(b)

»,<H,X,Y.y>l b,<y.b,Yy>l
<a,X, Y.y > ,<S,L,R,S,S> <y>8, Y,Z0> ,<S,S,S,S,S>

<z,X,Y,y> ,<S,R,S,S,S> <z,A,Y,y><S,L,R,S,S>

le)

&,<£,&,A,Zo>/ »,<Z0,X,Y,Z>/

b,<c,X,Zo,Zo>l
<c,X,Z0,Z0> ,<S,S,S,S,S>

te)
Figura 9.1 Una MT che accetta L = {wcwR | w e {a,b}*}. (a) Passi 1, 2.
(b) Passi dal 3.1 fino al 3.4. (c) Passi 3.5 e 3.6. (d) Passi dal 3.7 al
3.9. (e) Passo 4. z corrisponde ad a, b o c. y corrisponde ad a o b.
X, Y, Z corrispondono ad A, Zo o V> .
378 Informatica teorica

Passo 1: La configurazione iniziale è c0 = (qn, Tx, TZ0, TZ0, TZ0, TZ0), dove x è la
stringa in ingresso da riconoscere.
Passo 2\ Poniamo il contenuto del nastro T] pari a x, del nastro T2 pari ale del
nastro T3 pari a n, scandendo la stringa di ingresso da sinistra a destra.
Successivamente si ritorni alla prima posizione della stringa che è stata
copiata sul nastro Tb allo scopo di far partire l’effettivo processo di
riconoscimento. Il simbolo A viene impiegato come codifica unaria per i
nastri T2 e T3. Dopo questo passo, /V/si trova nello stato q2.
Passo 3: Si ripetano le seguenti azioni, dalla 3.1 alla 3.9
3.1 Si effettuino i mosse (z è il contenuto di T2) su Tb da sinistra a
destra (M entra nello stato qì).
3.2 Se il valore correntemente letto su Ti è una c, si esca dal ciclo (M
entra nello stato qA-
3.3 Si decrementi il valore codificato in T3 (se T3 è vuoto, M si ferma
senza accettare);
3.4 Si memorizzi il corrente valore di ingresso in T4 (M entra nello stato
?5)-
3.5 Si sposti la testina di T) da sinistra a destra, fino al primo simbolo
vuoto che segue la stringa di ingresso.
3.6 Si effettuino i mosse da destra a sinistra sul nastro Tg dopo aver
controllato che il simbolo corrente sia uguale al simbolo contenuto
in T4, la macchina entra nello stato q2.
3.7 Si decrementi il valore codificato in T3 (se T3 è vuoto, M si ferma
senza accettare).
3.8 Si incrementi il valore codificato in T2.
3.9 Si muova la testina di Ti da destra a sinistra, fino al simbolo Zo che
precede la stringa di ingresso.
Passo 4: La stringa è accettata se il simbolo corrente è una c e il contenuto di T3 è
zero.
Qual é la complessità temporale del riconoscimento di L = {wcwÀ| wg{a,b}*}2
Cerchiamo dapprima di ottenere una risposta sulla base di considerazioni
informali, per poi analizzarla formalmente. M effettua una doppia scansione
iniziale della stringa di ingresso x, di lunghezza n, per predisporre i nastri Ti e T2.
Successivamente effettua una doppia scansione per ciascuna coppia di simboli
nella posizione (1, rì), (2, n - 1), ... fino a che viene raggiunto il simbolo mediano
c. Il numero di mosse, quindi, è circa 2-n + 2-n-(n~ l)/2, dove n è la lunghezza di
x.
Cerchiamo ora di ottenere la medesima conclusione in modo più formale,
esaminando il grafo che rappresenta M. Il numero di mosse effettuate sulla catena
di stati q2, q3, q5, q6, q2, qx, q2, è pari a 2-n + 5 (non importa quale sia il valore della
z). Inoltre, questa catena viene eseguita m = (n - l)/2 volte. Infatti, alla (m + 1)-
379
La complessità del calcolo

esima iterazione, la catena, da q2, diventa q2, q-ì, q^, qp, il che corrisponde a m + 3
mosse. In conclusione, se x = wcwÀ per qualche we {a,b}* en = |x|, si ottiene:

T^x) = 2-n + 3 + (2-n + 5)-(w- l)/2 + (n- l)/2 + 3 = (2-n + 6)-(w + l)/2
Rimane da considerare ciò che accade quando la stringa in ingresso x non
appartiene a L.
Caso 1. x = 8. Qui si ha Ty(x) = 0.
Caso 2. |x| = 1 e x c. Qui si ha Tm(x) = 7.
Caso 3. x = Wiw'w2w"wiÀ per qualche wbw2 e {a,b}*, w'e {a, b}, w"e {a, b, c},
w' w”. Qui si ha

7aXX) = 2‘M + 3 + (|wi| - l)-(2-n + 5) + n + 2 + |W]|


Caso 4. x = wicw2 per qualche wi,w2e {a,b}*, e |wi| < |w2|. Qui si ha

T^x) = 2-n + 3 + (|wi| - l)-(2-n +5) + |wi| + 2


Consideriamo, infine, il problema, più semplice, della complessità spaziale. Il
numero di celle usate sui nastri Ti e T3 è pari a n + 2. Questo è un limite anche per
il numero di celle usate sul nastro T2. Il nastro T4, invece, usa solo una cella. In
conclusione, S^x) è limitato da 3 ■ (|x| + 2) + 1 per ogni x.

Traiamo ora alcune conclusioni dall’esempio precedente. Secondo le Definizioni
9.5 e 9.6, sia TM che Sm sono funzioni definite su I*. L’esempio ha però mostrato
che Tm dipende sia dalla lunghezza della stringa che dal suo valore. Ciò vale, in
generale, per le complessità spaziali e temporali. Infatti, come evidenziato sia
nell’introduzione di questo capitolo che nella Sezione 9.1, la complessità di una
soluzione dipende sia dal “contenuto” dell’ingresso che dalla sua dimensione; da
qui la necessità di introdurre i concetti di complessità nel caso medio, ottimo e
pessimo.
In pratica, tuttavia, considerando la complessità come una funzione definita su
I*, si introducono inutili complicazioni nell’analisi degli algoritmi. Decidiamo
dunque di basare le definizioni di complessità spaziale e temporale sulla lunghezza
della stringa (ossia il loro dominio è N invece di 7*). Per semplicità, useremo gli
stessi nomi di funzione TM e SM per indicare la complessità spaziale e temporale sia
quando il dominio è N che quando è I* : il contesto renderà sempre chiaro quale
dominio si sta considerando. Nel seguito ci si riferirà per lo più alla complessità
espressa in termini di lunghezza della stringa.

Definizione 9.7

Sia M una MT. La funzione di complessità temporale TM, con dominio N, si


definisce come:
380 Informatica teorica

TJn) = max {TJx) | |x| = n}

Analogamente, la funzione di complessità spaziale SM, con dominio N, si definisce


come:
S}/n) = max {S^x) | |x| = n}

La Definizione 9.7 correla la complessità spaziale e temporale alla lunghezza dei
dati in ingresso. Lo studio esatto di tali complessità risulta tuttavia a volte
inutilmente complicato e dettagliato, in quanto il reale interesse nello studio della
complessità risiede nel correlare la crescita di dimensione dell’ingresso con il
consumo delle risorse. In generale, quindi, nella pratica si studia il comportamento
asintotico della complessità, che si può descrivere mediante la notazione theta-
grande, che è stata presentata nella Sezione 9.1.2.
Se torniamo quindi ad analizzare l’Esempio 9.4, si può concludere che la
complessità temporale 7^ è ®(w2) e che quella spaziale SMè ®(ri).

ESERCIZI
9.5 Si consideri un’altra MT M2 che riconosce L = | we {a,b}*}. M2 copia
il contenuto del nastro T/ sul nastro Tb fino a che non incontra ima c.
successivamente legge all’indietro T! mano a mano che legge T/ e controlla i
simboli letti per verificarne l’uguaglianza. Si noti che, in questo caso, il
nastro Ti simula il comportamento di una pila e che l’intero processo di
riconoscimento assomiglia al processo di riconoscimento effettuato mediante
un automa a pila (si veda l’Esercizio 4.2le l’Esempio 9.6). Si progetti M2 e
si descrivano TM> e SM^, in ogni dettaglio, sia nel caso in cui il dominio è I*

che nel caso in cui il dominio è M. Si dimostri che TM> , la funzione di

complessità temporale con dominio M, è ®(n).


9.6 Si progetti una MT che riconosce L = {w® | we{a,b}*}. M>, dovrebbe
essere basata sulla definizione di Mdata nell’Esempio 9.4. Se x è una stringa
di ingresso, n la sua lunghezza e x,- indica il /-esimo simbolo di x, M
dovrebbe effettuare un controllo di uguaglianza per tutte le coppie (x^„_/+1),
per i < n - i + 1. Si dimostri che Tms è ®(«2).
9.7 * Si progetti una MT M4 per riconoscere L = {wcwA| w&{a,b}*}, che abbia
complessità spaziale ©(log n). Essenzialmente, M4 è derivata dalla macchina
originale M dell’Esempio 9.4 codificando i contatori in base binaria invece
che in base unaria. Tuttavia, sono necessarie due modifiche non banali per
proseguire nella costruzione di M4 con la desiderata complessità spaziale.
In primo luogo bisogna evitare la ricopiatura iniziale della stringa di ingresso
su Tj poiché ciò produrrebbe immediatamente una complessità spaziale
®(w). Dato però che, dopo il passo 2, Ti viene usato come nastro di sola
381
La complessità del calcolo

lettura, si può impiegare direttamente Tj al posto di Tb come nell’Esempio


9.4, sfruttando il fatto che nelle MT la testina di lettura può spostarsi sia a
destra che a sinistra. Questo piccolo “trucco” è tuttavia necessario se si
desidera ottenere la complessità desiderata.
In secondo luogo, è necessario che, ad ogni iterazione del ciclo, la testina di
Tj raggiunga le posizioni i e n - i + 1 senza richiedere che alcun’altra testina
legga più celle di un numero limitato da una funzione ©(log rì).
Una volta completato il progetto di Af4, si analizzi anche la sua complessità
temporale.
9.7 Si progetti una MT M che riconosca il linguaggio
L = {an'ban!b...an‘ c | k > 2,«j < «2 < ... < nk}
e tale che TM sia ©(«).

Avendo compreso che un problema P (ad esempio il riconoscimento di un


linguaggio) può essere risolto da macchine diverse con diverse complessità, ci si
potrebbe chiedere se ha senso definire una misura della complessità per il problema
in sé.
Nel caso del riconoscimento di un linguaggio, si potrebbe cercare di definire la
complessità “migliore” fra quelle ottenute dalle macchine che riconoscono L.
Sfortunatamente, questa definizione non ha senso, poiché è noto che, in generale,
diverse soluzioni possono risultare non confrontabili, anche dal punto di vista della
loro complessità theta-grande. Tuttavia vedremo, nella Sezione 9.3, che si possono
ugualmente compiere interessanti confronti sulla complessità del riconoscimento
dei linguaggi, senza esplicito riferimento a particolari macchine.
Torniamo ora al problema dell’appartenenza ad un insieme già affrontato
nell’Esempio 9.1, risolvendolo questa volta mediante una MT a k nastri.

Esempio 9.5
Definiamo una MT M che risolve il problema di appartenenza (si veda l’Esempio
9.1) per un insieme di caratteri di alfabeto I. La stringa in ingresso è x = ay, dove y
è una rappresentazione in forma di stringa dell’insieme ed a è un singolo carattere.
M accetta x ogni volta che a e {c | c è un carattere di y}.
Oltre al nastro di ingresso T/, M ha un altro nastro, Tb in cui memorizza il
primo carattere di x. La soluzione è mostrata in Figura 9.2 nella consueta forma di
grafo condensato.
Qual è la complessità temporale, in questo caso? Non è difficile ricavare la
seguente formula:
T^x) = ifx = <awi<2w2, Mq6(/- {a})*, w2<=I* then |tnvia| else |x|
Quindi 7)w(n) = n.
382 Informatica teorica

z ?= t
Figura 9.2 Una MT che risolve il problema di appartenenza, z e t indicano
simboli qualunque contenuti in I.

Il risultato ottenuto nell’Esempio 9.5 è lo stesso di quello osservato nell’Esempio


9.1, in cui si era impiegato un linguaggio di programmazione di alto livello come
modello computazionale. La principale differenza, però, risiede nel fatto che
l’algoritmo di ricerca lineare è il migliore che si possa realizzare con le MT: non è
possibile ottenere una migliore complessità temporale applicando la ricerca binaria
nel caso in cui l’insieme sia ordinato. Il nastro di una MT è, infatti, un dispositivo
ad accesso sequenziale, che non offre, quindi, l’accesso diretto in una unità di
tempo. Il lettore è invitato a verificare che l’esecuzione della ricerca binaria su una
MT condurrebbe ad una complessità temporale addirittura peggiore di quella della
ricerca lineare.

ESERCIZI
9.8 La MT dell’Esempio 9.5 risolve un problema leggermente diverso dalle
funzioni degli Esempi 9.1 e 9.2. Infatti, in questo caso si cerca un singolo
carattere, mentre in precedenza si cercava un numero intero. Si modifichi la
MT M dell’Esempio 9.5 affinché cerchi numeri interi codificati in binario.
Più precisamente, la nuova macchina Mi deve riconoscere il linguaggio
L = {x|x = z$yi$...$ym, e 3z, l<i<m, tale che z = yt}
Per semplicità, si supponga che z e y, abbiano tutti la medesima lunghezza e
si calcoli Tm2 sia come funzione di x che come funzione del numero di
elementi m dell’insieme che si vuole esaminare.
9.9 * Si elimini l’ipotesi semplificatrice nel precedente esercizio e lo si risolva di
nuovo.

L’Esempio 9.5 ed i relativi esercizi confermano un importante risultato, già


accennato nella Sezione 9.1.1: la complessità di un problema dipende dal modello
computazionale scelto. La complessità nel caso pessimo del problema di ricerca in
un insieme ordinato è ©(log rì) se si sceglie una macchina astratta lava come
modello computazionale; è invece ©(n) se ci si riferisce alle MT.
383
La complessità del calcolo

Questa osservazione andrebbe confrontata con la tesi di Church, che si riferisce


alla risolvibilità dei problemi. Come è noto, essa afferma che, se un problema si
può risolvere in lava, allora può anche essere risolto da una MT e viceversa. Tale
risultato non si estende alla complessità: se un problema è risolto da un programma
lava con una funzione di complessità T, ciò non garantisce che una MT possa
risolvere il medesimo problema con la medesima funzione di complessità.
| Tuttavia, come si vedrà nelle Sezioni 9.2.3 e 9.4, le complessità che si riferiscono a
modelli computazionali differenti si possono correlare l’una all’altra in modo
sistematico.
Nella Sezione 9.1.1 si è menzionato un altro fattore che può influenzare le
misure di complessità: la codifica dei dati. E infatti possibile correlare la
complessità alla lunghezza della stringa di ingresso; tuttavia la stringa di ingresso
viene impiegata per rappresentare i dati in ingresso in forma codificata. Se la
codifica cambia, si può ottenere una differente complessità per lo stesso algoritmo.
Un esempio di ciò viene fornito dalla macchina descritta nell’Esempio 9.3 e dalla
sua modifica, presentata nell’Esercizio 9.6, che ne influenza enormemente la
complessità spaziale. La codifica dei dati ha quindi un evidente e spesso pesante
impatto sulla complessità computazionale. Per il momento ignoriamo questo
problema, continuando la discussione con l’esame delle MT che riconoscono
' linguaggi, senza alcuna ulteriore supposizione sul fatto che le stringhe in ingresso
rappresentino effettivamente altre entità in forma codificata.
Tuttavia, per comprendere meglio come la scelta della codifica impatti su tutta
la soluzione e di conseguenza anche sulla complessità spaziale e temporale,
' consideriamo il seguente esempio.

Esempio 9.6
Consideriamo ora una MT in grado di riconoscere il linguaggio L = {anbn | n > 1}.
Tale macchina può essere costruita facilmente simulando il comportamento di un
AP che riconosce lo stesso linguaggio: il numero-di a del nastro in ingresso viene
salvato in unario sul nastro di memoria e tale numero viene poi confrontato con il
numero di b. La complessità temporale risultante sarebbe &(n) (viene eseguita una
transizione per ogni simbolo in ingresso), che è facile capire essere la migliore
ottenibile. Ovviamente, poiché il numero di a viene salvato in unario, anche la
complessità spaziale risulta essere ®(n).
Qualora volessimo minimizzare la complessità spaziale, conviene contare in
codifica binaria (o equivalentemente, in altra base > 1) il numero di a ricevute e poi
decrementare il contatore binario per ogni b ricevuta. La complessità spaziale
sarebbe così 0(log n), ottenuta però al prezzo di un peggioramento di un fattore
logaritmico della complessità temporale (la verifica di quest’ultima affermazione
viene lasciata in esercizio al lettore).

384 Informatica teorica

ESERCIZIO
9.10 Si costruiscano MT che risolvono i problemi sotto elencati e si analizzi la
complessità delle loro computazioni. Si cerchi di stabilire se e come i
risultati ottenuti si possono migliorare.
1. Sottrazione di due interi codificati in base 2.
2. Sottrazione di due interi codificati in base unaria.
3. Riconoscimento del linguaggio L = {anbncn\ n > 1}
4. Riconoscimento del linguaggio L = {anbn2\ n > 1}
5. Riconoscimento del linguaggio L = {anbn\ n > 1} u {a"h2"| n > 1}

9.2.2 La complessità della risoluzione di problemi mediante altri automi


Finora si è analizzata la complessità della risoluzione di qualche semplice
problema per mezzo di MT multinastro. In che misura si possono generalizzare i
risultati ottenuti? Le formule di complessità subiscono modifiche se si cambia la
macchina astratta che calcola la soluzione del problema? Se la risposta a
quest’ultima domanda fosse affermativa, ci si deve attendere un cambiamento
radicale? In un certo senso, queste domande tentano di trovare, per la complessità
computazionale, un enunciato analogo alla tesi di Church. Poiché si sono raggiunti
risultati di validità generale nel campo della risolvibilità dei problemi e poiché la
complessità si può considerare come un raffinamento del concetto di decidibilità,
vi sono fondate speranze di poter rispondere a queste domande. A tale scopo,
cominciamo ad analizzare la complessità della risoluzione dei problemi mediante
altri formalismi, scelti fra quelli introdotti nel Capitolo 4: gli automi a stati finiti,
gli automi a pila e le MT a nastro singolo. Analogamente a quanto fatto nella
precedente sezione consideriamo per il momento solo modelli deterministici.
9.2.2.1 La complessità delle computazioni degli automi a stati finiti
Sia A un automa a stati finiti. Si definisce Tfix) come l’intero i tale che òl(q0, x) = q
per qualche q, se ne esiste qualcuno, cioè come il numero di transizioni effettuate
per processare la stringa in ingresso x a partire dallo stato iniziale. Se 8*(^0, x) è
indefinita, convenzionalmente si pone Ta(x~) = |x|, cioè se a un certo punto della
lettura dell’ingresso l’AF si blocca perché dallo stato corrente non vi è alcuna
transizione etichettata con il simbolo corrente in ingresso, la complessità temporale
è convenzionalmente pari alla lunghezza della stringa1. Ta(x) indica evidentemente
il numero di mosse (transizioni di stato) compiute da A durante il riconoscimento.
Ritornando alla Definizione 4.1 e constatando che l’AF effettua esattamente un

1 Questa convenzione è dettata dal fatto che per gli AF una 8 parziale può essere vista come
l’abbreviazione di una 8 totale che porti l’automa in uno stato di errore e ivi lo faccia
rimanere laddove la 8 parziale non fosse definita.
385
La complessità del calcolo

passo per ogni simbolo di ingresso, si può calcolare la complessità temporale nei
seguenti termini.

Enunciato 9.3
Per ogni AF e per ogni x, TA(x) = |x|

Vale la pena di puntualizzare l’importanza dell’Enunciato 9.3, che afferma che per
qualsiasi AF la complessità temporale cresce linearmente con il crescere della
lunghezza della stringa in ingresso. Diversamente dagli esempi precedenti, in
questo caso un’intera classe di modelli ha una formula di complessità determinata
(e molto semplice), indipendentemente dal particolare problema da risolvere.
Si noti anche che è immediato simulare gli AF mediante opportuni AP o MT,
in modo tale che valga ancora la medesima formula di complessità. Più
precisamente, una MT che simula un AF può impiegare |x| + 1 mosse per decidere
se x e L, poiché una MT può accettare la stringa di ingresso solo se si trova in una
configurazione di arresto, mentre un AF non richiede che uno stato finale sia uno
stato di arresto. Si ottiene quindi il seguente enunciato generale.

Enunciato 9.4
I linguaggi regolari possono essere riconosciuti da AF, AP e MT in un numero di
mosse pari a |x| o a |x| + 1, dove x è la stringa di ingresso da analizzare.

L’Enunciato 9.4 afferma che poiché la complessità temporale di un AF è sempre
pari alla lunghezza della stringa in ingresso è possibile simulare un AF con un AP
o con una MT senza cambiare la complessità dell’AF, a meno al più di una “extra­
mossa” finale.
La complessità spaziale per gli AF è un problema molto semplice. È
sufficiente considerare che un AF non è altro che una MT a k nastri con k = 0. Si
ottiene quindi che SA(x) = 0 per ogni AF A e per ogni stringa di ingresso x. Questo
risultato sorprende solo in apparenza. Infatti gli AF sono realmente dispositivi a
memoria finita, potendo memorizzare solo una quantità finita e limitata a priori di
informazione attraverso gli stati, mentre S(x) si riferisce alla memoria illimitata
funzione della stringa di ingresso necessaria per la sua elaborazione (escludendo
quindi la memoria finita dell’unità di controllo).
9.2.2.2 La complessità delle computazioni degli automi a pila
Analizziamo ora la complessità temporale per un dato AP A, dapprima in funzione
della stringa in ingresso (T)(x)) e successivamente in funzione della sua lunghezza
Wj).
Come per gli altri tipi di automa, quando si calcola la complessità temporale
in funzione della stringa di ingresso, si contano i passi che portano da una

i
386 Informatica teorica

configurazione iniziale a una configurazione finale, se tale configurazione esiste;


quando invece si considera come parametro la lunghezza della stringa in ingresso,
la complessità temporale è definita come il massimo di tutte le complessità
temporali in funzione di stringhe d’ingresso della lunghezza considerata.

Definizione 9.8
Sia A un AP. Per ogni x e I*, sia c0 = (qr0, x, Zo) una configurazione iniziale, c0 >- cx
>- ... cr la sequenza di transizioni che partono da c0; e cr una configurazione di
arresto (di accettazione o di rifiuto per x), se ne esiste una. Se A si ferma, si ponga
TA(x) = r; altrimenti si ponga TA(x) = oo, se non esiste alcuna configurazione di
arresto.
TA(n) = max{7^(x) | |x| = n}

Analizziamo ora, considerando sia la stringa che la sua lunghezza come
possibili argomenti, la complessità spaziale.

Definizione 9.9
Sia Co = (qo, x, Zo) = (qh x,, y;)h-...,-cr la sequenza di transizioni effettuate da
un AP A quando opera su di un ingresso x e Z* (la sequenza può essere infinita se
l’AP non si ferma). Allora:
ó'/x) = max{|y,| | i= 1, ..., r}
SA(n) = max{ó)i(x) | |x| = n}

La complessità spaziale di un AP corrisponde quindi alla porzione della pila
necessaria per effettuare la computazione. Come per gli AF e le MT, la memoria
finita dell’unità di controllo non viene considerata.
Per capire meglio queste definizioni in pratica, consideriamo nuovamente
l’Esempio 9.4, studiandolo però dal punto di vista della complessità degli AP.
387
La complessità del calcolo

O.A/AA
Figura 9.3 Un AP che accetta {wcwA}.

Esempio 9.7
L’AP descritto in Figura 9.3 (si veda l’Esercizio 4.21) riconosce il linguaggio L =
itwcwR | w e {a,b}*}, che è stato esaminato anche nell'Esempio 9.4.
La determinazione di TXx) (e di TA(n)) è molto semplice, poiché A non
effettua alcuna s-mossa, se si eccettua l’ultima. Quindi TA(x) = |x| + 1 (cioè la
lunghezza della stringa in ingresso più la mossa necessaria per spostarsi nello stato
finale) se x e L(A) e, in generale, TA(n) = n + 1. Anche SA(n) è molto facile da
ottenere. I simboli di ingresso sono impilati fino a che si incontra c; essi vengono
successivamente disimpilati (se x e E). Quindi SA(x) = (|x| - l)/2 se x e L e, in
generale, SA(n) = n.

Questi risultati non sono assolutamente sorprendenti. In generale, è facile
dimostrare che, per ogni AP A, si può costruire una MT a 1-nastro che lo simuli e
che abbia le medesime formule di complessità spaziali e temporali2.
Diversamente dagli AF, gli AP possono però effettuare s-mosse. Per questo
motivo, la semplice formula che esprime la complessità temporale negli AF non
vale, in generale, per gli AP. Si intuisce tuttavia che è ugualmente possibile
esprimere la complessità di un qualsiasi AP mediante una formula abbastanza
semplice. Si noti dapprima che, se l’automa non presenta cicli, è possibile
supporre, senza alcuna perdita di generalità che TA(n) sia finita per ogni n. Grazie
al Teorema 4.13, che afferma che per ogni AP esiste un AP equivalente aciclico,
tale risultato può essere generalizzato per tutti i linguaggi non contestuali
deterministici.

2 Come accade per gli AF, una MT che simula un AP può richiedere un passo
supplementare per entrare in uno stato finale.
388 Informatica teorica

Teorema 9.5
Ogni linguaggio L, non contestuale e deterministico, può essere riconosciuto da un
AP A con complessità temporale TA(rì) = kA-n, dove n indica la lunghezza della
stringa di ingresso e kA è una costante opportuna.

Dimostrazione
Come si è detto, è possibile supporre, senza alcuna perdita di generalità, che A sia
privo di cicli. Per q e Q, Z e T, sia CqZ il massimo numero di mosse di una
sequenza di transizioni di A del tipo {q, e, Z'y-'lp, e, aZ), a e T*, ossia la lunghezza
della più lunga sequenza di s-mosse che non tolga Z dalla cima della pila. Poiché A
è privo di cicli, CqZ risulta finito per ogni q e per ogni Z.
Sia C = max{CgZ| q e Q, Z e T}, h = max{|a| | ò(q, a, Z)={q', a) V ò(q, e, Z) =
(q', a), per q e Q, Z e T}, ossia la massima lunghezza delle stringhe impilatali in
una sola mossa. Si noti che sia C che h sono effettivamente calcolabili.
Si osservi a questo punto che, nella sequenza {q0, x, Zo) l~*dA {q, e, y) vi sono
esattamente |x| transizioni del tipo (p, ay, y) >- (p',y, y')e, al più, C-|x| transizioni
del tipo Zyi) >- {p',y, yzy\). Tutte le altre transizioni sono del tipo
(p,y,Zyx}^(p',y, yj).
Le transizioni dell’ultimo tipo non possono quindi essere più di h ■ |x| + h ■ C- |x|.
In questo modo, la lunghezza totale della sequenza di transizioni è limitata da
(1 + h + h-C + C) ■ |x|.

Corollario 9.6
Ogni linguaggio Z, non contestuale e deterministico, si può riconoscere con una
complessità spaziale 0(n).
’ ■
La dimostrazione, banale, è lasciata al lettore per esercizio. Grazie al Teorema 9.5
e al Corollario 9.6 possiamo quindi concludere che sia la complessità temporale
che la complessità spaziale necessaria per riconoscere un linguaggio non
contestuale deterministico sono lineari rispetto alla lunghezza della stringa in
ingresso.
9.2.2.3 La complessità delle MT a nastro singolo
Studiamo adesso la complessità delle computazioni effettuate dalle MT a nastro
singolo. Si otterranno alcuni interessanti risultati ed alcune osservazioni in un certo
senso sorprendenti. Le complessità spaziale e temporale per le MT a nastro singolo
risultano ovviamente definite come per le MT multi nastro (Definizioni 9.6 e 9.7).
La complessità del calcolo 389

Una prima conseguenza sorprendente si ricava dalla definizione di complessità


spaziale. Infatti, S^x), che corrisponde al massimo numero di celle del nastro (di
memoria) occupate da M durante la computazione corrispondente all’ingresso x,
non può essere mai minore di |x|. Questo è dovuto al fatto che nella MT a nastro
singolo, l’unico nastro è sia di ingresso, che di memoria (e d’uscita). Ciò significa
che è l’unico nastro di cui dobbiamo analizzare l’occupazione per calcolare la
complessità spaziale, e, poiché contiene inizialmente la stringa in ingresso, la
complessità spaziale deve essere almeno equivalente alla dimensione dell’ingresso.
Ciò non vale, ovviamente, per le MT multinastro, gli AF e gli AP, dove il nastro in
ingresso e i dispositivi di memoria sono organi distinti.
In modo un po’ meno banale, si possono poi ottenere nuovi risultati
relativamente alla complessità temporale. Si consideri di nuovo l’esempio, ormai
familiare, del linguaggio: L = {wcwÀ| we {a,b} }. Una MT Ma nastro singolo che
riconosce L effettua i seguenti passi.
Passo 1: M raggiunge il carattere c della stringa di ingresso x, se esiste.
Passo 2: Sia z la posizione della cella che contiene c. M alternativamente controlla
l’uguaglianza dei caratteri contenuti nelle posizioni z - 1 e z + 1, z - 2 e
z + 2 e così via. Dopo il controllo, M scrive un carattere speciale nella
cella, per marcare il fatto che il contenuto di quella cella è già stato
controllato.
Passo 3: M riconosce x se tutti i controlli di uguaglianza hanno avuto successo e se
vengono trovati simboli vuoti nelle posizioni z + m, z - m dove
m = (|x| + l)/2.

ESERCIZIO

9.11 Si esponga in dettaglio la costruzione di Me si dimostri che TM è ®(m2).

Il risultato dimostrato nell’Esercizio 9.11 afferma che una MT a nastro singolo che
riconosce L ha la stessa complessità theta-grande della macchina multinastro
dell’Esempio 9.4.
Lo stesso problema però, risolto ancora con una MT multi nastro che opera
come la macchina M2, descritta nell’Esercizio 9.4, permette di arrivare a una
soluzione con una complessità lineare. Se il lettore ora provasse a simulare tale
macchina con una MT a nastro singolo, mantenendo lineare la sua formula di
complessità, scoprirebbe che tale problema non è affatto semplice; anche un
tentativo di ottenere una complessità lineare con una diversa macchina a nastro
singolo - nonostante ciò sia ottenibile mediante un AP e una macchina a più nastri-
sarebbe destinato al fallimento. Il seguente teorema, infatti, dimostra che tale
obiettivo non è proprio raggiungibile.
390 Informatica teorica

Teorema 9.7
Nessuna MT M a nastro singolo può riconoscere L = s,wcwR | w&{a,b}*} con
O(Tm) < ®(n2).

Dimostrazione *
La dimostrazione di questo teorema non è banale ed è basata sulla nozione di
sequenza di attraversamento, che ha consentito di ricavare molti risultati relativi
alla teoria della computazione. Intuitivamente, una sequenza di attraversamento
della computazione di una MT è la sequenza degli stati in cui la macchina
attraversa il confine fra due celle consecutive durante la computazione (si veda la
Figura 9.4). La Figura 9.4 mostra il nastro, il cammino seguito dalla testina, nonché
lo stato presente ogni volta che la testina attraversa il confine tra le celle z e z + 1.
Nel caso della Figura 9.4, la sequenza di stati qt ,qt + j , qt +k è la sequenza di
attraversamento del confine fra le celle z e z + 1. Formalmente, una sequenza di
attraversamento viene definita nel modo seguente.

Definizione 9.10 (sequenza di attraversamento)


Sia c0 ■- ci >- ... >- ct >- ... una sequenza di transizioni di una MT M a nastro singolo,
{q(i)\ t = 0, 1, ...} la sequenza di stati in cui M è entrata e {z(t)| t = 0,1,...} la
sequenza delle posizioni della testina agli istanti 0, 1, ..., t (per convenzione si pone
z(0)=l).
La sequenza di attraversamento SA(z), associata alla posizione z, è la sottosequenza
{q(t])} di {q(t)} per cui si verifica una delle due condizioni:
a. z(tk) = z e z(tk + 1) = z + 1
b. ziti) = z + 1 e z(fi + 1) = z

Una volta definita formalmente una sequenza di attraversamento, prima di
addentrarsi nella dimostrazione del Teorema 9.7, bisogna premettere qualche
lemma ausiliario.

Lemma 9.8
Se una MT M, a nastro singolo e deterministica, non ha nessuna mossa in cui
ò(q, i) = (q , i', S) per qualche q' e f, allora il tempo speso da M sulla stringa di
ingresso w è pari alla sommatoria delle lunghezze delle sequenze di
attraversamento di tutte le z. Nel caso generale, il tempo di computazione di M è
limitato da una funzione lineare di tale sommatoria, purché M non possa effettuare
una sequenza illimitata di mosse in cui la testina rimane ferma (5-mosse).
391
La complessità del calcolo

Traccia della dimostrazione


La prima parte dell’enunciato può essere immediatamente ricavata da un esame
della Figura 9.4. Infatti, se Mnon può effettuare 5-mosse, ciascuno stato appartiene
soltanto ad ima SA(z).
La seconda parte dell’enunciato deriva dal fatto che Mpuò effettuare un numero di
+00

5-mosse consecutive limitato da un intero k. Quindi, se AS (x) è pari a r, il


Z~— 00

numero di mosse di M è al più kr. Notiamo che, per ogni MT M, si può facilmente
costruire una M' equivalente che effettua solo un numero limitato di S-mosse
consecutive, tale che T>An) > TM(n) per ogni n.

Lemma 9.9
Sia M una MT a nastro singolo deterministica. Si supponga che, al termine della
computazione, M si fermi sempre alla destra delle celle che originariamente
contenevano i dati in ingresso. Allora, se M accetta WiW2 e jSzdQwxl) = £4(|xi|),
quando M riceve in ingresso x,x2 (ossia quando la sequenza di attraversamento del
confine fra nj e w2 è la medesima che fra Xi e x2), allora M accetta xiw2.

Figura 9.4 Una sequenza di attraversamento.


392 Informatica teorica

Traccia della dimostrazione


Sia |wi| = z e |%i| = y. Inoltre, sia SA(z) = {q,i,..., q^}, quando Mriceve in ingresso
wiw2, e sia SA(y) = {gu,---, qa}, quando Mriceve in ingresso xi%2.
Si consideri ora il comportamento di M in corrispondenza di %iw2. La prima
volta che Mattraversa il confine fra le posizioni y e_y + 1, essa entra nello stato qiX.
La sua mossa successiva sarà quindi identica a quella effettuata dopo il primo
attraversamento del confine fra le celle z e z + 1, in corrispondenza dell’ingresso
wiw2. La medesima situazione si verifica in corrispondenza del terzo
attraversamento dello stesso confine (la seconda volta il confine viene oltrepassato
nella direzione opposta), del quinto e così via. Si noti che k deve essere dispari
poiché M si ferma alla destra di wiw2. Quindi, ogni volta che M raggiunge la
porzione di nastro occupata da w2, le sue mosse sono le stesse che avrebbe
compiuto per Wiw2, il che dimostra la tesi. Si noti che ogni MT si può modificare
per assecondare Tipotesi del lemma, aumentando la sua TM con un termine che sia
©(»).

Dimostrazione del teorema principale


Si supponga che valgano le ipotesi del Lemma 9.8 e del Lemma 9.9, ossia che M
non abbia alcuna transizione per cui ò(q, i) = {q', i', S) e che, se la stringa
d’ingresso viene accettata, la testina si trovi alla destra delle celle contenenti
originariamente tale stringa. Queste supposizioni non provocano alcuna perdita di
generalità. Infatti, come si è visto nelle dimostrazioni dei lemmi precedenti, se una
MT Mha una complessità temporale TM, allora si può costruire una Mr equivalente
a M, che soddisfi l’ipotesi dei lemmi e per la quale 0(7^-) < 0(7^).
Si consideri una stringa wcwR e sia |wcwÀ| = n, cosicché \w\ = m = (n - l)/2.
Sia lw,z = |£4(z)|, 1 < z < m dove SA(z) si riferisce alla computazione di una MT che
riconosce wcwK. Sia a(z) la media di Zw, su tutte le stringhe w, con |w| = m, ossia:

Si noti che, almeno per la metà di tutte le parole w, lw z < 2a(z~) per ogni z. Infatti,
se per più di metà di tali parole Zw,z fosse maggiore di 2a(z), anche nel caso in cui la
rimanente Zwz fosse 0, si avrebbe
'-■2-a(z)-2m

a(z) > -- ------------------------- = a(z)


2m
Ciò tuttavia conduce ad una contraddizione. Quindi, poiché vi sono esattamente 2m
parole w diverse, esistono almeno 2m~I parole in cui Zw z < 2a(z) .
393
La complessità del calcolo

Per ogni i, il numero di sequenze di attraversamento diverse aventi lunghezza z è


pari a |g|\ dove Q è l’insieme degli stati di M. Quindi, il numero di sequenze di
2-a(z)
attraversamento diverse, non più lunghe di 2a(z) è I Q l' • Si noti che, per ogni
z=i
k
k > 1 e n > 2, nr < nk + ì. Ciò si può facilmente dimostrare mediante
Z=1

k
induzione rispetto a k, poiché, per k = 1, n<«2 e < zz * +1 implica
Z=1

£+1 k
z v"1 ' k+1 . £+1 k+1 . k+ 2
E
z=l
n = 2, n + n
Z=1
< n +n < n

Poiché |2| è necessariamente maggiore o uguale a 2 (altrimenti M non potrebbe


accettare L), il numero di sequenze di attraversamento diverse aventi lunghezza
non superiore a 2a(z), è minore o uguale a |g|2-o(z)+1. Esistono allora almeno

---------------- parole w con la stessa SA(z), per ogni z. Si consideri la partizione


। q |2-a(z) + l
w= wz con |wz| = z, | w _ |= m - z . Per ogni z, il numero di parole diverse w z è
pari a 2" Si possono ora verificare due casi, a seconda che sia valida o meno la
disuguaglianza seguente:
~ w -1
m z
। q |2-a(z) + l
Nel primo caso, il numero di parole w diverse, aventi la stessa SA(z), risulterebbe
maggiore del numero delle parole z. Quindi, almeno due di loro dovranno
differire nella parte wz, ad esempio w' = w'z w'z, w" = w" z w"z, con w'z * w" _.
Allora, per il Lemma 9.9, poiché M accetta w'cw'R, deve anche accettare
w'z w'z cw's : una contraddizione.
Necessariamente, quindi:
~ m -1
< 2m~z
। q |2-a(z)+l
ossia,

. 2-log2(|2|) 2
In virtù del Lemma 9.8, il tempo impiegato da A/per il riconoscimento di x = un/
è
394 Informatica teorica

m
'y', ^w,z
z = -co z=1
Quindi, il tempo medio speso da M per riconoscere tutte le stringhe x = -wcwR, con
|x| = n è pari a:
ym i
,V' z = \ w’z
E
\w\=m
?m
2
2m

iw z m
m

z
Zj z = l^J |w|=w z-1 m
2m 21og|g| 2
Z=1 Z=1

m
1 m m
2log \ Q\
Z-
Z=1
21og|g| 2

m(m -1) m 1
\Q |+ 2
4 log
2log iei

Ovviamente, la più lunga computazione sulle stringhe di lunghezza n non può


essere più breve della computazione media. Quindi TM è ®(n2) e la tesi è
finalmente dimostrata.

Il Teorema 9.7 mostra un fatto abbastanza sorprendente. Sebbene le MT a nastro
singolo abbiano la massima potenza computazionale, per quanto riguarda la classe
dei problemi che possono risolvere, qualche volta risultano più lente di altri
formalismi meno potenti. La ragione risiede nel modo in cui le MT a nastro singolo
usano il loro nastro. Poiché il nastro viene utilizzato contemporaneamente da
ingresso, uscita e memoria ausiliaria, molti movimenti in avanti e indietro della
testina sono dovuti solo alla necessità di raggiungere l’informazione ausiliaria. Ad
esempio, nel caso di L = {wcwA}, l’automa a pila deve semplicemente paragonare i
simboli in ingresso, successivi alla c, con i simboli in cima alla pila (e disimpilarli).
Al contrario, una MT a nastro singolo, mentre sta leggendo wR, deve cercare il
simbolo corrispondente che si trova immagazzinato da qualche parte sul nastro,
magari lontano. Si noti che lo stesso inconveniente non vale per le MT multinastro
che, come è noto, sono più potenti degli altri automi anche dal punto di vista della
complessità computazionale. Il seguente problema ne è un’ulteriore esempio.

Esempio 9.8

Consideriamo il problema di riconoscere il linguaggio L = {w | w g {a, b}*}


utilizzando una MT Afa nastro singolo.
395
La complessità del calcolo

La macchina M deve, in primo luogo, individuare la metà della stringa in ingresso.


A tale scopo, viene effettuata una prima passata della stringa, memorizzando la sua
lunghezza alla sua destra. Durante questa passata la macchina dovrà tenere traccia
del carattere fino a dove si è contato, per esempio cambiandolo da a ad a', e da b a
b', e riconvertendolo nel carattere originale prima di passare a contare il prossimo
carattere. Più precisamente, quando si legge un carattere della stringa in ingresso,
esso viene convertito in a ’ o b ’ a seconda del carattere originale, per poi arrivare in
fondo alla stringa (dove si trova il conteggio della lunghezza dell’ingresso) e
aggiungere un simbolo per contare anche l’ultimo simbolo letto. Poi la testina
viene riportata nella posizione corrispondente al simbolo appena letto (n ’ o b ’), che
viene riconvertito nel simbolo originale, prima di passare al conteggio del simbolo
successivo. Ciò richiede un tempo 0(«) per ogni carattere letto e quindi 0(«2) per
l’intera stringa.
Una volta memorizzata la lunghezza della stringa a destra della stessa, per
ogni coppia di caratteri della stringa che memorizza la lunghezza dell’input,
un’opportuna marca all’interno della stringa di input (ad esempio sostituendo
nuovamente il carattere a con il carattere n’ e b con .&’) viene spostata a destra di
una posizione. Al termine la marca si troverà a metà della stringa di ingresso.
Anche questa macro-operazione richiede un tempo 0(n2) (0(«) per ogni coppia di
caratteri).
A questo punto si marca anche il primo simbolo in ingresso e lo si confronta
con il simbolo marcato a metà stringa; se sono uguali, si spostano a destra di una
posizione entrambe le marche. Si prosegue con il confronto dei simboli marcati,
spostando di volta in volta le marche, fino a che la seconda marca non raggiunge la
fine della stringa. Anche questa macro-operazione richiede un tempo 0(«2) (0(«)
per ogni confronto).
Si osservi che non è necessario controllare la posizione della prima marca
quando la macro-operazione di confronto è finita, perché il controllo sulla
lunghezza della stringa (che deve essere necessariamente pari) viene fatto quando
si trova il simbolo mediano della stringa di ingresso.
Lo stesso linguaggio può essere riconosciuto con complessità 0(«) utilizzando
una MT con due nastri, in cui nel primo viene salvata la lunghezza della stringa e
nel secondo w. Brevemente, con una prima passata viene salvata sul primo nastro
la lunghezza della stringa; poi, con una seconda passata e un procedimento analogo
a quello utilizzato per la macchina a nastro singolo, si individua il centro della
stringa e si copia la seconda metà della stringa nel secondo nastro. Infine, con una
terza passata si confronta il contenuto del secondo nastro con la prima metà della
stringa in ingresso. Ogni passata costa 0(«), poiché la lettura dell’ingresso e la
scrittura della memoria possono essere fatte contemporaneamente, senza dover
scorrere tutto l’ingresso.

Avendo quindi appurato la generalità del modello MT multinastro, concentriamo


l’attenzione su alcune proprietà generali relative alla complessità di questo
396 Informatica teorica

modello. La Sezione 9.2.3 ne presenta alcune, che dovrebbero chiarire alcuni


aspetti fondamentali della complessità computazionale, senza peraltro pretendere
di fornire un panorama completo di tale argomento.

ESERCIZI
9.12 * Si dimostri che nessuna MT a nastro singolo può riconoscere L = {wws}
con una complessità temporale minore di 0 (n2).
9.13 Si costruiscano MT a nastro singolo che risolvano i problemi elencati
nell'Esercizio 9.10 e si controlli se il loro comportamento asintotico sia
cambiato o meno rispetto alle MT multinastro.

9.2. 3 Alcune proprietà generali della complessità delle MT multinastro


I risultati presentati in questa sezione dovrebbero chiarire l’utilità dei modelli
formali nella comprensione dei concetti relativi alla complessità computazionale.
Le sezioni successive saranno invece dedicate ad argomenti più pratici, orientati
alla programmazione.
Come primo passo, estendiamo alcune definizioni di complessità, introdotte
per modelli deterministici nelle precedenti sezioni, alle computazioni non
deterministiche.

Definizione 9.11
Sia M una MT multinastro non deterministica che accetta il linguaggio L. M ha
complessità temporale TM (e, rispettivamente, complessità spaziale SM) con
dominio I se, per ogni stringa di ingresso x, x e L se e solo se esiste almeno una
computazione di M che accetta x in T^x) mosse (rispettivamente, usando S^x)
celle di memoria) e nessun’altra computazione accetta x in meno di Z^x) mosse
(rispettivamente, usando meno di ST.x) celle del nastro di memoria). TJn)
(rispettivamente, viene definita come il massimo di Z«(x) (rispettivamente,
S>AXA su tutte le x e Z di lunghezza n.

Avvertimento *
Il lettore attento probabilmente non si è lasciato sfuggire alcuni punti cruciali della
definizione precedente. In primo luogo, la definizione si riferisce alle MT usate
come accettori di linguaggi, il che è una restrizione rispetto ai loro usi potenziali.
La definizione è coerente con la consueta modalità mediante la quale i dispositivi
non deterministici vengono utilizzati come riconoscitori di linguaggi. Fra le molte
computazioni possibili, siamo interessati a trovarne almeno una che conduca
all’accettazione. Sembra perciò naturale concentrare la definizione di complessità
su tale computazione.
La complessità del calcolo 397

Nel caso in cui, invece, si sia interessati alle MT come traduttori di linguaggi (ossia
al calcolo di tutti i valori di una traduzione a più valori r(x) effettuato da una MT
non deterministica), la filosofia del “caso pessimo” dovrebbe condurre alla scelta
della computazione più lunga fra quelle relative alTingresso x. Questa scelta, però,
comporterebbe qualche complicazione nel caso in cui alcune computazioni su x
non terminasssero. In ogni caso, qui focalizziamo l’attenzione sul riconoscimento
dei linguaggi, per uniformità con la letteratura esistente.
Un’altra spiacevole conseguenza della Definizione 9.11 è che essa non si
riduce alla Definizione 9.5 nel caso particolare in cui M è deterministica. Infatti, se
x g L, la definizione è soddisfatta da ogni valore di TM. Ad esempio, se T^n) = m2,
per qualche x £ L, M può impiegare più di |x|2 mosse ed entrare addirittura in
computazioni senza fine, secondo la Definizione 9.11. Ciò non è consentito,
invece, dalla Definizione 9.5.
Si può quindi adottare, in alternativa, la seguente definizione.

Definizione 9.1 la
Sia M una MT non deterministica. T^x) è tale che nessuna computazione
effettuata sull’ingresso x impiega più di T^x) mosse e almeno una ne impiega
esattamente TU-U S^x), T^n) e S^n) sono definite conseguentemente.

Tale definizione, però, potrebbe risultare in contrasto con le osservazioni
precedenti; per questo motivo si preferisce, generalmente, la Definizione 9.11.
Nella maggior parte dei casi interessanti, tuttavia, la differenza fra le due
definizioni non è molto grande. Si noti dapprima che, se TM è ima funzione totale
computabile, il linguaggio accettato da M è ricorsivo. Infatti, per stabilire se
x e L(M), è sufficiente prendere in considerazione tutte le computazioni di
lunghezza minore o uguale a T^n), in cui n = |x|. Tali computazioni sono
necessariamente un numero finito. Si può dunque supporre, senza perdita di
generalità, che M si fermi sempre. Analogo ragionamento vale se T^n) <fin) per
qualche finizione totale computabile f. Secondariamente, tutti i risultati presentati
in questo testo, concernenti la complessità computazionale delle MT non
deterministiche, non perdono la loro validità qualunque sia la definizione adottata,
come il lettore può verificare personalmente. Infine, si intuisce facilmente che, in
molti casi interessanti, le due definizioni sono praticamente equivalenti, come si
illustrerà successivamente, in questa stessa sezione, nel corollario del Teorema
della Accelerazione Lineare (Corollario 9.14).
Dopo aver associato una finizione di complessità ad una macchina, prendiamo
ora in considerazione la classe dei problemi risolvibili all’interno di un determinato
limite di complessità, ossia mediante una macchina la cui complessità sia limitata
superiormente da una determinata funzione. In termini di riconoscimento dei
linguaggi, ciò conduce alla seguente definizione.
398 Informatica teorica

Definizione 9.12
Sia f una funzione totale definita sui numeri naturali. DTIME(/) è la famiglia di
linguaggi riconosciuti da qualche MT deterministica multinastro con complessità
temporale limitata da f ossia tale che Tfin) < fin) per ogni n. Analogamente,
DSPACE(/) è la famiglia dei linguaggi accettati da una MT deterministica
multinastro con complessità spaziale limitata da f. NTIME(/) e NSPACE(/) sono
definite analogamente per i linguaggi accettati dalle MT non deterministiche.

Ad esempio, L = {wcwA} è in DTIME(m) e in DSPACE(log2 (fi)), come mostrano
gli Esercizi 9.4 e 9.6. Si noti però che ciò non implica che L possa essere
riconosciuto, dalla stessa MT, in tempo n e spazio log(n). Molto spesso, come già
osservato in precedenza, bisogna rinunciare all’efficienza temporale per quella
spaziale e viceversa.

ESERCIZI

9.14 Si dimostri che sefi(fi) <ffifi) per ogni n, allora DTIME(fj) c DTIME(^);
si estenda poi la dimostrazione anche a NTIME, DSPACE e NSPACE.
9.15 Si determinino le più piccole classi di complessità, deterministica e non
deterministica, temporale e spaziale, che contengano i seguenti linguaggi
Zi = {ww| w e {a,b}*}
L2 = {anxban2 ...ank | 3z,j, 1 < i *j < A tale che «,= nfi

Nelle Sezioni 9.1 e 9.2 si è osservato che la complessità della soluzione di un


problema può essere migliorata mediante un’opportuna modifica deH’algoritmo di
risoluzione. Sorge quindi spontaneo chiedersi di quanto si può migliorare la
complessità. I prossimi teoremi, mostrano che si possono ottenere, in modo
sistematico, miglioramenti lineari sia per la complessità spaziale che per quella
temporale.

fb)
Figura 9.5 (a) Un nastro di M. (b) Il corrispondente nastro di M.
399
La complessità del calcolo

Teorema 9.10
Se L è accettato da una MT multinastro (deterministica o non deterministica) con
complessità spaziale S allora, per ogni costante reale c > 0, L è accettato anche da
un’opportuna MT avente complessità spaziale c-S (c-S indica la funzione S' tale
che S'(n) = c-S(n) per ogni n).

Traccia della dimostratone

Si consideri un intero r tale che r-c>2. Possiamo a questo punto “fondere” insieme
r celle adiacenti di un qualunque nastro della MT M, ottenendo un’unica cella, e
avere ancora una MT M' equivalente a quella originaria, mediante un’opportuna
codifica e una modifica di T, Q, 8. Chiaramente, la nuova MT ha la complessità
spaziale desiderata. La Figura 9.5 fornisce uno schema intuitivo della costruzione.
Allo scopo di codificare la posizione della testina di M, all’interno della r-upla
(ai,...,ar), lo stato di M' corrisponde ad una coppia (q, i), con 1 <i< r, nel caso di
una MT a nastro singolo, mentre corrisponde ad una (k + l)-upla, nel caso di una
MT a k nastri.

Figura 9.6 (a) Una MT a k nastri, M. (b) Una MT equivalente ad un nastro, M',
con la medesima complessità spaziale.
400 Informatica teorica

Teorema 9.11
Se L è accettato da una MT a k nastri con complessità spaziale S, allora è accettato
anche da un’opportuna MT ad un nastro M' con la medesima complessità S.

La dimostrazione è lasciata al lettore per esercizio. Come suggerimento, la Figura
9.6 indica la possibilità di impiegare alfabeti mutuamente disgiunti per ciascun
nastro di memoria. Le porzioni “significative” di ciascun nastro di memoria di M
(ossia le porzioni che contengono solo celle non vuote, nonché la posizione della
testina) vengono memorizzate consecutivamente nell’unico nastro di memoria M’.
Le posizioni delle k testine di M vengono memorizzate mediante opportuni simboli
speciali.

ESERCIZIO
9.16 Si dimostri che, se un linguaggio L è accettato da una MT con complessità
spaziale tale che S(n) > n per ogni n, allora è accettato da una MT a nastro
singolo con complessità spaziale S(n).

In generale, per la complessità temporale, non si hanno risultati simili a questi. Ad


esempio, il risultato dell’Esercizio 9.16 non è applicabile alla complessità
temporale, poiché L = {wcwÀ} può essere riconosciuto, con una complessità
temporale ®(n), da una MT a un nastro, mentre non esiste alcuna MT a nastro
singolo in grado di risolvere il problema con la medesima complessità (si veda il
Teorema 9.7).
Come vedremo, è tuttavia possibile ricavare un teorema, analogo al Teorema
9.10, valido per la complessità temporale. Occorre però farlo precedere da una
nuova definizione.

Definizione 9.13
Sia f una qualunque finizione definita sui numeri naturali. Si definisce sup f (»)
«—> co .

(rispettivamente inf f («) ) come il limite dell’estremo superiore


«—> co
(rispettivamente dell’estremo inferiore) della successione {/(«), fin + 1),...} quando
n —> oo .
' ■
Ovviamente, se esiste lim f (w), allora lim f(ri) = sup f (w) = inf f (n) .
CO «—>00 W^OO « —> CO

Per esempio,
sup n ■ (sin (w) + 1) = oo , inf n ■ (sin (ri) + 1) = 0 ,
La complessità del calcolo 401

sup (sin (w) + 1) = 2 , inf (sin (n) + 1) = 0


„ >v.
n + log n n + log n
sup -----------------= inf ------------------ = 1 .
« «-><» n

Teorema 9.12 (Teorema dell’Accelerazione Lineare)


Sia L un linguaggio accettato da una MT a k nastri M con complessità temporale
Tm, tale che:

taf
n—><x n
Allora, per ogni costante reale c > 0, si può costruire una MT a (k + 1) nastri M'
che accetta L con complessità temporale TM’, tale che TM (rì) = max {n + 1,

Figura 9.7 Costruzione relativa alla accelerazione di una MT.

Dimostrazione
La dimostrazione si ispira all’analoga dimostrazione relativa alla riduzione lineare
della complessità spaziale (Teorema 9.10), in quanto consiste nella fusione di r
celle adiacenti di Min un’unica cella di M’, per un opportuno valore di r (si veda la
Figura 9.7). E tuttavia necessario prestare una maggiore attenzione ai dettagli per
ottenere l’espressione della complessità temporale di M'.
Durante la fase preliminare, M' copia il contenuto del nastro di ingresso Tj sul
suo nastro supplementare di memoria T7+;, fondendo insieme r celle adiacenti di 17
in un’unica cella di T7+7. Successivamente, la testina di TK7 viene posizionata di
nuovo sulla prima cella del nastro.
Questa fase preliminare richiede un tempo n + \ n/r J, dove n indica, come al
solito, la lunghezza della stringa di ingresso. Da questo momento in poi, M' simula
Musando TK7 come nastro di ingresso. La computazione di M’ consiste in una
sequenza di macro-mosse. Ciascuna macro-mossa, a sua volta, è costituita da due
gruppi formati rispettivamente da, al più, quattro e due mosse elementari.
402 Informatica teorica

Il primo gruppo è organizzato nel modo seguente. Per ciascun nastro T;, 1 <i < k +
1, M' visita le due celle alla destra e alla sinistra della cella precedentemente letta e
si riposiziona su di essa. Ad esempio, la M' mostrata in Figura 9.6 visita le celle
(ar...ar) effettuando un passo a sinistra, (ci...cr), con due passi a destra e ritornando
su {bi...br) con un passo a sinistra.
A questo punto, M’ “conosce” il contenuto delle 3-r celle di M, {aA...aX
(bi...br\ {ci...cr). Poiché tale contenuto può consistere in |r|3 r valori diversi, questi
ultimi potranno essere memorizzati da M’ mediante l’insieme dei suoi stati. Si noti
che, quando M opera sulla porzione di T; corrispondente alle 3 -r celle di cui sopra,
essa può:
1. Fermarsi, accettando o rifiutando.
2. Non lasciare mai la porzione di nastro sopra descritta, per tutti i nastri T;. In
questo caso, essa dovrà entrare, alla fine, in un ciclo chiuso di configurazioni
ripetute un numero infinito di volte, a causa del numero finito delle possibili
configurazioni diverse.
3. Effettuare una sequenza di almeno r + 1 mosse, tali che, per qualche Tz, la
corrispondente testina di lettura lasci, alla fine, la porzione di 3 ■ r celle sopra
citate.
I casi 1. e 2. possono essere decisi da M' senza alcuna mossa, semplicemente
ispezionando il suo stato. Infatti essi corrispondono ad un numero finito di possibili
alternative.
Nel caso 1., M' si ferma, cosi come farebbe M. Nel caso 2., M' si ferma
rifiutando la stringa, o, equivalentemente, entra in un ciclo, come farebbe M. Nel
caso 3., M' simula r mosse di M per ciascun T„ mediante il secondo gruppo
composto, al più, di due mosse. Questa situazione si articola nel modo seguente.
M' modifica il contenuto della cella corrente e (eventualmente) della cella
adiacente, come farebbe M nella corrispondente porzione di nastro, e lascia la
testina posizionata sulla cella corrispondente al gruppo di celle in cui la testina di
M si ritroverebbe dopo r mosse. Ad esempio, si supponga che r mosse di M
consistano nel visitare, da destra verso sinistra, b)...bs...blar...am cambiando i loro
valori in bf ...bs'...am' e ritornando a bs’. Si noti che, se un elemento di ax...ar viene
visitato da M, nessun elemento di c\...cr può essere visitato in una sequenza di r
mosse. Ciò può essere simulato da M' mediante due mosse, che sostituiscano
con {ai...am’ ...ar'} e (b1...br') con {b\ ...bs'..br), e lasciando la testina
posizionata su (bi'...br'), in uno stato che memorizza la posizione s per il nastro T>.
Come si può notare, quindi, r mosse di M sono state simulate da, al più, sei
mosse di M'. Ciò implica che, se M impiega T^ri) mosse per effettuare i suoi
calcoli, M' non ne impiega più di

r r r r
La complessità del calcolo 403

yr (fi )
Ora, si ricordi che inf ----- = 00 , il che significa che per ogni costante reale 8
n —> co n
esiste un «5 in corrispondenza del quale, per ogni n > n&, T(rì)ln > 8. Risulta quindi
molto facile verificare che, per ogni n > n& e n > 2, dove n + 2 < 2n,
n 6T(n) <6 2 1 >
fi H--------1----------------- F 2 < T’(ft) • 1— +
r r <r 8 r•8)
Chiaramente, per ogni c > 0 assegnato, si possono trovare un intero r ed una 8 tali
che:
6 2 1
— H------ H------------ < C
r 8 r■8
Resta da considerare l’insieme finito di parole la cui lunghezza è minore di max{2,
ns}. Questo non è però un problema, poiché un insieme finito di parole può sempre
essere riconosciuto da un AF, e quindi da una MT, in n + 1 mosse (si ricordi che
una MT ha bisogno, al più, di una mossa supplementare per entrare nello stato di
arresto dopo aver letto la fine della stringa di ingresso).

Basandosi sul Teorema 9.12, è banale dimostrare il seguente corollario, che viene
quindi lasciato al lettore per esercizio.

Corollario 9.13

1. Se inf ---------- = 00
n —> co n

c > 0, allora DTIME(/) = DTIME(c-/), NTIME(/) = NTIME(c-/).


2. Se/(«) = c-n per qualche Ole g(n) = (1 + e)-m per ogni e, con s > 0 allora
DT1ME1/) = DTIME(g), NTIMEtf) = NTIME(g).

Sia l’enunciato che la dimostrazione del Teorema 9.12 hanno un notevole impatto
concettuale e pratico. Dapprima si osservi che la proprietà dell’accelerazione
lineare afferma ulteriormente l’importanza della notazione theta-grande. Infatti,
grazie al Teorema 9.12, ogni volta che due linguaggi vengono riconosciuti da
macchine dotate di funzioni di complessità che soddisfano le ipotesi del teorema ed
appartengono alla medesima classe theta-grande, allora possono essere riconosciuti
da macchine limitate dalla stessa funzione.
Si noti poi che la costruzione impiegata nella dimostrazione garantisce la
possibilità di guadagnare in velocità di esecuzione, a patto di pagare con risorse di
calcolo (ossia effettuando molte computazioni in parallelo), come del resto si
verifica nella realtà, quando si usano calcolatori più potenti per accelerare la
risoluzione di problemi pratici. Il meccanismo fondamentale alla radice della
dimostrazione del Teorema 9.12 infatti, trascende l’applicazione al modello
specifico della MT mulinastro ed è facilmente estendibile ad altri modelli di
404 Informatica teorica

calcolo più vicini alla classica architettura di von Neumann; il lettore è invitato a
verificare di persona questa affermazione in seguito alla lettura della Sezione 9.3.
Secondo il Teorema 9.12, tuttavia, il guadagno ottenibile mediante questo
approccio sistematico (aumentare le risorse di calcolo disponibili) è vincolato a
restare lineare. Questo risultato porta con sé un’interessante conseguenza. Si
supponga di eseguire un algoritmo con complessità temporale ©(/?) per risolvere
un problema P su di un piccolo personal computer. La velocità di esecuzione che si
può raggiungere passando ad un supercalcolatore che esegue il medesimo
algoritmo (per casi di P sufficientemente “grandi”) è molto minore del guadagno
ricavabile passando ad un algoritmo 0(nL9) eseguito dallo stesso personal
computer. In altre parole, l’intuito nel trovare buoni algoritmi dà migliori risultati
rispetto al calcolo fondato sulla sola potenza del calcolatore.
Un’altra conseguenza del Teorema 9.12 comporta la possibilità di colmare il
divario fra le definizioni di complessità 9.11 e 9.1 la, per le computazioni non
deterministiche.

Corollario 9.14
Sia funa funzione totale che:

a. • r -------- = oo
ini
«—> oc n
b. Per ogni n, il valore di fin) è computabile da una MT deterministica in un
tempo t, con t <fin).
Se una MT non deterministica M ha una complessità temporale fi secondo la
Definizione 9.11, si può costruire una MT equivalente M' con complessità
temporale/^ secondo la Definizione 9.1 la. Quindi, per ogni f che soddisfa l’ipotesi,
NTIME(/) non dipende dalla scelta di una delle due definizioni.

Dimostrazione
Si può costruire facilmente M' seguendo la traccia seguente.
Per ogni ingresso x assegnato, di lunghezza n, M' dapprima calcola fin) nel tempo
t, con t <firì). Non c’è perdita di generalità nel supporre che/(«) sia codificata in
base unaria su qualche nastro di M', poiché la conversione di un numero qualunque
m dalla base unaria ad una base k con k > 1, e viceversa, può essere effettuata in un
tempo 0(m).
405
La complessità del calcolo

M' simula M decrementando fin) di un’unità per ciascun passo; se non accetta x
prima di f(n) mosse, si ferma, rifiutando x. Chiaramente, esisterà qualche
computazione di M' che accetta x se, e solo se, M si comporta nello stesso modo.
Inoltre, il limite di tempo per tutte le computazioni di M' su stringhe di lunghezza n
è dato da 2 ■/(»). In virtù del Teorema 9.12, tali computazioni si possono accelerare
in modo tale da venire effettuate in un tempo limitato esattamente da /fw).

ESERCIZI

9.17 Si mostri che, se un linguaggio è riconosciuto da una MT multinastro M


con complessità temporale TM, allora può essere riconosciuto da una MT a
nastro singolo con una complessità temporale ®(TM )3.
9.18 Si mostri che il Corollario 9.14 vale anche sef(n) = c-n, con c > 1, oppure
se/(n) = c + n, con c > 1.
9.19 Si dimostri che il Corollario 9.14 vale anche per la complessità spaziale.
9.20 Si dimostri che le funzioni nk, per k fissato, n*log/!(n) per k, h fissati e nsl~n ,
soddisfano le ipotesi del Corollario 9.14

Per riassumere e analizzare ulteriormente le importanti implicazioni che derivano


dai risultati presentati in questa sezione si considerino i seguenti esempi.

Esempio 9.9
Vogliamo fornire una maggiorazione, a meno della relazione 0, della relazione tra
le complessità temporali di una MT a k nastri e una a k + 1 nastri, quando si usa la
prima per simulare la seconda.
Si osservi che una MT a nastro singolo può simulare una MT a k nastri con
complessità ©(^(n)) < O(Tk(h)2). Informalmente, l’idea è di collassare i k nastri
della seconda sull’unico nastro della prima. In questo modo ogni mossa della MT a
£ nastri costa al più una scansione completa dell’unico nastro della prima.
Lo stesso ragionamento si può effettuare nel nostro caso, collassando il (£+1)-
esimo nastro della MT a k + 1 nastri sul £-esimo della MT a k nastri, ottenendo
dunque 0(Tk(n)) < 0(Tk+1(»)2).

Esempio 9.10
Consideriamo il problema di decidere la veridicità delle seguenti affermazioni:

3 Tm è un’ovvia abbreviazione di “la funzione T definita da l'(n) = (’T'tXn))2”.


406 Informatica teorica

a. Un linguaggio non contestuale deterministico può essere riconosciuto da una


MT a A: nastri con complessità temporale 0(n).
b. Un linguaggio non contestuale deterministico può essere riconosciuto da una
MT a nastro singolo con complessità temporale 0(«).
c. Un linguaggio regolare può essere riconosciuto da una MT a nastro singolo
con complessità temporale 0(n).
d. Nessun linguaggio non contestuale può essere riconosciuto da una MT a k
nastri con complessità spaziale inferiore a 0(log n).
Tali affermazioni permettono di ragionare sulle relazioni di complessità tra i
diversi modelli di calcolo basati su automi.
La prima affermazione (a.) è sicuramente vera. Infatti, un qualsiasi linguaggio
non contestuale deterministico può essere riconosciuto da un AP in tempo lineare.
Una MT a 1 nastro (e più in generale a k nastri) può simulare un AP facilmente,
gestendo il suo unico nastro con una politica LIFO. Questa simulazione non
richiede nessun incremento (a meno di 0) della complessità temporale. Infatti
gestendo il nastro come una pila, la posizione in cui la macchina deve leggere e
quella in cui deve scrivere sono al più separate da un numero costante di celle.
La seconda affermazione (b.) è invece falsa, come dimostrato dal Teorema 9.7.
La terza affermazione (c.) è vera; infatti, una MT a nastro singolo può simulare un
AF senza alterarne la complessità temporale, in quanto l'unico nastro della
macchina verrà utilizzato unicamente come nastro di ingresso.
Infine, l'ultima affermazione (d.) è falsa. Infatti, la classe dei linguaggi non
contestuali contiene anche la classe dei linguaggi regolari e questi ultimi sono
sempre riconoscibili da una MT a k nastri con complessità spaziale 0(A), in quanto
di fatto la MT quando simula una AF non utilizza i nastri di memoria

9.3 Analisi della complessità di programmi per calcolatori reali


Gli esempi considerati finora hanno un carattere prettamente matematico:
appartengono infatti al campo del riconoscimento dei linguaggi formali ed i
modelli di computazione impiegati sono contenuti nella classe degli automi astratti.
Sorge quindi spontaneo chiedersi che utilità possono avere nella pratica i risultati
ottenuti e se questi possono essere generalizzati e in che misura.
Si supponga di essere riusciti a dimostrare che un certo problema può essere risolto
da una MT con complessità temporale 0(/); lo stesso risultato vale per un
programma Java che risolve lo stesso problema? Questo punto è già stato
commentato, quando si è mostrato come il problema di appartenenza, per un
insieme ordinato, può essere risolto in tempo 0(«)4 da una MT e in un tempo

4 La notazione precedente sostituisce, in gergo, la dicitura “limitato nel tempo da una


funzione 0(«)”. Nel seguito la useremo per semplicità, ogni volta che ciò non provocherà
equivoci.
La complessità del calcolo w

0(log n) da un programma Java, che implementa l’algoritmo della ricerca binaria.


Se consideriamo ora il problema di sommare due numeri naturali, la sua soluzione
mediante una MT multinastro richiede un numero di transizioni dipendente dalla
dimensione degli addendi, come stabilito con precisione nel seguente enunciato, la
cui (banale) dimostrazione viene lasciata per esercizio al lettore (si veda anche
l’Esempio 4.21).

Enunciato 9.15
Una MT a due nastri può effettuare la somma di due numeri naturali e n2 per
ogni base k, con k > 1, con una funzione di complessità temporale che è 0(m), dove
n = max{| logt + 1, (log^ m2J +1}- Nessuna MT Mmultinastro può effettuare
questa operazione con &(TM) < 0(m).

D’altra parte, è ben noto che ogni calcolatore digitale è dotato di un’istruzione
ADD che esegue in una unità di tempo (un ciclo macchina) la somma di due
numeri interi, i cui valori possono essere immagazzinati o nei registri o nella
memoria principale, e lascia il risultato o in un registro o in una cella della
memoria principale.
Ciò motiva la necessità di studiare modelli computazionali più pratici, in
modo che i risultati relativi alla complessità, dimostrati su quei modelli, abbiano
un’applicabilità pratica e immediata. In questa sezione si studiano due di tali
modelli: la macchina RAM (una astrazione di un calcolatore convenzionale) e
SMALG (un linguaggio di programmazione ad alto livello ispirato all’ALGOL),
già parzialmente introdotto nell’Appendice 7.A. In seguito, nella Sezione 9.5, si
confronteranno in termini rigorosi le analisi di complessità relative ai diversi
modelli computazionali (ricavandone risultati non del tutto scontati).

9.3.1 La macchina RAM


La macchina RAM (Random Access Machine) è un modello classico, fortemente
ispirato alla tradizionale architettura di vonNeumann; esso è utilizzato in vari testi
di teoria della compoutazione (si faccia riferimento alle note bibliografiche per
approfondimenti). La RAM è costituita da un nastro di ingresso, un nastro di
uscita, un programma rappresentato da una sequenza finita di istruzioni, un
contatore che indica l’istruzione corrente da eseguire e una memoria ad accesso
diretto (termine preferibile alla traduzione letterale dall’inglese). La struttura
generale è schematizzata in Figura 9.8.
Sia i nastri che la memoria sono composti da un numero illimitato di celle,
ciascuna delle quali può contenere un qualunque valore intero. Mentre ai nastri di
ingresso e di uscita si può accedere solo sequenzialmente, la memoria è indirizzata
e si può accedere direttamente a una cella attraverso il suo indirizzo. In dettaglio,
ciascun nastro è dotato di una testina, posizionata inizialmente sulla prima cella del
nastro e ogni operazione di lettura/scrittura fa avanzare, implicitamente, la testina
408 Informatica teorica

di una cella verso destra. Le celle di memoria, invece, sono indirizzate da numeri
naturali: M[i\ (con i > 0) indica la z-esima cella di memoria e A/[0] si riferisce ad
un registro speciale, l’accumulatore, che si usa per contenere il valore di uno dei
due operandi delle operazioni aritmetiche binarie che la macchina può effettuare.
Il programma, che definisce ciascuna RAM, è cablato nella macchina ed è quindi
fisso. Un programma è una sequenza di istruzioni, ciascuna formata dal codice
dell’operazione da effettuare e dall’operando (che in alcuni casi può essere
un’etichetta). Il repertorio delle istruzioni è un insieme molto semplificato di ciò
che è possibile trovare nei calcolatori reali: si possono eseguire operazioni di
lettura e scrittura sui nastri, assegnamenti, operazioni aritmetiche e istruzioni di
salto condizionato e non. Le etichette sono associate solo alle istruzioni di salto,
mentre tutte le altre istruzioni sono associate ad un operando.
Le istruzioni della RAM e la loro semantica sono descritte brevemente nella
Tabella 9.1, in cui si adotta una intuitiva notazione ispirata a linguaggio lava
nell’ipotesi che M si possa considerare come un vettore illimitato di interi. Il
contatore istruzioni si può vedere come una variabile intera CI, il cui valore è
contenuto nel sottoinsieme dove m è la dimensione, ossia il numero di
istruzioni, del programma.
Tutte le istruzioni dalla 1 alla 8 nella Tabella 9.1, oltre all’effettiva operazione
indicata dal codice, incrementano implicitamente il valore di CI di una unità. La
macchina si ferma in uno stato corretto quando viene raggiunta l’istruzione di
HALT. Essa si ferma invece in uno stato scorretto quando i valori sono al di fuori
dell’insieme di definizione o vengono incontrate istruzioni sintatticamente senza
senso (ad esempio READ= x) durante l’esecuzione.

Figura 9.8 La macchina RAM.


La complessità del calcolo

Le istruzioni vengono identificate simbolicamente, mediante un’etichetta. Due


istruzioni non possono avere la medesima etichetta e le etichette sono limitate
unicamente ai valori del CI. La descrizione della semantica dei salti (istruzioni
dalla 9 alla 11 nella Tabella 9.1) utilizza una funzione b che correla semplicemente

Tabella 9.1 Insieme delle istruzioni della macchina RAM


Codice operativo Formati delle istruzioni Semantica delle istruzioni
1 LOAD LOAD= X Àf[O] =x
LOAD X A/[0] = Mx]
LOAD* X M0] = W]]

2 STORE STORE X Mx] = M°]


STORE* X MMx]]=M0]

3 ADD ADD= X Àf[O] = Af[d] + X


ADD X A/[0] = M[0] + Mx]
ADD* X Af[O] = Àf[d] +

4 SUB SUB= X Àf[O] =<0]-x


SUB X M0] = M0]-Mx]
SUB* X À/[0] = - M[M[x]]

5 MULT MULT= X M[0]=M[0]*x


MULT X Af[O] = Àf[d] * Mx]
MULT* X M0] = Md] * MMx]]

6 DTV DIV= X M0]=M0]/xj


DTV X M0] = Md] / Mx]
DIV* X M0] = M0]/MMx]]
7 READ READ X Mx] = valore corrente in ingresso
READ* X MMx]] = valore corrente in
ingresso
8 WRITE WRITE= X stampa in uscita x
WRITE x stampa in uscita Mx]
WRITE* x stampa in uscita MMx]]

9 JUMP JUMP lab CI = b(lab)

10 JGZ JGZ lab CI = if M[0]>0 then b(lab)


else CI + 1
11 JZ JZ lab CI = if M0]=0 then bllab)
else CI + 1
12 HALT HALT la computazione termina

5 In Java la divisione intera / tronca il risultato della divisione di due interi.


410 Informatica teorica

le etichette ai numeri interi del sottoinsieme Più precisamente, b(lab) = y


significa che l’istruzione etichettata da lab è la j-esima istruzione del programma.
I simboli ‘=’ e giustapposti al codice delle istruzioni denotano le modalità
di indirizzamento immediato e indiretto, rispettivamente.
I programmi per la RAM si possono interpretare come funzioni. Un programma P
che legge in ingresso n valori interi e produce in uscita m valori interi prima di
fermarsi, si può considerare come una funzione p:F Poiché non è
garantito che i programmi RAM si fermino, le funzioni corrispondenti sono
funzioni parziali, in quanto non sono definite per quei valori di ingresso per cui il
programma non termina. Da un punto di vista formale, si può dimostrare molto
facilmente che le RAM, come ogni altro ragionevole modello di calcolatore, sono
in grado di calcolare esattamente tutte le funzioni MT-computabili.
I programmi RAM si possono anche interpretare come riconoscitori di
linguaggi, adottando un’opportuna codifica dei caratteri. Infatti, poiché si è
ipotizzato che la RAM possa manipolare unicamente numeri interi, una sua
interpretazione in qualità di riconoscitore di linguaggi richiede una codifica dei
caratteri alfabetici mediante i numeri interi. Se Vt è l’alfabeto di un linguaggio L e
k = | VT\, ciascun simbolo a g Vt può essere rappresentato da un intero ia tale che
1 < ia < k. Ciascuna stringa x g Vt può essere rappresentata da un’opportuna
sequenza di interi seguiti da uno 0, che funge da terminatore. Le stringhe
appartenenti a L vengono accettate da un programma RAM se, leggendo l’intera
stringa di ingresso (fino allo 0 più a destra), la macchina stampa un messaggio di
accettazione (ad esempio un 1) e si ferma. In corrispondenza delle stringhe non
contenute in L, la macchina si può fermare e stampare un valore qualunque diverso
da 1, oppure può non fermarsi del tutto. Da un punto di vista formale, si può
dimostrare molto facilmente che un linguaggio viene accettato da una RAM se, e
solo se, è ricorsivamente enumerabile. Un linguaggio viene accettato da una RAM
che si ferma su tutte le stringhe di ingresso se e solo se è ricorsivo.
Per capire meglio il funzionamento della RAM, presentiamo ora due esempi
che illustrano, rispettivamente, un programma per il calcolo di una funzione e un
programma usato come riconoscitore di un linguaggio.

Esempio 9.11
Si consideri la funzione è_primo definita sugli interi positivi nel modo seguente:
è_primo(n) = if n è un numero primo then 1 else 0
Un programma di Figura 9.9 implementa la funzione è_primo enumerando tutti i
valori da 2 a n fino a che il valore non uguaglia n o divide esattamente n. Nel
primo caso, il programma stampa in uscita un 1; nel secondo caso il valore di
uscita è uno 0.
411
La complessità del calcolo

Esempio 9.12
Il programma RAM di Figura 9.10 riconosce il linguaggio, ormai familiare, L =
{wcwÀ| wg {a,b} } utilizzando la seguente codifica per rappresentare i simboli
dell’alfabeto (a, b e c) attraverso l’uso di numeri naturali.
- a è codificata da 1.
- b è codificato da 2.
- c è codificato da 3.
- 0 è usato come marca di fine nel nastro d’ingresso.
Programma RAM Commento
READ 1 II valore letto in ingresso (ri) è memorizzato in Affi]-
LOAD= 1 II valore 1 viene caricato nell’accumulatore.
SUB 1 II valore in à/[0] (cioè 1) viene decrementato di n (il
contenuto di Af[O].
JZ YES Se n = 1 (cioè se 1-n è pari a 0), allora n è banalmente
primo e il resto del programma viene saltato.
LOAD= 2 II valore 2 viene caricato nell’accumulatore e
STORE 2 poi salvato in At[2] (cioè à/[2] è inizializzato a 2).

CICLO: LOAD 1 II valore di AZ[ 1 ] viene caricato nell’accumulatore e


SUB 2 gli viene sottratto il valore contenuto in A/[2] (cioè il
valore usato come divisore).
JZ YES Se il valore del divisore è pari a n, allora n è primo e
il programma termina.
LOAD 1 A/[ 11 viene nuovamente caricato nell’accumulatore,
DIV 2 diviso per Af[2] e
MULT 2 nuovamente moltiplicato per A/[21.
SUB 1 Al valore ottenuto come (Af[l] / Af[2])* U[2| viene
sottratto A/[l].
JZ NO Se Affi] è divisibile per Af[2], allora ((AZ[1] / Af[2])*
At[2]) - A/[l] è pari a 0; ciò implica che n non è un
numero primo.
LOAD 2 A/[21 viene caricato nell’accumulatore
ADD= 1 viene incrementato di 1 e
STORE 2 nuovamente salvato in M[2],
JUMP CICLO La sequenza di istruzioni che comincia con CICLO
viene ripetuta.
YES: WRITE= 1 Viene scritto in uscita il valore 1.
HALT

NO: WRITE= 0 Viene scritto in uscita il valore 0.


HALT

Figura 9.9 Un programma RAM che calcola la funzione è_primo.


412 Informatica teorica

Per ogni sequenza che rappresenta una stringa di L, il programma stampa un 1 e,


successivamente, la macchina si ferma. Per ogni sequenza che rappresenta una
stringa da rifiutare, il programma stampa uno 0 e la macchina si ferma.

Programma RAM Commento


LOAD= 4 M[2], il puntatore alla pila, è inizializzato al
STORE 2 valore 4
LOAD= 0 à/[4] è inizializzato a 0, che rappresenta il
STORE 4 simbolo iniziale della pila
LOAD= 0 Àf[3], lo stato dell’AP, è inizializzato a 0, che
STORE 3 rappresenta lo stato q0.

CICLO: READ 1 Se il simbolo corrente è 0, la stringa è


LOAD 1 terminata e deve essere o accettata o rifiutata.
JZ FINETEST
LOAD 3 Se lo stato corrente è 1, salta all’istruzione
JGZ DISIMP etichettata DISIMP.
LOAD 1 Se il simbolo corrente non è c, viene messo
SUB= 2 nella pila. Altrimenti cambia stato.
JGZ CAMBIA
LOAD 2 Il puntatore alla pila Af[2] è incrementato di 1
ADD= 1 e il simbolo corrente in ingresso è
STORE 2 memorizzato in M[M[2J] (ossia è messo nella
LOAD 1 pila).
STORE* 2 La sequenza di istruzioni che comincia con
JUMP CICLO CICLO viene allora ripetuta.

CAMBIA: LOAD= 1 Il nuovo stato è q^.


STORE 3

DISIMP: LOAD 1 Verifica che il simbolo corrente di ingresso sia


SUB* 2 uguale al simbolo in cima alla pila. In tal caso
JZ CONTIN prosegue da CONTIN; altrimenti rifiuta la
JUMP NO stringa.

CONTIN: LOAD 2 Toglie un valore dalla pila e controlla che non


SUB= 1 sia vuoto. Se è vuoto allora ricomincia
STORE 2 FINETEST; altrimenti ritorna a CICLO.
SUB= 4
JZ FINETEST
JUMP CICLO

Figura 9.10 (Prima parte)


413
La complessità del calcolo

FINETEST: LOAD 1 Per accettare la stringa in ingresso controlla:


JGZ NO a. l’ingresso sia stato letto completamente;
LOAD 3 b. lo stato sia q^,
JZ NO
LOAD 2 c. la pila sia vuota.
SUB= 4
JZ SI

NO: WRITE= 0
HALT

SI: WRITE= 1
HALT
(Seconda parte)

Figura 9.10 Un programma RAM che decide L = {wcw*}.

Il programma RAM simula un AP che riconosce L, come descritto nell’Esercizio


4.21 e nell’Esempio 9.7. Le locazioni di memoria A/[4], à/[5], ... rappresentano il
contenuto della pila dell’AP. à/[4] è inizializzato con il simbolo iniziale della pila
(codificato da uno 0). Gli altri valori della pila sono 1 e 2, che vengono posti sulla
pila in corrispondenza della lettura di una a o di una b, rispettivamente, durante
l’analisi di w. à/[1] memorizza il simbolo di ingresso correntemente letto. à/[2]
memorizza l’indirizzo del simbolo in cima alla pila. à/[3] memorizza il codice
dello stato dell’AP. Gli stati sono così codificati: 0 rappresenta lo stato q0 (dove
l’AP rimane fino a che viene letto c), 1 rappresenta lo stato (dove l’AP rimane
durante l’analisi di w/?).

ESERCIZI

9.21 Si scrivano programmi RAM che calcolano le funzioni definite nel modo
seguente:
1. primo(n) = k, dove n > 0 e k è l’z-esimo numero primo, cominciando
da 2.
2 tutti-primi(n) = {x| x è un numero primo e x < n}
3. fln\, n2, ■■■, n^) = 2”13”2 ... p™k , dove pi è l’z-esimo numero primo. Si
supponga che la stringa di ingresso contenga per primo il numero k,
seguito da nr, n2, ..., nk.
4. fl, dove fè la funzione del punto 3.
5. ordinato(n\,...,n^) = if < n2 < ...< nk then 1 else 0. La stringa in
ingresso è strutturata come nel punto 3 precedente.
9.22 Si scrivano programmi RAM che riconoscono i seguenti linguaggi
1. Li = {wwA| we {a,b}*}
2. L2 = {mv| M-e {a,b} }
414 Informatica teorica

3. £3 = {anbn\ n > 0}
In particolare, si scrivano due programmi RAM, Pi e P2, che
riconoscono Z,3. Pi si ottiene come simulazione diretta dell’AP che
riconosce £3. P2 conta le occorrenze di a, invece di memorizzarle nella
pila.

Possiamo ora usare il modello RAM per stimare la complessità delle computazioni
effettuate dai calcolatori reali. Infatti, il modello computazionale RAM si comporta
in modo molto simile ai calcolatori convenzionali, anche se vi sono alcune
differenze sostanziali. La differenza più evidente è che i calcolatori convenzionali
sono programmabili: i programmi sono immagazzinati in memoria e possono
modificare se stessi.
Un modello classico, che fa uso dello stesso insieme di istruzioni della RAM e
non presenta questa differenza con i colcolatori reali, è la macchina RASP
(Random Access Stored Program), che mantiene il programma in una parte della
memoria e permette di modificarlo. Tale macchina usa lo stesso insieme di
istruzioni della RAM, con l’unica eccezione che tutte le istruzioni del tipo “codice
operativo*”, cioè le istruzioni che utilizzano l’indirizzamento indiretto, non sono
consentite.
Si osservi che la macchina RASP rappresenta per la macchina RAM quello
che la MT universale rappresenta per la MT. Infatti, le prime sono macchine che
possono essere programmate e quindi prendono in ingresso sia il programma che i
dati su cui utilizzare il programma, le seconde sono macchine già programmate,
atte a risolvere uno specifico problema in modo prestabilito e ricevono in ingresso
solo i dati su cui operare.
Tornando al confronto tra la RAM e i calcolatori reali, altre differenze
risiedono nel repertorio delle istruzioni, poiché la nostra RAM è una versione
semplificata di quanto si può trovare nel mondo dei calcolatori reali. Ad esempio,
molti calcolatori prevedono una istruzione INC per incrementare un valore di
un’unità. Una istruzione “INC z” può essere simulata da una sequenza di istruzioni
RAM.
LOAD i
ADD= 1
STORE i
D’altra parte, la macchina RAM ha, come istruzioni primitive, la MULT e la DIV,
che possono non essere disponibili sui calcolatori più semplici, in quanto possono
essere simulate utilizzando la somma e la sottrazione, o, più probabilmente
ricorrendo ad un’apposito coprocessore matematico. In ogni caso, per eseguire
operazioni di tipo moltiplicativo su calcolatori reali, bisogna impiegare
un’opportuna sequenza di istruzioni, contenenti anche dei cicli, che difficilmente
può essere completata nello stesso tempo richiesto da un’istruzione elementare.
415
La complessità del calcolo

Inoltre, le macchine che dispongono dell’operazione di divisione intera, molto


spesso forniscono anche il resto, come effetto collaterale, in qualche registro
dell’unità aritmetica.
In tal caso, il seguente test nel programma RAM di Figura 9.9
M1] = (M1]/M2])*M2]
ossia
MI] % M2] = 0
verrebbe implementato da una sola istruzione macchina.
E tuttavia facile verificare che queste differenze, non alterano l’ordine di
grandezza della complessità computazionale (ossia la ©-classe di equivalenza) di
un algoritmo codificato mediante diversi repertori di istruzioni; si può quindi
assumere la macchina RAM come una ragionevole approssimazione dei calcolatori
reali dal punto di vista dell’analisi di complessità e i risultati ottenuti ragionando in
modo formale sui programmi RAM dovrebbero essere immediatamente trasferibili
ai tipici programmi per calcolatore.
Una volta introdotto il modello, affrontiamo quindi il problema della
valutazione della complessità computazionale dei programmi RAM. Come prima
cosa, analogamente a quanto fatto per le MT, bisogna identificare l’unità di misura
(che nelle MT era stata identificata nella transizione). Nel caso della RAM tale
decisione non è ovvia, perché a differenza delle MT in cui ogni transizione ha un
numero finito di alternative possibili e quindi ha un tempo di esecuzione non legato
ai dati su cui opera,, nella RAM l’esecuzione delle diverse operazioni dipende
dagli operandi (che in questo caso sono interi e non singoli simboli come nel caso
delle MT).
Diventa quindi più importante analizzare le diverse istruzioni e definire il
tempo richiesto per l’esecuzione di ciascuna e lo spazio richiesto per ciascuna
locazione di memoria e per l’accumulatore (si ricordi che il programma non è
immagazzinato in memoria). Questa quantità può essere definita principalmente in
due modi: attraverso il criterio del costo uniforme o attraverso criterio del costo
logaritmico.
Il criterio del costo uniforme si basa sull’assunzione che l’esecuzione di
ciascuna istruzione richieda un’unità di tempo e ciascuna locazione di memoria e
l’accumulatore richiedono un’unità di spazio. Con tale criterio di costo si fa
un’analisi simile a quella fatta per le MT: le istruzioni rappresentano l’unità di
tempo e le celle di memoria l’unità di spazio. Nella RAM tuttavia, come abbiamo
osservato, le istruzioni corrispondono a operazioni di diversa natura e possono
manipolare dati di diversa dimensione, così come le celle di memoria contengono
interi e non semplici simboli. Viene naturale, quindi, chiedersi se tale criterio di
costo è realistico.
La domanda è lecita; basta infatti riflettere sul fatto che, nella pratica, le
istruzioni di ingresso/uscita sono certamente più lente degli altri tipi di istruzioni;
la valutazione degli operandi è più lenta per l’indirizzamento indiretto rispetto
416 Informatica teorica

all’indirizzamento diretto, mentre l’indirizzamento diretto risulta più lento di


quello immediato. Per tenere conto di questi fatti, si potrebbe modificare
leggermente il criterio del costo uniforme, supponendo che l’esecuzione di
ciascuna istruzione i della RAM richieda k, unità di tempo, dove k, è una costante.
Si intuisce facilmente che questa supposizione non cambia il risultato dell’analisi
di complessità in termini di notazione d’ordine (theta-grande), in quanto le costanti
non influenzano la classe di complessità.
Problemi molto più seri, relativi al criterio del costo uniforme, sono invece
dovuti al fatto che i valori interi immagazzinati nelle celle di memoria della RAM
possono risultare arbitrariamente grandi, non essendo semplici simboli
dell’alfabeto come nel caso delle MT. Inoltre, il dispositivo di memoria RAM è
illimitato, quindi, i suoi indirizzi possono essere arbitrariamente grandi. Di
conseguenza, si può supporre con sicurezza che il tempo richiesto per eseguire una
istruzione RAM sia prefissato e indipendente dai valori degli operandi, solo
nell’ipotesi che ciascuno di essi possa essere contenuto in una singola cella di
memoria. Ad esempio, nonostante la disponibilità di celle di memoria di 128 bit di
molti calcolatori moderni, una somma (ADD) di due interi molto grandi ( > 2128
richiederebbe più di una unità di tempo per la sua esecuzione e di una cella di
memoria per operandi e risultato. Lo stesso vale per le altre operazioni aritmetiche
(SUB, e, a maggior ragione, MULT e DIV). Analogamente, si può supporre che
l’accesso a locazioni di memoria venga effettuato in un tempo limitato a priori solo
se la dimensione stessa della memoria è limitata. Analizziamo dunque T efficacia di
questo primo criterio di costo attraverso due esempi, il primo mirato a mostrare che
in alcuni casi il criterio risulta realistico, il secondo invece utilizzato per mostrare i
limiti del criterio.

Esempio 9.13
Si consideri il programma RAM in Figura 9.11. Tale programma legge da tastiera
un numero n e, per tutti i numeri da 1 fino a n stampa a video un 1 se il numero è
dispari. Appare evidente che la parte che concorre maggiormente alla complessità
del programma è il ciclo (CICLO) che viene ripetuto n volte. Infatti, tutte le
istruzioni che lo precedono e lo seguono vengono ripetute esattamente una volta. Il
numero di operazioni ripetute all’interno del ciclo è costante e non dipende dal
valore di n, per cui possiamo dire che la complessità temporale del programma
considerato è pari a 0(«).
Si osservi che il dato in ingresso n non viene manipolato in alcun modo
durante l’esecuzione del programma e che l’unica altra variabile, utilizzata come
contatore e salvata in M[2], raggiunge al più il valore n. Questo significa che la
complessità dell’algoritmo è al più influenzata linearmente dalla dimensione di n
(che, poiché n è codificato in binario, è log ri) e quindi la complessità trovata, senza
considerare tale parametro, risulta realistica.
417
La complessità del calcolo

Programma RAM Commento


READ 1 Il valore letto in ingresso (ri) è memorizzato in A7] 1 ].
LOAD= 1 Il valore 1 viene caricato nell’accumulatore.
STORE 2 Il valore 1 viene memorizzato in M[2], che viene
utilizzato da contatore.
CICLO: LOAD 2 Il valore di A/[2] viene caricato nell’accumulatore e
SUB 1 gli viene sottratto il valore contenuto in A/[l],
JGZ FINE Se il valore del contatore è maggiore di n, allora il
programma termina.
LOAD 2 M[2] viene nuovamente caricato nell’accumulatore,
DIV= 2 diviso per 2 e
MULT= 2 nuovamente moltiplicato 2.
SUB 2 Al valore ottenuto come (A7| 2] ! 2)* 2 viene sottratto
M2].
JZ NO Se A/[2] è divisibile per 2, allora ((M[2] ! 2)*2) -
2] è pari a 0; ciò implica che n è un numero pari.
WRITE= 1 Viene stampato il valore 1.
NO: LOAD 2 M\2\ viene caricato nell’accumulatore,

ADD= 1 viene incrementato di 1 e


STORE 2 nuovamente salvato in M[2],

JUMP CICLO La sequenza di istruzioni che comincia con CICLO


viene ripetuta.
FINE: HALT

Figura 9.11 Programma RAM che risolve il problema dell’Esempio 9.13.

Esempio 9.14
Consideriamo il problema del calcolo del doppio fattoriale. Un semplice modo per
risolvere tale problema è riportato in Figura 9.12. La soluzione proposta, dopo aver
letto l’ingresso (n), fa uso di due cicli: il primo, che viene ripetuto n volte, calcola
n\, il secondo, che viene ripetuto ni volte, calcola («!)!.
Appare evidente che la complessità temporale è dominata dal secondo ciclo
(CICLO2), che viene ripetuto ni (ottenendo così una complessità pari a ©(«!)). In
questo esempio, però, i dati utilizzati all’interno dei cicli sono ottenuti
moltiplicando valori proporzionali a n, fino ad ottenere («!)!, la cui dimensione in
bit è &(nln log rìf. Risulta quindi irrealistico immaginare che la gestione di un
dato di tale dimensioni non influenzi la complessità temporale.

6 Si veda la Sezione 9.8 per una dimostrazione che log (zw!) è ©(w.log m). Si ponga quindi
m = ni per ottenere quanto affermato.
418 Informatica teorica

Programma RAM Commento


READ 1 Il valore letto in ingresso (n) è memorizzato in à/[1].
LOAD= 1 Il valore 1 viene caricato nell’accumulatore
STORE 2 e salvato sia in M[2] (cella che conterrà ni)
STORE 3 che in à/[3] (contatore).
CICLO 1: LOAD 3 Il valore del contatore
SUB 1 e n vengono confrontati.

JGZ FINE1 Se il ciclo è già stato eseguito n volte, termina,


LOAD 2 altrimenti, il valore parziale del fattoriale
MULT 3 viene aggiornato
STORE 2
LOAD= 1 e il contatore incrementato.
ADD 3
STORE 3
JUMP CICLO 1
FINE1: LOAD= 1 Il valore 1 viene caricato nell’accumulatore
STORE 1 e salvato sia in Affi] (cella che conterrà («!)!)
STORE 3 che in A/[3] (contatore).
CICLO2: LOAD 3 Il valore del contatore
SUB 2 e ni vengono confrontati.
JGZ FINE2 Se il ciclo è già stato eseguito ni volte, termina,
LOAD 1 altrimenti, il valore parziale del doppio fattoriale
MULT 3 viene aggiornato
STORE 1
LOAD= 1 e il contatore incrementato.
ADD 3
STORE 3
JUMP CICLO2
FINE2: WRITE 1
HALT

Figura 9.12 Programma RAM che risolve il problema dell’Esempio 9.14


(calcolo del doppio fattoriale).

Le osservazioni fatte in precedenza e i limiti del criterio di costo uniforme


sottolineati nell’Esempio 9.14 conducono all’altro criterio di costo citato in
precedenza: il criterio di costo logaritmico. Questo criterio è basato sulla
supposizione che il tempo richiesto per eseguire un’istruzione sia proporzionale
alla lunghezza degli operandi dell’istruzione considerata. Poiché gli operandi sono
rappresentati in memoria in codice binario, un operando di valore v è rappresentato
da [log2 |v| J + 1 bit (1 bit se v = 0). Di conseguenza, se l è la seguente funzione sui
numeri interi:

Z(z) = if zVO then Llog2 |v|J + 1 else 1


419
La complessità del calcolo

si può definire la complessità temporale logaritmica delle varie istruzioni RAM


come mostrato nella Tabella 9.2. Si noti che le istruzioni STORE e le istruzioni
aritmetiche hanno un operando implicito: l’accumulatore (Af[0]). Quest’ultimo
viene quindi esplicitamente tenuto in conto nelle espressioni che forniscono il
costo logaritmico.
La medesima filosofia si applica ai costi relativi allo spazio. Sia m l’indirizzo
più alto della cella di memoria cui si fa accesso durante l’esecuzione di un
programma RAM, e sia Mt il valore assoluto più grande immagazzinato in M[i\
durante l’esecuzione. Si definisce allora la complessità spaziale logaritmica del
programma come la somma:
m

i =0

Tabella 9.2 II costo logaritmico delle istruzioni RAM.


Formato dell’istruzione Costo logaritmico__________
LOAD= X /(x)
LOAD X /(x) + Z(A/[x])
LOAD* X l(x) + Z(A/[x]) + /(«x]])

STORE X /«°D + /(x)


STORE* X /«od + + /«*D

ADD= X /«OD + /(x)


ADD X /«od + /(x) + /«x])
ADD* X /«OD + /(x) + /«x]) + /«M[x]D
SUB, MULT, DIV' sono definite esattamente come per ADD

READ X /(valore di ingresso corrente) + /(x)


READ* X /(valore di ingresso corrente) + /(x) + /«XI)

WRITE= X /(x)
WRITE X /(x) + /«x])
WRITE* X /(x) + /«x]) + /«Mx]D

JUMP lab 1
JGZ lab /«od
JZ lab /«od

HALT 1

7 Si noti che attribuire alle operazioni moltiplicative lo stesso costo di quelle additive è
alquanto ottimistico perché non sono noti algoritmi che eseguano queste operazioni in
tempo proporzionale alla lunghezza dei loro operandi. Ciò conferma l’osservazione che un
ragionamento rigoroso dovrebbe escludere queste operazioni dall’insieme di quelle assunte
come elementari.
420 Informatica teorica

Analizziamo ora il problema affrontato nell’Esempio 9.14 con il criterio di costo


logaritmico in modo da poter iniziare a comprendere le differenze tra i due criteri.

Esempio 9.15
Consideriamo nuovamente il problema del calcolo del doppio fattoriale, la cui
soluzione è riportata in Figura 9.12. Come osservato nell’Esempio 9.14, in cui
abbiamo analizzato la complessità di tale soluzione con il criterio del costo
uniforme, il programma, dopo aver letto l’ingresso (m), fa uso di due cicli: il primo,
che viene ripetuto n volte, calcola ni, il secondo, che viene ripetuto ni volte,
calcola («!)!.
Appare evidente che anche in questo caso la complessità temporale è dominata
dal secondo ciclo (CICLO2), che viene ripetuto ni e all’interno del quale vengono
svolte operazioni sui dati fino al calcolo di («!)!. Le operazioni dominanti
all’interno di tale ciclo sono:
LOAD 1
MLTLT 3
STORE 1
che, facendo riferimento ai costi in Tabella 9.2, hanno rispettivamente i seguenti
costi:
Z(l) + Z«l])
/«OD + 1(3) + Z«3])
Z«O]) + Z(1)
Il dato che viene immagazzinato nella cella 1 è quello che domina la complessità
spaziale e, ad ogni iterazione, è pari a i!, fino a valere (n!)!. Perciò la complessità
spaziale del programma risulterà 0(n!n log n), cioè la dimensione in bit di (n!)!.
Poiché la complessità temporale, a parità di criterio di costo, è sempre maggiore o
uguale alla complessità temporale, la complessità temporale con il criterio del
costo logaritmico risulterà molto superiore alla complessità temporale valutata con
il criterio del costo uniforme (0(n!)), mostrando quindi che tale critero risulta
irrealistico quando il programma in analisi gestisce dati di grandi dimensioni. Si
lascia al lettore come esercizio la valutazione esatta di tale complessità

Non è dunque sorprendente che i risultati dell’analisi di complessità siano molto
diversi, a seconda del criterio di costo adottato. Il criterio del costo uniforme è
semplice da applicare, ma può essere impiegato con sicurezza solo se si prevede
con certezza che ogni valore che comparirà nel corso della computazione potrà
essere immagazzinato in una sola cella di memoria. Altrimenti, bisogna riferirsi al
criterio di costo logaritmico. Infatti, come visto negli Esempi 9.14 e 9.15, il criterio
di costo uniforme risulta anche gravemente inadeguato per analizzare la
complessità di un programma che calcola la funzione doppio fattoriale per valori di
ingresso non piccolissimi (basti pensare al valore di (10!)!).
In pratica, si potrebbe tener conto di entrambi i criteri nel modo seguente. Sia
k la lunghezza della parola del calcolatore impiegato (in generale k vale 8, 16, 32,
421
La complessità del calcolo

64 o 128). Per ogni intero positivo i, sia s l’intero definito dalla seguente
disuguaglianza:
2k■ s-1 < . < 2^(5 + 1)_i

Si ridefinisca allora Z(z) come s + 1. Intuitivamente, Z(z) indica il numero di parole


richieste per immagazzinare i. Ciò si riduce al criterio di costo uniforme se i<2k~
\ Tuttavia, per motivi di semplicità, si preferisce non usare la precedente formula
(che tuttavia non cambia l’ordine di grandezza della valutazione di complessità a
criterio di costo logaritmico) e si lascia al lettore l’accortezza di comprendere
quando il criterio del costo uniforme risulta inappropriato.
Per approfondire ulteriormente la differenza tra i due criteri, analizziamo un
ulteriore esempio, che calcola la complessità del programma RAM proposto
nell’Esempio 9.11.1 simboli T e S indicano le funzioni di complessità temporale e
spaziale secondo entrambi i criteri di costo.

Esempio 9.16
Si consideri il programma RAM mostrato in Figura 9.9. Per valutare la sua
complessità temporale (nel caso pessimo) T, secondo il criterio di costo uniforme,
si conta semplicemente il numero massimo di esecuzioni di istruzioni.
E immediato comprendere che:
a. Le prime sei istruzioni vengono eseguite al più una volta.
b. Due delle ultime quattro istruzioni sono eseguite una sola volta.
c. Le istruzioni contenute fra “CICLO: LOAD 1” e “JUMP CICLO” vengono
ripetute al più À/[l] - 2 volte, dove A/[l] = n (n è il valore di ingresso).
d. La sequenza di tre istruzioni che comincia da CICLO viene eseguita ancora
un’altra volta.

Quindi, supponendo n > 2, T(rì) = 6 + 2 + 12(z? - 2) = 12-n - 13 (se n>2) (ossia Tè


©(«))•
Per ottenere T(n) secondo il criterio di costo logaritmico, si può adattare
l’analisi precedente sostituendo l’opportuno costo logaritmico al costo unitario
delle singole istruzioni. Riferendosi ai componenti precedenti del costo totale (da a.
a d.), si può effettuare un’analisi di costo dettagliata.
Costo di a.:
Istruzione Costo________
READ 1 1 + l(n)
LOAD= 1 1
SUB 1 1 + 1 + Z(zz)
JZ Sì l(n - 1) < l(n)
LOAD= 2 2
STORE 2 2+2
Totale parziale arrotondato 10 + 3-Z(n)
422 Informatica teorica

Costo di b.: 2
Costo di c.:
Istruzione Costo______________________________
CICLO: LOAD 1 1 + Z(m)
SUB 2 Z(») + 2 + Z(VZ[2])
a: JZ Sì W])
LOAD 1 1 + Z(m)
DIV 2 Z(») + 2 + Z(ÀZ[2])
MULT 2 Z(n/M[2]) + 2 + Z(ÀZ[2]) < Z(») + 38
SUB 1 Z(ÀZ[O])+ 1 + Z(»)<2-Z(») + 1
JZ NO Z(A/[0]) < Z(rz)
LOAD 2 2 + Z(ÀZ[2])
ADD= 1 Z(ÀZ[2]) + 1
STORE 2 Z(AZ[2] + 1) + 2 < Z(AZ[2]) + 3
JUMP CICLO j
Totale parziale arrotondato 17 + 8- l(n) + 5 ■ Z(ÀZ[2]) + Z(À/[0])
Si noti che LZ[2] assume i valori 2, 3,..., n - 1; A/[0] assume i valori n - 2, n - 3,
1. Quindi, il totale parziale dovuto a n - 2 iterazioni è
n-1 n-2

17 - (m-2) + 8-(m-2)-Z(m)+5-^Z(z) + Z(z)


i= 2 i=1

Costo di d.: 1 + l(rì) + Z(«) + 2 + Z(«) +1=3 -Z(n) + 4


Raggruppando tutti e quattro i termini e tralasciando i dettagli, si può facilmente
osservare come la complessità temporale sia 0(«-log ri).
Si sarebbe potuto giungere direttamente a questa conclusione considerando
che la complessità temporale del programma è dominata dal ciclo e, aH’intemo del
ciclo, dalle operazioni MULT e DIV (si veda la valutazione dettagliata dei costi di
c.). Quindi, poiché viene eseguito un totale di (n - 2) operazioni del tipo MULT e
DIV, la complessità temporale è 0(wlog ri), secondo il criterio di costo
logaritmico. Secondo il criterio di costo uniforme, invece, ciascuna operazione del
ciclo costa una unità di tempo: quindi la complessità temporale risultante è 0(«).

ESERCIZI
9.23 Si valuti la complessità spaziale del programma RAM mostrato in Figura
9.8. Si usi sia il criterio di costo uniforme che il criterio di costo
logaritmico.

8 Si ricordi che log(a/Z>) = log a - log b.


423
La complessità del calcolo

9.24 Si valutino le complessità spaziali e temporali del programma RAM


mostrato in Figura 9.9. Si usi sia il criterio di costo uniforme che il criterio
di costo logaritmico.
9.25 Si scriva un programma RAM per valutare rì mediante un ciclo di n
moltiplicazioni. Si dimostri che, secondo il criterio di costo uniforme, T è
0(n) e S è una costante. Secondo il criterio di costo logaritmico, T è
0(n2 ■ log rì) e S è 0(n ■ log rì).
Suggerimento: La complessità temporale è dominata dal ciclo di
moltiplicazioni, che viene eseguito n volte. Ciascuna moltiplicazione (come
ogni altra istruzione) costa una unità di tempo, secondo il criterio di costo
uniforme; costa invece ©(/(«')) secondo il criterio di costo logaritmico
poiché, in ciascun passo 1, 2, ..., i, ..., n - 1, il risultato parziale rì viene
moltiplicato per n. Tuttavia
n-\
'^l(rì) è 0(z?2 ■ log n)
1=1
La complessità spaziale, nel caso del costo logaritmico, dipende dal fatto
che il maggiore valore immagazzinato nelle celle di memoria durante
l’esecuzione vale rì, che richiede una cella di memoria di 0(wlog rì) bit.
9.26 Anche secondo il criterio di costo logaritmico, supporre che il costo di
MULT e di DIV sia pari al costo di ADD e di SUB potrebbe rivelarsi
inadeguato. Si scriva un programma RAM che calcola rì senza operazioni
MLTLT e si verifichi la sua complessità.
Suggerimento: si può supporre di avere a disposizione, come funzione
elementare, la funzione “moltiplica per 2”, che si può facilmente
implementare mediante imo scorrimento dei bit di un registro.
9.27 Si progettino MT a k nastri (con opportuni valori di k) e programmi RAM
che risolvano i seguenti problemi; si confrontino le loro complessità
applicando, ai programmi RAM, i due criteri di costo.
1. Riconoscere il linguaggio L = {anbkn\ n,k > 1}.
2. Riconoscere il linguaggio Lt = {rìb,n\ n > 1, 1 < i < k}.
3. Riconoscere il linguaggio L = {w1cw2c...w^ k > 2, wie{a,b}+, 3 ij,
i j tale che iv, = w,}.
4. Calcolare il fattoriale di n.
5. Per i coefficienti a0, «i, —, a», e la variabile x dati, calcolare il
polinomio
UnX” + Un-ix"'7 + ... + O{X + «(;
In alcuni casi si suggerisce di non sviluppare il progetto nei minimi dettagli,
ma solo di raggiungere un punto in cui il grado di complessità della
computazione possa essere determinato con sicurezza.
424 Informatica teorica

9.3.2 Linguaggi di programmazione di alto livello


Nell’analisi della complessità computazionale sorgono due esigenze diverse e
contrastanti. Da una parte i modelli di computazione devono essere
sufficientemente vicini ai calcolatori reali, se ci si aspetta di ottenere risultati
realistici dall’analisi di complessità, d’altra parte, si preferirebbe effettuare l’analisi
algoritmica senza preoccuparsi di troppi dettagli dipendenti dal particolare
linguaggio e processore impiegato per effettuare realmente le computazioni.
Spesso, i modelli di computazione analizzati fino ad ora sono ad un livello
troppo basso per poter essere impiegati in pratica. Dopo aver usato la RAM per la
soluzione di semplici esercizi di programmazione, il lettore concorderà con la
necessità di disporre di un linguaggio di programmazione a livello più alto,
specialmente se si desidera progettare ed analizzare programmi più lunghi in modo
sicuro, facile e comprensibile.
In questa sezione definiamo perciò un nuovo modello computazionale che
esibisce le caratteristiche essenziali dei linguaggi di programmazione di alto
livello. Questo modello, anche se verrà progressivamente arricchito per
fronteggiare nuove esigenze; rimane un linguaggio “giocattolo”, ossia una
semplificazione di un vero linguaggio di programmazione. I concetti qui esposti,
tuttavia, consentiranno di affrontare, in modo sicuro, lo studio della complessità dei
programmi scritti in veri linguaggi di programmazione ottenendo un buon
compromesso tra semplicità e precisione. Ad esempio, riusciremo a studiare i
metodi Java, presentati nella Sezione 9.1 (ricerca e ricerca_binaria), e a
ripercorrere la loro analisi su di un terreno più solido.
Il linguaggio che viene studiato qui è chiamato SMALG (Small ALGOL o
ALGOL semplificato) in omaggio all’ALGOL-60, l’antenato comune della
maggior parte dei linguaggi di programmazione moderni. La macchina che esegue
i programmi SMALG è chiamata SMALM (Macchina SMALG). La sintassi di
SMALG è definita dalla grammatica non contestuale data in Figura 9.13, di cui un
frammento era già stato riportato nella Figura 7.A. 1.
SMALG ha istruzioni di controllo strutturate: sequenze (StatList), frasi
condizionali (CondStat) e cicli (WhileStat). I dati elementari sono di tipo intero,
mentre i dati strutturati sono vettori monodimensionali (array). Le espressioni
relazionali (Cond) e aritmetiche (Exp) sono una versione semplificata delle
strutture corrispondenti dei linguaggi di programmazione reali.
La Figura 9.14 mostra qualche esempio di programmi SMALG (non molto
utili in pratica ma utili a scopo introduttivo), il cui significato non richiede
spiegazioni.
Tuttavia, per svolgere un ragionamento rigoroso sui programmi SMALG e, in
particolare, sulla loro complessità computazionale, bisogna approfondire l’analisi
del linguaggio. In particolare, è importante acquisire nozioni rigorose che
consentano di effettuare l’analisi di complessità su una solida base teorica.
La complessità del calcolo 4

G = (VT, Vn, P, (Program))


Vt~ { if, else, while, {,},
a, b, c, z, A, B, C, Z,
0, 1, 9, *, /, %, =, !, >, <,
(, ) , [, ], ;, print, read}
VN = { (Program, (StatList), (Stat), (AssStat), (CondStat), (WhileStat),
(inputStat), (OutputStat), (Var), Expr), (Terni), (SimpleVar),
(indexedVar), (Ident), (ArrayVarld), (Index), (Const), (Digit),
(Letter), (Cond), (RelOp)}
P = { (Program) { (StatList) }
(StatList) —> (Stat);(StatList) | £
(Stat) —> (AssStat) | ( CondStat ) | (WhileStat) | ( InputStat ) | (OutputStat)
(AssStat)-» (Var) = (Expr)
(Var) -> (SimpleVar) | (indexedVar)
(SimpleVar) —> (Ident)
(indexedVar) (ArrayVarld) [ (Index) ]
(ArrayVarld) —> (Ident)
(Index) —> (Const) | (SimpleVar)
(Expr) -> (Terni) + (Terni) | (Terni) - (Temi) | (Terni) * (Terni) |
(Terni) / (Terni) | (Terni) % (Terni) | (Terni)
(Temi) -> (Var) | (Const)
(Const) —> (Digit) | (Digit)(Const)
(Digit)—» 0| 1 | 2 | ... | 9
(Ident) —» (Letter) | (Letter)(ldent)
(Letter) -»a|b|...|z|A|B|...|Z
(CondStat) -» if ((Cond)) {(StatList)} else{(StatList)} |
if ((Cond)) {(StatList)}
(WhileStat)^ while ((Cond)) {(StatList)}
(Cond) —» (Expr)(RelOp)(Expr)
(RelOp)—» = | !=|>|>=|<|<=
(InputStat) —» read ( (Var) ) .
(OutputStat) —» print ((Var))

Figura 9.13 La grammatica di SMALG.


426 Informatica teorica

1. {i = 5 ; j = 10; x = 3 ;
if(i != j){a[2] = x + i;} else{a[j] = 6;}}
2. {while(5 == 5){x = 3;
if(x < 3){x = x + 1;}
else(x = x-1;}}}
3. {read(n);
if (n <= 0){write(n);}
elsefz = n; c = n-1;
while(c > 0){z = z * n; c = c -1;}
write(z);}}

Figura 9.14 Alcuni esempi di programmi SMALG.

Il lettore avrà probabilmente già compreso, durante l’analisi delle macchine a stati
(AF, AP e MT) e delle RAM, che il problema della definizione rigorosa delle
trasformazioni di stato (ossia delle computazioni) compiute da macchine astratte o,
“programmi”, diventa molto più difficile quanto più potenti ed astratte diventano le
macchine. Infatti, mentre le funzioni di transizione degli AF sono immediatamente
descrivibili mediante una semplice tabella, le transizioni delle MT richiedono
maggiori dettagli per la loro definizione formale e le trasformazioni di stato delle
RAM sono ancora più complicate (ad esempio quando si ha a che fare con
l’indirizzamento indiretto). Quando si raggiunge il livello di astrazione dei
linguaggi di programmazione di alto livello, sorge qualche difficoltà. Poiché lo
studio della semantica formale, operazionale o denotazionale che sia, non rientra
negli scopi di questo libro, ci limiteremo a definire lo spazio di stato di SMALM e
le trasformazioni operate dai programmi SMALG in modo piuttosto informale ma
sufficientemente preciso da permettere un’analisi rigorosa della loro complessità.
Lo spazio di stato di SMALM consiste di:
1. Una locazione di memoria che contiene un valore intero per ciascuna variabile
semplice. La locazione associata alla variabile semplice x sarà indicata con
Mx.
2. Una sequenza illimitata di locazioni di memoria contenenti valori interi per
ciascuna variabile vettoriale. Una variabile indicizzata a [ i ] è associata a:
2.1 La i-esima locazione nella sequenza associata ad a, se i è una costante
intera di valore maggiore di zero.
2.2 La j -esima locazione nella sequenza associata ad a, con j > 0, se i è una
variabile semplice e j è il valore memorizzato nella locazione M±.
2.3 In tutti gli altri casi a [ i ] è indefinita.
3. Un nastro di ingresso e di uscita, come nel caso della RAM.
La Figura 9.15 fornisce una descrizione grafica dello spazio di stato di SMALM
per il programma SMALG numero 1 di Figura 9.14. Oltre ai nastri di ingresso e di
uscita, sono presenti tre locazioni (AL, Mj e AQ, associate alla variabili i, j e x, e
le locazioni di memoria necessarie per memorizzare l’array a.
427
La complessità del calcolo

Nastra di ingresso Mi Ma [1]


■---------- —
__________________ Af; [2]
...............
M Ma [3]
Nastro di uscita x

Figura 9.15 Spazio di stato di SMALM per il programma SMALG numero 1 di


Figura 9.13.

Le trasformazioni di stato e le loro complessità temporali sono definite qui sotto


per le diverse istruzioni SMALG:
1. Istruzione di assegnamento ((AssStat)-» (Var) - (Expr);)

la. Viene valutato il valore dell’espressione, accedendo ai valori immagazzinati


nelle locazioni corrispondenti agli identificatori che compaiono in (Expr).
Ib. Successivamente, il risultato viene immagazzinato nella locazione
corrispondente all’identificatore denotato da <Var>.
Sia la complessità spaziale che quella temporale di questa istruzione dipendono
ovviamente da come effettivamente SMALM effettua le sue operazioni.
Supponendo, come generalmente si verifica, che le operazioni di SMALM siano
decomponibili in sequenze di operazioni più semplici del tipo di quelle della RAM,
si può ricavare l’espressione della complessità temporale, calcolata secondo le
regole della Tabella 9.3. La tabella usa una notazione di tipo Java, che non
necessita di ulteriori spiegazioni, per mostrare come la complessità temporale
complessiva di un’istruzione di assegnamento risulti dalla complessità temporale
dei suoi componenti sintattici. La funzione l, usata nelle espressioni del costo
logaritmico, è esattamente la stessa funzione già vista per la RAM.
Di nuovo, si sottolinea come le definizioni della Tabella 9.3 sottintendano
alcune scelte arbitrarie. Ad esempio, come nel caso della RAM, si è scelto di
assegnare il medesimo costo ad ogni operatore aritmetico, sia secondo il criterio di
costo uniforme che secondo quello logaritmico. Questa supposizione non è
chiaramente sempre realistica, poiché, come già osservato in precedenza alcune
operazioni possono essere derivate da altre attraverso l’uso di cicli. Si è anche
affermato che il costo dell’accesso ad un elemento di un vettore, il cui indice sia
noto prima di eseguire il programma, è sempre 1. Per questa ragione si ricava che
l’accesso ad a [5] richiede 1 unità di tempo anche secondo il criterio di costo
logaritmico, mentre l’accesso ad a[x] richiede una quantità di tempo variabile.
Questa è un’ipotesi ragionevole, ma non necessaria. Si noti infine che, secondo le
regole date, ogni istruzione di assegnamento può essere eseguita, da SMALM, in
428 Informatica teorica

Tabella 9.3. Complessità temporale di un’istruzione di assegnamento in termini


di complessità dei suoi componenti.
Costo uniforme Costo logaritmico
^AssStat 7” Var + rExBr + 1 7”var + Axpr + /(valore di (Expr))
Axnr if (Expr) è del tipo if (Expr) è del tipo
(Term 1 )operatore<'Tcrm2) (Term 1 )operatore(Term2)
then rTeml + rTem2 + 1 then
ihvu i TTermi.
t , +1 1TTerm2.
t +
else {è del tipo (Term)} TTenn +/(valore di (Termi))
+ /(valore di (Term2))
else {è del tipo (Term)} TTerm
Acrm if (Expr) è un (Var) if (Expr) è un (Var)
then rVar then Tyar
else {è una (Const)} 1 else /(valore di (Const))
T’-Var if (Var) è un (SimpleVar) if (Var) è un (SimpleVar)
è il tempo necessario then 1 then 1
per accedere alla else if (Var) è ima (IndexedVar) else if (Var) è una (IndexedVar)
locazione associata e (Index) è un (Const) e (Index) è un (Const)
alla variabile then 1 then 1
else {(Var) è una (IndexedVar) else {(Var) è una (IndexedVar)
e (Index) è un (Var)} 2 e (Index) è un (Var)}
1+ /(valore di (Index))
7\’ar if(Var) è un (SimpleVar) if (Var) è un (SimpleVar)
è il tempo necessario then 1 then 1 + /(valore di (Var))
per accedere al valore else if (Var) è una (IndexedVar) else if (Var) è una (IndexedVar)
associato alla variabile e (Index) è un (Const) e (Index) è un (Const)
then 1 then 1 + /(valore di (Var))
else 2 else {(Var) è una (IndexedVar)
e (Index) è un (Var)}
1 + /(valore di (Var))
+ /(valore di (Index))

una quantità di tempo limitata, secondo il criterio di costo uniforme. Quindi, finché
si considera la notazione theta-grande, non bisogna preoccuparsi molto del valore
esatto delle costanti che denotano le complessità temporali delle singole istruzioni
di assegnamento.

2. Lista di istruzioni ((StatList) —> (Stat);(StatList) | e )

2.1. Se (StatList) è la lista di istruzioni vuota, allora SMALM non effettua alcuna
operazione e T<statList> = 0.
2.2. Se (StatList)> è una (Stat) seguita da una (StatList), allora SMALM esegue
l’istruzione secondo le regole da 1 a 5 e, successivamente, esegue la lista di
istruzioni secondo la regola 2, applicata ricorsivamente. In questo caso,
T/statList) = T(stat) + ^statListy dove T(StatListy denota il tempo richiesto per eseguire
la lista di istruzioni nella parte destra della produzione.
La complessità del calcolo

3. Istruzione condizionale ((CondStat) if ((Cond)) {(StatList)} else


{(StatList)} | if ((Cond)) {(StatList)})

SMALM valuta la condizione booleana derivata da (Cond). Se il risultato è vero,


allora esegue la lista di istruzioni che segue (che chiameremo “ramo then”),
secondo la regola 2. Altrimenti, se c’è una parte else (potrebbe anche non esserci,
in quanto esistono due possibili riscritture di (CondStat)), esegue la lista di
istruzioni che segue l’else (che chiameremo “ramo else”), secondo la regola 2.
Se non c’è la parte else, l’effetto è lo stesso che si avrebbe se esistesse una parte
else con una lista vuota di istruzioni. Con questa convenzione, se T(statList)' e
7)statList>" sono i tempi necessari ad eseguire il ramo then e il ramo else,
rispettivamente,
r(Condstat) = 7)cond) + if (il valore di (Cond) è vero) then 7}statLiSt)-else T^tatListr

Le regole per valutare 7)Cond> sono simili a quelle date nella Tabella p.3 per 7^^.
Un limite superiore per 7)CondStat> è dato da
7}cond) + max{ 7}cond)', 7}Cond) "}•

4. Istruzione While ( WhileStat) -> while ((Cond)) {(StatList)})

SMALM valuta la condizione booleana derivata da (Cond). Se il risultato è falso,


l’esecuzione dell’istruzione while termina. Altrimenti, SMALM esegue la lista di
istruzioni che segue (Cond)e ripete nuovamente questo passo.
La complessità temporale per l’istruzione while è
7<whiiestat> = L<cond> + if (il valore di <Cond> è falso) then 0

else 7)statList) + T (WhileStat)

dove T (Whiiestat) è il tempo richiesto per calcolare la stessa istruzione while, dopo
l’esecuzione della lista di istruzioni.

5. Istruzioni di ingresso/uscita ((InputStat), (OutputStat))


5.1. Quando esegue un’istruzione di ingresso, SMALM legge il dato sotto la
testina del suo nastro di ingresso, lo memorizza nella variabile indicata e
sposta la testina di una cella verso destra.
Quindi Tjnputstat) = 1 secondo il criterio di costo uniforme, mentre secondo il
criterio di costo logaritmico può essere definito nel modo seguente:

^(inputstat) =/(valore letto) +


if ((Var) è una (SimpleVar)) then 1
else 1 + /(valore di (Index))
Quando (Var) non è una variabile semplice, allora è un elemento di un
array ((ArrayVarld) [(Index)]}, per cui oltre alla cella occupata dal dato (1),
430 Informatica teorica

bisogna tenere in considerazione la dimensione dell’indice (/(valore di


(Index)).
5.2. Le operazioni effettuate per eseguire un’istruzione di uscita sono duali e
Loutputstat = 7<var) dove ^Var è calcolato secondo la regola data
precedentemente nella Tabella 9.3.
Ora che abbiamo definito la complessità delle diverse istruzioni, possiamo definire
la complessità temporale globale di un programma SMALG P. Sia X il flusso di
valori memorizzati sul nastro di ingresso. TAX) = Z<statList), dove (StatList) è la lista
delle istruzioni del programma racchiuse nella coppia { e } e calcolate secondo le
precedenti regole, da 1 a 5, in corrispondenza dei dati di ingresso X.
La complessità spaziale di un programma SMALG P è definita dalla regola
seguente, nel caso si utilizzi il criterio del costo uniforme. Sia X la sequenza di
valori memorizzata sul nastro di ingresso:
Sp{X) = NSV + a
asA
dove
- NSV è il numero totale di variabili semplici referenziate da P
- A è la collezione delle variabili vettore referenziate da P
- per ogni a e A, Na è il massimo valore assunto dalle variabili che
referenziano componenti di vettori.
Qualora si consideri invece il criterio di costo logaritmico, oltre all’effettivo
numero di variabili (semplici o vettori) referenziate da P, bisogna tenere in
considerazione anche la loro dimensione. La complessità spaziale diventa quindi
Na -1

sp(x) = E/(*)+E/(A,V)+£ !?(“[*]>


xeSV asA a<=A i = 0
dove SV è l’insieme delle variabili semplici referenziate da P

Esempio 9.17
Applichiamo le regole precedenti ai programmi molto semplici di Figura 9.13.
1. L’esecuzione del programma SMALG numero 1, secondo il criterio di costo
uniforme, impiega un tempo
I\ = (1 + 1 + 1) + (1 + 1 + 1) + (1 + 1 + 1) + (1 + 1 + 1) + (1 + 1 + 1 + 1 + 1)
Secondo il criterio di costo logaritmico, impiega un tempo
7) < (1 + Z(5) + Z(5)) + (1 + /(IO ) + /(IO )) + (1 + Z(3 ) + 1(3)) + (1 + 1(5) + 1 +

+ /(IO) + max{( 1 + (1 + 1(3)) + (1 + 1(5)) + 1(8)), ((1 + /(IO)) + 1(6) + 1(6))}

In entrambi i casi, il programma è 0(1).


2. L’esecuzione del programma 2 richiede chiaramente un tempo T2 = °o,
secondo ogni criterio di costo, poiché non si ferma mai. Infatti,
TwhileStat — Tconci + /(StatList) + T (WhileStat)
431
La complessità del calcolo

2(77(Cond) 4“ ^(StatList)) + T (WhileStat) ••• 00


3. Il programma 3 è chiaramente il corrispettivo SMALG del programma RAM
dell’Esercizio 9.25, che calcola nn. Senza entrare in inutili dettagli relativi al
calcolo di tutte le costanti coinvolte nella funzione r3, è immediato
comprendere che lo stesso ragionamento del programma RAM si applica
altrettanto bene al suo corrispettivo SMALG.

Non è sorprendente che le complessità calcolate per i precedenti programmi
SMALG non siano altro che le complessità di opportuni programmi RAM, che si
possono considerare come la loro traduzione. La differenza fra i due non risiede
nella efficienza di esecuzione - né nella potenza computazionale - ma solo nella
comprensibilità del programma.
Analizziamo ora la complessità di un programma SMALG che risolva un
problema di maggior rilievo e studiamo l’ordine di grandezza della sua complessità
temporale.

Esempio 9.18
Si consideri il problema di verificare se, dato un array di n interi A e un intero x,
esistono in A due elementi che sommati diano x.
Consideriamo prima la seguente soluzione banale, che non fa nessuna ipotesi
suH’ordine in cui i valori appaiono in A:
{
output = 0;
i = 1;
while(i <= n){
j = 1;
while ( j <= n){
if(i != j){
if(A[i] + A[j] == x){
output = 1;
}
}
j = j + i;
}
i = i + 1;
}
}
La complessità temporale di tale algoritmo è, banalmente, 0(n2), se valutata con il
criterio di costo uniforme e diventa invece 0(n2log n), se valutata con il criterio di
costo logaritmico (in questo caso bisogna considerare oltre ai due cicli annidati
anche la dimensione dell’indirizzo delle celle in cui vengono memorizzati i valori
di A).
432 Informatica teorica

Osserviamo che la soluzione, e di conseguenza la complessità, cambia


notevolmente se si ipotizza che A sia già ordinato. Infatti, in questo caso sarebbe
possibile sfruttare l’ordinamento per produrre un algoritmo di ricerca più
sofisticato, codificato nel seguente programma SMALG:

{
output = 0;
i = 1 ; j = n;
while(i != j){
if<A[i] + A[j] == x){
output = 1 ;
i = j;
1
else{
if (A [i] + A[j] < x) {
i = i + 1; '
1
else{
j = j - 1;
1
1
1
1 ,

Tale algoritmo ha al suo interno un solo ciclo che scorre tutto l’array, per cui la sua
complessità sarà 0(n), valutata con il criterio di costo uniforme, e 0(n log n),
valutata con il criterio di costo logaritmico.
Nella Sezione 9.4.1 si discuterà approfonditamente il problema
dell’ordinamento, mostrando che tale problema richiede almeno 0(n log n)
(criterio del costo uniforme) per essere risolto e che esistono algoritmi con tale
complessità. Questo suggerisce, che anche quando non si possono fare ipotesi
sull’ordine dei dati in A, è più conveniente ordinare l’array e poi cercare la coppia
di elementi nell’array ordinato, che cercare tale coppia direttamente.

ESERCIZIO
9.28 Si codifichino i programmi SMALG di Figura 9.13 come programmi RAM
e si faccia l’opposto per il programma RAM dell’Esempio 9.6 e 9.7 e
l’Esercizio 9.27.
Sia PR il programma RAM corrispondente al programma SMALG P. Si
verifichi che, se P ha complessità temporale 7>, allora TPR è 0(T?) e
viceversa.
433
La complessità del calcolo

9.4 La valutazione della complessità all’atto pratico


Questa sezione non introduce alcun concetto nuovo sulla complessità
computazionale, ma fornisce ulteriori esempi dei concetti presentati altrove. Il suo
scopo è principalmente di consolidare la dimestichezza del lettore con la
valutazione della complessità computazionale. Lo schema di questa sezione è il
seguente: dapprima si discute la complessità di un programma di ordinamento; poi
si discute la complessità di un algoritmo sui grafi che calcola l’insieme di
raggiungibilità e si fanno alcune considerazioni generali sugli algoritmi riguardanti
i grafi; infine si chiude la sezione con qualche consiglio pratico.

9.4.1 Un esempio di ordinamento


Torniamo all’algoritmo di ordinamento noto come ordinamento per inserzione
diretta, di cui avevamo proposto una codifica in Java nella Sezione 8.3. La
versione SMALG è illustrata in Figura 9.16.
Cerchiamo ora di valutare la complessità temporale del programma SMALG
mostrato in Figura 9.16, trascurando il calcolo della complessità spaziale, che è un
problema decisamente banale.

{read(n)
while (i <= n){
read(a[i]);
i = i + 1;
1
while(i <= n){
x = a [i] ;
a [ 0] = x;
j = i - 1;
while(x <= a [ j]) {
k = j + 1;
a [k] = a [j ] ;
j = j - i;
1
k = j + 1;
a [k] = x;
i = i + 1;
1
i = 1;
while(i <= n){
print(a[i]);
i = i + 1;
1

Figura 9.16 Ordinamento per inserzione diretta in SMALG.


434 Informatica teorica

Come annotazione preliminare, bisogna aver presente che tutte le unità di costo
esaminate sono “astratte”, poiché non si riferiscono ad alcun specifico ciclo
macchina reale. Quindi, se si desidera determinare il tempo assoluto di esecuzione,
bisogna avere a disposizione maggiori informazioni sull’implementazione delle
singole istruzioni e sulla velocità fisica del calcolatore. Abbiamo già osservato,
tuttavia, che la notazione theta-grande fornisce utili risultati di validità generale
sulla complessità; in particolare essa mette in luce il comportamento asintotico
dell’algoritmo. Cerchiamo quindi, come primo passo, una descrizione di T che in
prima approssimazione ne definisca l’ordina di grandezza. In un secondo passo, si
potrebbe decidere di raffinare l’analisi per ottenere almeno un limite alle costanti
contenute nell’espressione di T. Fondiamo l’analisi dell’algoritmo sulla sua
descrizione SMALG illustrata in Figura 9.16, riferendoci alla SMALM come
modello computazionale.
Il passo successivo è la scelta di un criterio di costo. Una breve occhiata al
programma rivela che i dati non aumentano significativamente le loro dimensioni
nel corso dell’esecuzione. Sia m = max{n, a[l], ..., a[n]}; se le celle di
memoria possono immagazzinare m, allora saranno in grado di memorizzare ogni
altra variabile del programma durante l’esecuzione. Inoltre, il programma non
contiene alcuna moltiplicazione. Il criterio di costo uniforme sembra quindi
un’ipotesi piuttosto ragionevole.
Siamo ora pronti per cominciare la vera analisi di complessità. Dapprima
osserviamo che, secondo il criterio di costo uniforme, tutte le istruzioni di
assegnamento, di lettura, di scrittura e di valutazione di condizione si possono
eseguire in un tempo limitato da una costante opportuna, come riportato nella
Tabella 9.4.
Seguiamo ora i passi dell’esecuzione del programma.
1. Viene speso il tempo = Ci prima di entrare nel primo ciclo.
2. Il primo ciclo viene eseguito esattamente n volte e richiede un tempo
ti — fi = n ■ (ù2 + C3) + Ci
3. Il secondo ciclo comincia all’istante fi = fi + c4.
4. Il secondo ciclo viene eseguito esattamente n - 1 volte.
Quindi, all’uscita del secondo ciclo, il tempo è
n
G = t3 + (c2 + c5 + tj + C8 ) + c2
i= 2
n
= t3 + (n- l)(c2 + c5 + c8) + c2 + fi7
i=2
dove t? è il tempo richiesto per eseguire il ciclo più interno all’i-esima
iterazione del ciclo esterno.
435
La complessità del calcolo

Tabella 9.4 Limiti di tempo per il programma di Figura 9.16.

Esecuzione delle istruzioni Limite di tempo


read(n) ; i = 1 ; C1
i <= n C2
read( a [ i ] ) ; i = i + 1 ; C3
i = 2; q
x=a[i]; a[0] = x ; j =j - 1; c5
x < a [j ] c6
k = j + 1; a[k] = a[j]; j = j - 1; ci
k = j +1; a[k] = x; i = i + 1; Q
i = 1; q
i<=n c2
print(a[i]); i = i + 1; Cg

Per valutare t- , si noti che la condizione del ciclo interno può essere vera
al più i volte, poiché j è inizializzato ai- le viene decrementato a
ciascuna iterazione fino a quando raggiunge lo 0. A quel punto x = a [ 0 ]
e la condizione diventa falsa. Quindi, per ogni i, si ottiene
{i - z ■ (c6 + c7 ) + c6
Cosicché
n
n +1 ]
- <c6 + c7 ) • -------- «-1 +c6 '(«-!)
2 )
i=2

5. L’ultima porzione di programma richiede un tempo


— L = C4 + n (q + Cg) + C2
Mettendo tutto insieme, si ottiene t5 < q + c2n + c3n , per opportuni valori delle
costanti q, c2 , c3 facilmente ricavabili dalle precedenti costanti c\ ...Cg. Perciò, T
è ®(h2). ’
Si rende ora necessaria qualche osservazione.
ri. In base al criterio di costo uniforme, la complessità temporale è funzione solo
di n, ossia del numero di oggetti da ordinare. Se si applicasse il criterio di
costo logaritmico, essa dipenderebbe anche dai valori contenuti nel vettore. Se
si pone m = max{n, a[i] | i = 1,..., n}, si ricava facilmente una complessità
temporale ®(n2-log m). In generale, m non risulta correlato a n; in un certo
senso, ciò dimostra la validità della scelta del criterio di costo uniforme.
r2. Nella maggior parte dei casi, la notazione theta-grande fornisce esattamente
quanto richiesto per l’analisi di complessità. Ovviamente, se necessario, è
436 Informatica teorica

possibile calcolare il vero valore delle costanti cl e c(, a patto di avere


un’informazione completa sui dettagli implementativi, come, ad esempio,
l’esatta sequenza delle istruzioni in linguaggio macchina, la velocità di
esecuzione delle singole istruzioni e così via.
r3. I due cicli annidati costituiscono chiaramente il nucleo del programma e sono
responsabili della complessità ®(n2). Potrebbe tuttavia accadere che cx e c9,
che rappresentano il tempo di esecuzione delle istruzioni read e print,
risultino molto maggiori delle altre c, e quindi c2 »c3 . Come conseguenza,
in molti casi pratici, se il valore di n è sufficientemente piccolo, la
complessità temporale del programma risulta “dominata” da c2 (la costante
lineare) invece che da c3 (la costante quadratica). Durante l’analisi delle
prestazioni di un algoritmo, bisogna dunque concentrarsi sulle “reali” sorgenti
di complessità. Ad esempio, in molti programmi applicativi, può rivelarsi utile
tralasciare completamente il tempo di esecuzione della CPU per concentrarsi
unicamente sulle istruzioni di I/O.
r4. Bisogna sempre aver presente che T, per il modo in cui è stato definito,
rappresenta la “misura della complessità nel caso pessimo”. Si tratta
ovviamente di una misura importante, anche se non è l’unica significativa. In
molti casi può accadere che un algoritmo A\, che risolve un problema P, abbia
una complessità temporale Tt pari a ®(/i) nel caso pessimo, migliore rispetto
ad un altro algoritmo A2. Tuttavia A2 potrebbe rivelarsi preferibile rispetto a
Aa, sulla base delle sue prestazioni medie.
Proprio l’ordinamento è un esempio di tale situazione. Infatti, un algoritmo di
ordinamento molto famoso, chiamato “quicksort”, ha una complessità
temporale nel caso pessimo di ®(n2), ma una complessità temporale nel caso
medio che è ®(n-log n), con buone costanti di proporzionalità (anche le
costanti sono talvolta importanti), che lo rende spesso preferibile ad altri
algoritmi con complessità nel caso pessimo pari a ®(n - log n).

ESERCIZI
9.29 Si schematizzi una MT multinastro che implementa l’algoritmo di
ordinamento per inserzione diretta. Qual é il suo ordine di complessità?
9.30 Si può ottenere un ordine di complessità migliore impiegando, per il
problema dell’ordinamento, un’altra MT (senza necessariamente seguire la
traccia dell’algoritmo precedente)?
9.31 * L’esempio della Sezione 9.4.1 supponeva che n (la lunghezza del vettore
da ordinare) e m (il massimo dei valori immagazzinati nel vettore) fossero
valori indipendenti. Analogamente, l’Esempio 9.1 supponeva che n (la
lunghezza del vettore in cui effettuare la ricerca) e m (come prima) fossero
437
La complessità del calcolo

valori indipendenti. Si commenti la validità di questa ipotesi quando


n —> oo.

9.4.2 Un algoritmo sui grafi


I grafi sono una potente struttura matematica utilizzabile per modellare e risolvere
molti e diversi problemi. Per questo motivo, gli algoritmi sui grafi sono stati
oggetto di molti studi (alcuni elementi di teoria dei grafi sono stati brevemente
richiamati nella Sezione 1.4).
I grafi possono essere rappresentati da strutture dati diverse, come ad esempio:
a. Una matrice binaria {G[z, j|| per 1 < i,j < n, if (esiste un lato che connette v;
e v7) G[i,j] = 1 else G[i,j] = 0}.
b. Un insieme di coppie {(vf, v7)| esiste un lato che connette v, e v7}.
Entrambe le strutture si possono impiegare per rappresentare sia grafi orientati che
non orientati.
Per rappresentare i grafi usando SMALG, risulta conveniente arricchirlo con
vettori multidimensionali, per poter implementare la precedente codifica di grafi in
termini matriciali (soluzione a.). Alternativamente, si può mantenere il linguaggio
come definito nella grammatica in Figura 9.13 e rappresentare i grafi attraverso
una coppia di vettori, ad esempio JASfA] e VP[k], tali che (v„ v7) appartiene al grafo
se e solo se esiste un k per cui ESfÀ:] = i, VP[k] = j (VS e VP denotano “vertice
sorgente” e “vertice pozzo”, rispettivamente), rappresentando così la soluzione b..
Si noti che, per memorizzare un grafo, la prima rappresentazione richiede sempre
n2 celle, dove n è il numero dei vertici, mentre la seconda richiede 2m celle, dove
2
m è il numero dei lati. In generale m « n .
Si consideri ora il seguente problema sui grafi. Sia G = (V, É) un grafo
orientato. Per ogni vertice v; e V, si desidera calcolare il suo insieme di
raggiungibilità (ossia l’insieme dei vertici = {v71 esiste un cammino da v; a v7}).
Per risolvere il problema, si può ottenere facilmente un elegante algoritmo basato
sulla transitività della relazione di raggiungibilità: se v7 è raggiungibile da vf e vk è
raggiungibile da v7, allora vk è raggiungibile da v,. Tale algoritmo è composto dai
seguenti passi:

Passo 1: Per ciascun vertice v, e V, si inizializzi Vi come {v,}, il che equivale ad


affermare che un vertice è raggiungibile da se stesso.
Passo 2: Per ogni v7 e V,, se esiste un arco (v7, vf contenuto in E, allora si
inserisca vk in , cioè u {vj.
Passo 3: Si ripeta il passo 2 esattamente n - 1 volte, dove n è pari a | F|.
438 Informatica teorica

Il precedente algoritmo è stato descritto in modo astratto e non si riferisce a nessun


particolare modello computazionale. Basandosi su questa formulazione astratta, si
possono trarre alcune conclusioni che non dipendono da alcuna specifica
implementazione.
1. L’algoritmo proposto si può facilmente trasformare in un’opportuna MT, o in
un programma per la RAM, o in un programma SMALG.
2. L’algoritmo si ferma sempre perché il passo 2 viene ripetuto esattamente n - 1
volte.
3. L’algoritmo è corretto (ossia, quando si ferma, coincide esattamente con
Tinsieme di raggiungibilità di v(). Infatti è chiaro che, se v, e Kz, allora v, è
raggiungibile da v,. D’altra parte, si noti che alla A-esima iterazione del passo
2, l’algoritmo inserisce in Kz- tutti quei vertici v* per i quali esiste un cammino
di lunghezza h che porta da v,- a vt. Questa affermazione si può
immediatamente verificare per induzione. Infine, se v* è raggiungibile da v,-,
allora risulterà ovviamente raggiungibile attraverso un cammino di lunghezza
minore di n. Abbiamo quindi completamente dimostrato la correttezza.
Cerchiamo ora di analizzare la complessità. Intuitivamente, si osserva che il passo
1 può richiedere n “operazioni elementari”. Inoltre, il passo 2 viene ripetuto n - 1
volte per ciascun Kz. Di conseguenza, la seguente operazione:

V Vj e Vi, if 3 (yj, e E then Vt = Kz u {v*} (*)

viene ripetuta n-(n - 1) volte. Quest’operazione si può definire “elementare”? Si


può effettuare in un tempo limitato? Probabilmente no. Il suo tempo di esecuzione
dipenderà presumibilmente dalla dimensione del grafo.
Arrivati a questo punto, comprendiamo come l’analisi non possa più scendere
in profondità, almeno sulla base della precedente formulazione astratta. Per poter
continuare l’analisi in modo più approfondito bisogna studiare attentamente la
particolare implementazione.
Scegliamo, ad esempio, un'implementazione SMALG. Si supponga che G sia
stato immagazzinato nella memoria di SMALM come una matrice {G[z, j]|
i, j= 1,...,«}. In modo analogo, rappresentiamo gli insiemi di raggiungibilità
mediante un’altra matrice n x n R. Più precisamente, poniamo R[i, j] = 1 se
vj e Vi, 0 altrimenti. Il passo 1 dell’algoritmo consisterà perciò nella
inizializzazione di R[z, z] = 1 per z = l,...,zz. La prima iterazione del passo 2 consiste
nel copiare G[z',j] in R[i, z] per ogni z j. In generale, ogni successiva iterazione
del passo 2 si può implementare ripetendo, per ogni z, la porzione di programma
SMALG mostrata in Figura 9.16.
439
La complessità del calcolo

{ j = 1;
while(j <= n){
if(R[i, j] = 1) {
k = 1;
while(k <= n){
if (G[j,k] == 1) {
R[i,k] = 1;
}
k = k + 1;
}
}
j = i + i;
1
}

Figura 9.17 Una porzione di programma SMALG che implementa il passo 2


dell’algoritmo che calcola l’insieme di raggiungibilità.

E evidente che la porzione di programma in Figura 9.17 effettivamente implementa


l’operazione (*) desiderata. La complessità temporale della porzione di programma
è 0(/z2). Quindi, senza approfondire ulteriormente i dettagli relativi alla codifica, si
può concludere che si può implementare il precedente algoritmo, in un tempo
0(z?4), mediante una rappresentazione matriciale di G.
Si supponga ora che G sia rappresentato mediante due vettori KS,[r] e EP[r],
r = l,...,m. Analogamente, si rappresenti l’insieme di raggiungibilità mediante due
vettori P5[t], PT[t], t = 1,..., mm, supponendo che esista un cammino da v, a v7 se e
solo se esiste un t tale che Pó'f/1] = z, PT[t] =j. Si noti che mm < n2.
Di nuovo, la prima iterazione del passo 2 consiste nel copiare VS in PS e VP
in PT. Ogni iterazione successiva può essere implementata dalle istruzioni di
Figura 9.18.

{ t = 1;
while(t <= mm){
i = SP[t] ;
j = TP[t];
while(h <= mm){
if(SP[h] == j){
k = TP[h];
inserire <v7, vk> in SP e TP
}
h = h + 1;
}
t = t + 1;
}
}
Figura 9.18 Una implementazione alternativa del passo 2.
440 Informatica teorica

{ r = 1;
found = 0;
while(r <= mm){
if(SP[r] == i){
if(TP[r] == k){
found = 1 :
}
}
r = r + 1;
}
if(found == 0){
mm = mm + 1 ;
SP[mm] = i;
TP [mm] = k;
}
}
Figura 9.19 Un’implementazione ulteriormente dettagliata del passo 2.

Il precedente pezzo di programma non è ancora completo, in quanto non specifica


come “inserire (v,, vk) nei vettori PS e PT”. Sono possibili molte scelte. Si può
semplicemente aggiungere un nuovo elemento alla lista (ossia porre mm = mm + 1 ;
PS[mm] = i, PI[mm] = lì). Questa operazione risulterebbe molto semplice, ma
introdurrebbe il rischio di duplicazioni: non verrebbe perciò garantita la condizione
mm < n2. Quindi, prima di compiere l’inserzione, si controlla che (v„ vk) non
appartenga già alla lista, come risulta nel programma di Figura 9.19 (si noti che è
stato necessario impiegare gli interi 0 e 1 per codificare una condizione logica,
poiché in SMALG non sono definiti i booleani).
L’operazione “inserisci (v„ vk) nei vettori PS e PP~ richiede dunque un tempo
©(mm). Di conseguenza, il passo 2 dell’algoritmo è ©(mm3) e l’algoritmo
completo è &(n ■ mm3). Poiché mm < n2, si conclude che la complessità temporale
è, nel caso peggiore, 0(w7).
Analizziamo ora questi risultati più criticamente. Si è implicitamente supposto
che n, il numero di nodi del grafo, sia la misura della “dimensione” dell’ingresso.
Questa ipotesi risulta certamente ragionevole e semplifica i calcoli, anche se è, in
un certo senso, arbitraria. Ad esempio, maggiorando il numero di archi, in termini
di n, come 0(/r), abbiamo ottenuto un risultato eccessivamente pessimistico in
molti casi pratici.
Come si è anticipato nella Sezione 9.1, l’uso pratico dell’analisi di
complessità richiede la definizione di quale sia la “dimensione” dell’ingresso da
correlare al tempo di esecuzione. Ciò risulta molto chiaro nel caso delle MT usate
nei problemi di riconoscimento dei linguaggi, ma diventa più vago quando ci si
sposta verso modelli computazionali di livello più alto. Un’altra definizione per la
“dimensione” dell’ingresso negli algoritmi sui grafi si può esprimere in termini di
una coppia di parametri: il numero di nodi e il numero di archi. Ciò
complicherebbe tuttavia l’analisi formale della complessità. La scelta della
441
La complessità del calcolo

particolare nozione di “dimensione” da impiegare nell’analisi dipende dal


problema che si sta studiando.

ESERCIZI

9.32 Si consideri l’implementazione del problema dell’insieme di raggiungibilità


in cui si usano i vettori VS, VP, PS e PT. Si supponga che l’operazione
“inserisci nei vettori PS e PT’ venga implementata senza controllare
se {vhVk) sia già presente. Si valuti la complessità di questa
implementazione.
9.33 Si cerchi di ottenere un algoritmo più efficiente (e la corrispondente
implementazione in SMALG) per il problema del calcolo degli insiemi di
raggiungibilità.
9.34 Si formuli il problema della raggiungibilità dei grafi come problema di
traduzione di linguaggi. Si schematizzi una MT che lo risolva e si calcoli la
sua complessità temporale come finizione della lunghezza dell’ingresso.
9.35 Si trovi un algoritmo per valutare l’altezza di un albero e si valuti la sua
complessità.
9.36 Si cerchi di trovare un algoritmo di ordinamento che sia più efficiente
dell’ordinamento per inserzione diretta e si valuti la sua complessità
9.37 Un grafo pesato è un grafo in cui a ciascun lato è associato un peso (detto
anche costo) intero e non negativo. Si trovi un algoritmo che determini il
cammino di costo minimo che congiunge due nodi prefissati. Si valuti la
complessità della soluzione trovata.
9.38 Date due stringhe X! e x2, si consideri il problema del calcolo della più
lunga sottostringa contenuta sia in Xi che in x2 (y è una sottostringa di x se e
solo se x = zyw, per qualche z e w). Si risolva il problema mediante una MT
e mediante SMALG. Si confrontino le complessità delle soluzioni ottenute.
9.39 Data una sequenza di interi S = {zb z2, ..., z„} e un intero k, si determini se
esiste una sottosequenza di S la cui somma è k. Questo problema è noto
come “problema dello zaino”. Si risolva il problema dello zaino e si
determini la complessità della soluzione trovata.

9.5 Riassunto e confronto dei risultati ottenuti


I risultati ottenuti relativi allo studio della complessità dipendono dal modello
scelto per la formalizzazione degli algoritmi, nonché dalla particolare codifica del
problema da risolvere. Queste osservazioni sono già state espresse in precedenza,
quando si è affermato che non è possibile estendere la tesi di Church dall’ambito
della decidibilità a quello della complessità. Abbiamo già discusso ampiamente
questi concetti, fornendo numerosi esempi in proposito. Il lettore potrebbe
dubitare, a questo punto, della possibilità di ricavare qualche risultato di portata
generale a proposito della complessità dei problemi. Mostreremo tuttavia nel
442 Informatica teorica

Celle usate per memorizzare lo stato e le posizioni delle


Blocco 0 testine

k celle usate per memorizzare il primo simbolo di ciascun


Blocco 1
nastro

k celle usate per memorizzare il simbolo i- esimo di cia­


Blocco /
scun nastro

Figura 9.20 Simulazione di una MT mediante una RAM.

seguito di questa sezione, come sia possibile fornire un quadro molto generale
aH’intemo del quale si possono valutare e confrontare le complessità dei vari
modelli formali.
Cominciamo ad analizzare eventuali relazioni fra i risultati sulla complessità
ottenuti per i modelli RAM e MT (deterministica). A tale scopo, mostriamo come
sia possibile simulare una MT a k nastri mediante una RAM. Il modo più naturale
(si veda la Figura 9.20) consiste nel considerare la memoria della RAM come
divisa in blocchi, tutti di dimensione k, ad eccezione del blocco 0, che ha
dimensione k + 1, Il blocco 0 memorizza lo stato della MT, e le posizioni delle k
testine dei nastri di memoria. I successivi blocchi di k celle di memoria ciascuno
(blocco 1, blocco 2 ecc.) vengono impiegati per contenere i valori contenuti nelle
posizioni 1, 2 ecc. di ciascuno dei k nastri di memoria della MT. Quindi, il valore
rappresentato nella /-esima cella del /-esimo nastro della MT è contenuto nella
locazione c + k-j + i, dove c è una costante opportuna della memoria della RAM.
Inoltre, per accedere, in un dato istante, al valore presente sotto la testina di lettura
del nastro /«-esimo, è necessario compiere un accesso indiretto attraverso il blocco
0 per trovare il valore della posizione corrente della «z-esima testina. Infine,
l’esecuzione della funzione di transizione 8(7, i, Si,..., sk) e della funzione di uscita
q(<y, i, si,..., sk), richiedono un numero prefissato di accessi alla memoria della
RAM per accedere a q, sb ..., sk, un’operazione di lettura per accedere a z ed un
numero finito di test su questi valori per determinare 8 e q. Ciò conduce
direttamente al seguente teorema:
La complessità del calcolo 443

Enunciato 9.16
Una MT multinastro con complessità temporale TM può essere simulata da una
macchina RAM con complessità temporale TR = 0(Tw), secondo il criterio di costo
uniforme, o TR = &(TM - log TM), secondo il criterio di costo logaritmico.

La simulazione di una RAM mediante una MT è leggermente più complessa da
analizzare. Illustreremo il caso del criterio di costo logaritmico, che risulta molto
più ampiamente applicabile del criterio di costo uniforme, come si è visto nella
Sezione 9.3.1. Ulteriori commenti sul criterio di costo uniforme verranno fomiti in
seguito, dopo il Teorema 9.17.

Teorema 9.17
Sia L un linguaggio accettato da una RAM con complessità temporale TR secondo
il criterio di costo logaritmico. Se il programma RAM non usa le istruzioni MULT
e DIV, allora L può essere riconosciuto, da un’opportuna MT multinastro, in un
tempo limitato da una funzione TM = 0 (T/).

Dimostrazione
Si faccia corrispondere una RAM ad una MT nel modo seguente.
a. I nastri di ingresso delle due macchine sono isomorfi. Infatti, il nastro di
ingresso della RAM contiene una sequenza di interi appartenenti ad un
insieme limitato poiché, per ipotesi, la macchina opera come riconoscitore di
linguaggi. Il corrispondente nastro di ingresso della MT contiene, quindi, una
sequenza di simboli appartenenti ad un insieme di ingresso finito.
b. Poiché si stanno considerando le macchine in qualità di riconoscitori di
linguaggi, si ignorano, per il momento, i nastri di uscita.
c. La MT ha tre nastri (oltre al nastro d’ingresso 17).
c.l. Il primo nastro, Tb codifica i valori contenuti nella memoria RAM
secondo la seguente convenzione:
Sia I = {ij | j = 1 ,...,A} l’insieme degli indici tali che, ad un dato istante,
M[ij] risulti esplicitamente inizializzato, cioè l’insieme degli indici che
identificano celle di memoria per cui vi è stata almento un’operazione di
STORE. Il contenuto del nastro Ti della MT è mostrato in Figura 9.21.
Tale nastro contiene tutti gli elementi in I, ciascuno seguito dal contenuto
della cella di memoria corrispondente. Una cella di memoria viene,
quindi, rappresentata sul nastro Ti (si veda la simulazione descritta qui di
seguito) solo se, in essa, è già stato immagazzinato un valore in qualche
istante precedente. Il simbolo ‘$’ è utilizzato come delimitatore; in
particolare ‘$’ è usato per dividere un indice dal contenuto della cella
corrispondente, mentre “$$” è utilizzato per separare tra loro le diverse
444 Informatica teorica

$ $ ij $ M(ii] $ $ ‘k $ Af[it] $ $ «

Figura 9.21 Nastro che codifica i valori contenuti nella memoria di una RAM.

coppie < ij, . I valori di z) e M\ij\ sono codificati in notazione binaria,


con il vincolo che z) < z)+1. Si noti che, anche se la RAM viene impiegata
come riconoscitore di linguaggi, può accadere che A/[z] contenga un
intero illimitato (ad esempio un valore temporaneo) durante la
computazione.
c.2. Il secondo nastro, T2, memorizza il contenuto dell’accumulatore (A/[0]),
codificato in notazione binaria.
c.3. Il terzo nastro, T3, viene utilizzato come memoria temporanea.
Mostriamo ora come sia possibile simulare, mediante la MT, un campione
significativo di istruzioni RAM. Si lascia la simulazione delle restanti istruzioni al
lettore per esercizio.
1. LOAD h
1.1. Si cerca nel nastro Ti la sequenza $$7z$à/[7z]$$.
La ricerca si può effettuare sequenzialmente, partendo dal limite sinistro
del nastro e confrontando h con z),j = 1,..., k fino a che z) = h per qualche
j. Se la ricerca fallisce, la MT entra in uno stato di errore e si ferma.
1.2. Una volta trovata la sequenza $$/z$A/[/z]$$, si copia il valore di M[h\ sul
secondo nastro.
2. STORE h
2.1. Si cerca nel nastro Ti la sequenza $$/z$A/[/z]$$.
2.1.1. Se la ricerca fallisce, ciò significa che ci si trova in uno dei
seguenti casi:
a. ik < h. In questo caso, si memorizza la sequenza /z$A/[O]$$ al
limite destro di Tb mentre A/[0] (il valore contenuto
nell’accumulatore della RAM) è rappresentato dal contenuto di
T2.
b. Esiste un; < k tale che ij< h < ij+\. In questo caso:
- Si copia la porzione di Ti alla destra di z)$A/[z}]$$ in T3.
- Si memorizza la sequenza /z$A/[0]$$ alla destra di z)$A/[z}]$$.
- Si memorizza il contenuto di T3 in Ti alla destra di /z$A/[0]$$.
2.1.2. Se si è trovato un j tale che z) = h, allora
- Si copia la porzione di I) alla destra di /z$A/[/z]$$ in T3.
- Si memorizza A/[0]$$ alla destra di /z$ in Tb
- Si copia il contenuto di T3 alla destra di /z$A/[0]$$.
3. ADD */z
3.1. Si cerca in Ti la sequenza $$/z$A/[/z].
Se la sequenza non viene trovata, si entra in uno stato di errore e ci si
ferma.
La complessità del calcolo 445

3.2. Quando la sequenza viene trovata, si copia M[h\ in T3.


3.3. Si cerca la sequenza $$A/[/z]$A/[A/[/z]] in Tp Se la sequenza non viene
trovata, si entra in uno stato di errore e ci si ferma.
3.4. Quando la sequenza viene trovata, si effettua la somma A/[0] + A/[A/[/z]],
dove A/[0] (il valore contenuto nell’accumulatore) è rappresentato dal
contenuto di T2.
Eventualmente si usa la porzione temporanea di T3 per effettuare la
somma. Si lascia il risultato in T2.
E abbastanza ovvio che una MT come quella appena descritta è effettivamente in
grado di simulare la RAM. Bisogna mostrare ora che, se la RAM non usa le
istruzioni MULT e DIV e ha una complessità temporale TR, secondo il criterio di
costo logaritmico, allora la MT ha una complessità temporale TM= 0(7}/).
La chiave della dimostrazione è nel seguente lemma.

Lemma 9.18
La lunghezza della porzione non vuota del nastro Ti della MT è limitata da una
funzione 0(7}?).

Dimostrazione
Dapprima si noti che, all’inizio della computazione, sia la memoria della RAM che
i nastri di memoria della MT sono tutti vuoti.
Ciascuna istruzione della RAM modifica, al più, ima cella di memoria, ad esempio
M[h\, e il costo di questa operazione è almeno l(h) + (si veda la Tabella
9.2; può essere maggiore nel caso di un indirizzamento indiretto). Inoltre, la stringa
$$/z$Af[/z]$$ compare nel nastro Ti della MT solo se almeno un valore è stato
esplicitamente immagazzinato nella /z-esima cella di memoria della RAM. La
lunghezza di tale stringa è uguale a l(h) + l(M[h]) + 5 ed è minore di cx-th, dove th è
il tempo richiesto dalla RAM per memorizzare un valore in M[h] e cx è
un’opportuna costante. In conclusione, la lunghezza Li della porzione non vuota
k

del nastro Ti è minore di c • z} , dove z}. è il tempo necessario per memorizzare

un valore in Af[z}] (si veda la Figura 9.21) e dove c è una costante opportuna.
k

Tuttavia c • ti è minore o uguale a TR(n), dove n è la lunghezza della stringa


7=1 ‘
di ingresso, e ciò dimostra il lemma.

Per completare la dimostrazione del Teorema 9.17, si osservi che ogni istruzione
RAM viene simulata da una MT mediante ima sequenza di mosse la cui lunghezza
è limitata da d-Lx, dove L\ è la lunghezza della porzione non vuota del nastro Ti e
446 Informatica teorica

d è una costante opportuna. Ciò è dovuto al fatto che, per simulare un’istruzione
RAM, bisogna, nel caso pessimo, scandire l’intero nastro.
Poiché un’istruzione RAM costa almeno un’unità di tempo, se TR è la
complessità temporale della RAM, il numero di istruzioni RAM necessarie alla
simulazione non è maggiore di TR(n). Quindi, la complessità totale della
simulazione è limitata da una funzione ®(TR)-

Per quale motivo abbiamo limitato la dimostrazione del Teorema 9.17 (l’opposto
del Teorema 9.16) al caso particolare in cui viene applicato alla RAM il criterio del
costo logaritmico? Come si è detto, il criterio di costo logaritmico ha una validità
più ampia, rispetto al criterio di costo uniforme. Inoltre, il teorema vale solo nel
caso di costo logaritmico. Infatti, secondo il criterio di costo uniforme, una RAM
può calcolare numeri come 22 , per ogni n>0, in un tempo ®(rì). Una MT richiede
2” celle solo per memorizzarli e leggerli: la computazione richiede quindi almeno
0(2”) passi. Come conseguenza, non esiste alcuna relazione polinomiale fra le
complessità della RAM e della MT nel caso del criterio di costo uniforme.
Tuttavia, in tal caso è l’adozione del criterio di costo uniforme a risultare
irrealistica.
Per riassumere e generalizzare il precedente ragionamento, si stabiliscono i
seguenti enunciati.

Enunciato 9.19
La complessità della RAM, secondo il criterio di costo logaritmico, e quella della
MT multinastro sono correlate polinomialmente (ossia, se la complessità temporale
di un modello è 0(7), la complessità dell’altro è ®(P°T), dove P è una funzione
polinomiale9).

La dimostrazione è lasciata al lettore per esercizio.
Suggerimento-, Dapprima si generalizzi il Teorema 9.17 al calcolo di una
qualunque funzione intera. Successivamente si progettino i sottoprogrammi per la
MT che implementano le istruzioni MULT e DIV usando i sottoprogrammi di
ADD e SUB. Si verifichi che la loro complessità è correlata polinomialmente al
costo logaritmico di MULT e DIV.
Un risultato simile vale per la complessità spaziale ed è lasciato al lettore per
esercizio.

Esempio 9.19
Si consideri il linguaggio L = {vvclT w, JV e {a, è}* e IL la stringa che differisce
dalla stringa riflessa di w, w®, per al più 3 caratteri}. Tale linguaggio è chiaramente

9 Si ricordi che P°Tindica la composizione di P e di T.


447
La complessità del calcolo

non contestuale. Esso può essere riconosciuto da un automa a pila deterministico


che memorizza sulla pila w e, dopo aver ricevuto in ingresso una c, usa la pila e
una sequenza di quattro stati (ciascuno rappresenta il numero di errori commessi)
per verificare che la parte della stringa in ingresso che segue la c differisca da di
al più tre caratteri. Ogniqualvolta il carattere in ingresso è uguale al 'carattere in
cima alla pila, tale carattere viene rimosso dalla pila e si prosegue considerando il
carattere in ingresso successivo. Quando invece i due caratteri differiscono,
l’automa si sposta nello stato che rappresenta il numero di errori commessi fino a
quel momento più imo (a meno che non si siano già trovate tre differenze). Tale
automa può essere simulato da una MT a 1 nastro che usa il suo nastro come se
fosse una pila. Nel simulare l’automa a pila, la MT compie O(n) mosse, dove n è la
lunghezza della stringa in ingresso.
Proviamo ora a risolvere il problema del riconoscimento di L utilizzando una
RAM e supponendo che la stringa in ingresso non sia già disponibile in memoria.
Un semplice algoritmo che risolve il problema utilizza una struttura dati LIFO
(cioè una pila) ed un contatore che memorizza il numero di “errori” commessi
dalla stringa Wrispetto alla (quest’ultimo è inizializzato a zero). Ogni volta che
un carattere viene letto dall’input, si procede in questa maniera:
1. Se non si è ancora letto il separatore c, si copia il carattere letto in cima alla
pila. ,
2. Se si legge il carattere c, si procede al carattere successivo, registrando in
un’opportuna variabile il passaggio dalla fase di memorizzazione in pila alla
fase di confronto.
3. Dopo aver letto il separatore c, allora, per ogni ulteriore carattere letto:
a. se il carattere corrisponde a quello in cima alla pila, si elimina
quest’ultimo dalla pila e si procede alla lettura del carattere successivo.
b. altrimenti, si incrementa il contatore degli errori e
i. se il contatore errori è maggiore di 3, la funzione restituisce il valore
0 e si termina,
ii. altrimenti, si procede con la lettura del carattere successivo,
eliminando comunque il carattere in cima alla pila.
4. Se si termina la lettura della stringa in ingresso con la pila vuota e il contatore
minore o uguale a 3, la funzione termina e restituisce il valore 1.
Chiamando, nuovamente, n la lunghezza della stringa in ingresso, la complessità
temporale, valutata a costo uniforme, risulta 0(«), mentre, valutata a costo
logaritmico, è 0(« log(/?)). Questo è dovuto al fatto che i passi 1. e 3. vengono
eseguiti n volte, ma ognuno di essi costa in proporzione aH’indirizzo dell’ultima
cella utilizzata, che corrisponde alla cima della pila, e quindi log(n).
Si noti che nella sua semplicità, questo algoritmo non è migliorabile in termini
di complessità asintotica; infatti è evidente che per operare il riconoscimento la
RAM deve memorizzare la prima metà della stringa in ingresso e quindi deve
utilizzare necessariamente almeno 0(«) celle, e ogni operazione di STORE nella
448 Informatica teorica

cella di indirizzo i costa log(z). Questo esempio dimostra quindi che non sempre il
meccanismo di accesso diretto tipico della RAM risulta superiore, in termini di
complessità, al meccanismo di accesso sequenziale proprio della MT.

Correliamo ora le funzioni di complessità del modello SMALM con quelle dei
modelli RAM e MT. Come primo passo mostriamo come sia possibile tradurre un
generico programma SMALG in un programma RAM equivalente.
Sia Ps un programma SMALG. Esso può essere tradotto in un programma
RAM equivalente PR mediante la seguente procedura.
Come prima cosa, si definisce una biiezione fra la memoria della SMALM e
la memoria della RAM. Per motivi di semplicità, si supponga di conoscere a priori
che nessun indice di nessun vettore eccederà mai un valore N nel corso
dell’esecuzione. Successivamente si associa ciascuna variabile semplice di Ps con
una cella di memoria della RAM e ciascuna variabile vettore con una sequenza di
N celle di memoria. L’associazione viene fatta lasciando libere le celle Affi],
M[2] e M[3], che vengono utilizzate come memoria temporanea. Si indicherà con
i l’indirizzo della cella corrispondente alla variabile semplice i di SMALG. Si
indicherà con a l’indirizzo della prima cella della sequenza associata al vettore a.
Quindi, se due vettori a e b vengono immagazzinati consecutivamente, allora
b = a + N . Per ogni variabile x di Ps, x è un intero noto a priori.
Diamo allora un insieme di regole per costruire un programma RAM PR
equivalente ad un dato programma SMALG Ps. Sia X un non terminale della
X X
grammatica SMALG e sia Ps una porzione di programma tale che Ps .
La corrispondente porzione di PR si ottiene applicando le seguenti regole.
1. Valutazione delle espressioni.
• *
1.1. Se (Expr) =^> (SimpleVar) => x, dove x è un identificatore di variabile
semplice, allora si genera la sequenza
LOAD x
STORE 1
* *
1.2. Se (Expr) =^> (Const) =^> k, dove k è una costante, allora si genera la
sequenza
LOAD=k
STORE 1
» *
1.3. Se (Expr) =^> (ArrayVarld)[(Const)] =^> a\k\, dove a è un array
identificatore e k una costante, allora si genera la seguente sequenza
LOAD h
La complessità del calcolo 449

STORE 1
dove h è l’intero a +k-\.

1.4. Se (Expr) (ArrayVaridi [\SimpieVar)]op(ArrayVarld) [(Const)]


a[i] op b[k], dove op è un operatore aritmetico *, / o %), a e b sono
due identificatori di vettori, z è un identificatore di variabile semplice, k
ima costante, allora (supponendo ad esempio op = + e c = b + k - 1 ) si
genera la seguente sequenza
LOAD=à
ADD i
SUB= 1
STORE 1
LOAD=c
STORE2
LOAD*1
ADD* 2
STORE 1
La traduzione associata a tutte le altre possibili derivazioni di (Expr) è lasciata
al lettore per esercizio. Si noti che il solo requisito è che il valore
dell’espressione venga memorizzato in à/[1] alla fine dell’esecuzione della
sequenza generata.
2. Traduzione delle espressioni di assegnamento.
* * ■
Se (AssStat) => (SimpleVar) = (Expr) => x := (Expr) allora si genera la
sequenza di istruzioni necessaria a valutare l’espressione, secondo la regola 1,
seguita da
LOAD 1
STORE x

Di nuovo, la traduzione degli altri casi per (AssStat) è lasciata al lettore per
esercizio.
3. Traduzione di istruzioni condizionali.
Se (CondStat) if f(Cond^) {(StatList^2}else{(StatList/}, allora si
genera la sequenza
P (Cond)
JZERO F
P \cond)
JUMP C
F: P 2(Cond)

C:
dove
450 Informatica teorica

- P (Cond), P \cond), P 2(Cond> denotano, rispettivamente, le traduzioni della


condizione, della lista di istruzioni relative alla parte del ramo then e
della lista di istruzioni relative alla parte del ramo else.
- F, C sono etichette non usate in alcuna altra parte nel programma RAM;
C rappresenta l’etichetta della prima istruzione che segue la sequenza di
istruzioni appena presentata.
La trattazione dell’istruzione if senza una parte else si può descrivere in
modo analogo.
4. Traduzione dell’istruzione while.
Sia (WhileStat) while ({Cond}) {(StatList/}
Si produce allora la sequenza:
CICLO: P Conci,
JZERO USCITA
P (StatList)
JUMP CICLO
USCITA:
dove le notazioni hanno il significato già visto nel caso 3.
5. La traduzione delle istruzioni restanti (istruzioni di ingresso/uscita, lista di
istruzioni ecc.) è lasciata al lettore come semplice esercizio.

ESERCIZI
9. 40 Si modifichi la precedente costruzione eliminando l’ipotesi che la
dimensione dei vettori sia limitata staticamente.
Suggerimento: Se un programma SMALG usa j vettori, allora si usano
blocchi di j celle consecutive della macchina RAM per memorizzare gli
elementi dei vettori che hanno lo stesso indice.
9. 41 Si traducano i programmi SMALG di Figura 9.13 nei corrispondenti
programmi RAM.
9. 42* (La soluzione di questo esercizio richiede qualche precedente esperienza
sulle tecniche di compilazione). Si trasformino le precedenti regole per la
traduzione di Ps in PR in un programma per calcolatore.

Un breve esame delle regole di complessità ricavate per SMALG nella Sezione 9.4
e delle precedenti regole di traduzione chiarirà al lettore le seguenti affermazioni.

Enunciato 9.20
Se un problema può essere risolto da un programma SMALG con complessità
temporale T$, allora esiste un programma RAM che lo risolve con una complessità
Tr = ®(TS).
451
La complessità del calcolo

L’Enunciato 9.20 vale chiaramente per entrambi i criteri di costo. Il risultato


opposto (ossia una simulazione dei programmi RAM mediante una SMALM)
sarebbe immediatamente ricavabile solo nel caso in cui SMALG fosse dotato di
un’istruzione a basso livello di tipo go-to. Senza una simile istruzione, che è stata
evitata in onore ai principi della programmazione strutturata, si può ottenere il
risultato desiderato mediante la costruzione proposta da Bòhm e Jacopini, in cui si
dimostra come la sequenza, la selezione (if - else) e l’iterazione (while) sono
sufficienti per codificare il flusso di controllo di ogni programma.
A questo punto possiamo concludere e generalizzare la nostra discussione con
il seguente enunciato.

Enunciato 9.21
Tutti i modelli computazionali, adottando ipotesi ragionevoli per le misure di
costo, hanno funzioni di complessità spaziale e temporale correlate
polinomialmente.

In altre parole, ogni problema risolubile da un modello computazionale con
complessità temporale T può essere risolto da ogni altro modello computazionale
con una complessità temporale ®(P°T), dove P è una funzione polinomiale. Questa
affermazione vale, ovviamente, solo se vengono impiegate ipotesi di costo
“ragionevoli”: come infatti si è osservato, il criterio di costo costante non è
ragionevole per il caso generale, in quanto suppone che sia possibile accedere ad
una quantità di informazione illimitata in un tempo prefissato.
In un certo senso, l’Enunciato 9.21 è il corrispettivo della tesi di Church per
quanto riguarda la complessità computazionale. Anche se non ci si può riferire ad
un unico modello computazionale per ottenere misure di complessità di validità
assoluta, si può perlomeno stabilire se un problema ha complessità polinomiale,
esponenziale o addirittura peggiore. Per convenzione, inoltre, si stabilisce che i
problemi aventi complessità maggiore della polinomiale siano, in pratica,
intrattabili. Come sempre, questa osservazione va considerata con una certa
cautela. Una complessità polinomiale di tipo nk, con k molto grande, si può
diffìcilmente considerare trattabile, nelle applicazioni pratiche. D’altra parte, vi
sono casi in cui, entro un intervallo di valori di n praticamente accettabili, Tip) =
k-n2 può risultare migliore di T-Jn) = h-n se h » k. Si ritornerà su questo punto al
termine della Sezione 9.9.

Esempio 9.20
Si consideri la versione deterministica gli automi a pila doppia, A2P, (si faccia
riferimento all’Esercizio 4.77), che sono come i tradizionali automi a pila
deterministici, ma sono dotati di due pile invece che di una. Una singola mossa di
tali automi dipende dal valore contenuto sulla cima delle due pile, oltre che dallo
stato corrente e dal simbolo in ingresso. L’automa, inoltre, opera sulle due pile
452 Informatica teorica

contemporaneamente. Come visto nell’introduzione del Capitolo 8, tali automi


hanno la stessa potenza delle MT. È quindi possibile simulare un A2P con una MT
e viceversa. Analizziamo come varia la complessità in conseguenza di tale
operazione, limitandoci a considerare le MT a nastro singolo.
Consideriamo un A2P deterministico A che opera con complessità temporale
T(m), una MT a nastro singolo M per simularne il comportamento deve
memorizzare su un singolo nastro il contenuto delle due pile, facendo crescere una
pila verso sinistra e una verso destra. Per simulare una singola mossa di A, M è
costretta ad eseguire un numero di mosse proporzionale alla lunghezza del nastro
(T(m)). Quindi Mha complessità temporale ©(/^(m)).
Se, invece, consideriamo una MT a nastro singolo M con complessità
temporale T(n), un A2P A che la simula, copia inizialmente la stringa di ingresso
prima nella pila di sinistra e poi la travasa in quella di destra, in modo da
mantenere Tordine dei simboli. Poi, A simula Mmantenendo nella pila a sinistra la
porzione di nastro a sinistra della testina e nella pila di destra la porzione di nastro
a destra della testina. Una configurazione <x, q, iy> di M corrisponde, quindi, alla
configurazione <q, s, xRZ0, iyZ0> di A. Ogni singola mossa di M viene simulata
con un numero costante di mosse in A. La classe di complessità rimane perciò la
stessa.

Prima di chiudere questa sezione, diamo alcuni avvertimenti al lettore per facilitare
la comprensione del significato profondo dell’Enunciato 9.21.
- Si faccia attenzione al parametro di complessità impiegato. Nell’Esercizio
9.25 si è ricavato un programma RAM che calcola nn in un tempo 0(w2-log ri).
Se si traduce il programma RAM in un programma equivalente per una MT,
che riceve come ingresso una codifica binaria di n (ad esempio ri ), si ottiene
una complessità temporale esponenziale. Tuttavia, ciò non contraddice il
precedente enunciato, poiché sono stati implicitamente cambiati i parametri di
misura della complessità. In quest’ultimo caso, infatti, la complessità
temporale esponenziale considera, come dimensione dei dati d’ingresso, la
lunghezza della stringa di ingresso, seguendo la consuetudine delle MT. Così
facendo, però, la lunghezza della stringa di ingresso risulta | ri | (ossia /(«)) e
non il valore originale n.
Analogamente, dopo aver esaminato l’Esempio 9.8, che ricava una
complessità 0(m ■ log n) per il problema della determinazione se n sia, o meno,
un numero primo, il lettore potrebbe scoprire consultando la letteratura
scientifica che solo recentemente si è dimostrato che è possibile risolvere
questo problema in tempo polinomiale: evidentemente il raggiungimento di
questo risultato è basato su algoritmi ben più sofisticati di quello deH’Esempio
9.8. La contraddizione con l’Enunciato 9.21 è però solo apparente. Basta
usare, infatti, lo stesso avvertimento di prima, ricordando che la dimensione
della stringa che codifica n è 0(log n) (a meno di impiegare una codifica
453
La complessità del calcolo

unaria per ri) per constatare che, rispetto a alla lunghezza della codifica del
dato in ingresso, la complessità dell’algoritmo risulta esponenziale.
Se si eliminano le operazioni MULT e DIV dal repertorio delle istruzioni
RAM, allora il Teorema 9.17 vale anche secondo il criterio di costo uniforme,
se si sostituisce ®(Tr) al posto di ®(Tr). Ciò si può dimostrare seguendo lo
stesso procedimento impiegato per il Teorema 9.17 e osservando che il
Lemma 9.18 rimane valido avendo cura di sostituire ®(7«2) al posto di ®(TR).
Questa osservazione conferma ulteriormente che MULT e DIV difficilmente
si possono considerare come operazioni elementari, almeno se si cercano
risultati di validità matematica generale.
I risultati precedenti (in particolare il Teorema 9.17 e le sue conseguenze)
sono stati ricavati considerando computazioni complete (ossia computazioni
che cominciano da una configurazione iniziale della macchina di calcolo in
cui la memoria è vuota). Ovviamente, per tali computazioni, T è almeno 0(«)
poiché, a parte i casi banali, bisogna leggere l’intera stringa di ingresso.
Quest’ipotesi, peraltro ragionevole, esclude tuttavia alcuni casi interessanti
illustrati nel seguente esempio.
La porzione di programma SMALG, presentata in Figura 9.22, risolve il
problema dell’appartenenza ad un vettore ordinato di lunghezza n (si veda
l’Esempio 9.2). Si usano i e j per delimitare la porzione x, del vettore,
attualmente in corso di esame. Il risultato della ricerca è z = 1 se esiste un
elemento del vettore uguale a y, altrimenti è z = 0. La complessità del
programma SMALG, secondo il criterio di costo uniforme, si può ricavare
facilmente: essa coincide, ovviamente, con il risultato ricavato informalmente
nel caso del programma Java dell’Esempio 9.2 (è 0 (log «)). Si analizzi ora la
complessità del programma precedente usando il criterio di costo logaritmico
e sotto l’ipotesi che, per ogni i, /(%[?]) < m, l(y) < m, per qualche costante
intera m. Si noti che l’esecuzione di k / 2 si può considerare come
un’operazione elementare, in quanto la sottostante SMALM può eseguirla
mediante uno scorrimento verso destra di una posizione della stringa di bit che
rappresenta k. Il tempo richiesto per eseguire il corpo del ciclo è ©(/(«)),
poiché i,j e k sono contenuti in mentre m è una costante. Il ciclo
viene eseguito al più /(«) volte, quindi la complessità temporale, secondo il
criterio di costo logaritmico, risulta pari a 0(log2(«)).
454 Informatica teorica

{ i= 1; j = n;
while(j <= j){
k = k + j;
k = k / 2;
if(y == x[k]){
i = k;
j = k - 1;
}
else{
if(y < x[k]){
j = k - 1;
}
else{
i = k + 1;
}
}
}
if(x == a[k]){ .
z = 1;
}
else{
z = 0;
}
}
Figura 9.22 Ricerca binaria in SMALG.

Se si considera una MT che risolve il medesimo problema, si può facilmente


dimostrare, in base alle osservazioni compiute alla fine dell’Esempio 9.5 e
dell’Esercizio 9.8, che il tempo necessario per accedere ad un generico elemento
del vettore è 0(w), non ®(log n). Non vi è quindi alcuna possibilità di far
funzionare la MT in un tempo 0(P(log2/?)), dove P è una funzione polinomiale.
Fortunatamente, la contraddizione precedente scompare quando le porzioni di
programma vengono inserite in programmi completi, che hanno complessità
temporale almeno lineare.

9.6 Concetti avanzati relativi alla complessità: gerarchie, riducibilità e


completezza
Nella Sezione 8.10 abbiamo introdotto la nozione di riducibilità di un problema per
dimostrare molti risultati circa la indecidibilità. Si è anche riusciti a classificare i
problemi in base ad una misura della loro difficoltà fondata sulla nozione di grado
di irrisolubilità. Gran parte di questi metodi si può applicare anche alla
classificazione dei problemi in termini di complessità della loro soluzione. In
questa sezione, vengono compiuti alcuni passi preliminari in questa direzione.
Questo è un tipico argomento che si colloca sul confine fra la teoria di base e
quella avanzata; infatti, la maggior parte degli argomenti trattati in questa e nelle
prossime sezioni, richiedono l’uso di strumenti matematici complessi, ma sono
455
La complessità del calcolo

argomenti di fondamentale importanza per la formazione di un informatico e


meritano quindi, se pur in modo non approfondito, di essere trattati in questo testo.
Si osservi inoltre che gli argomenti che verranno trattati sono alla base di un campo
di ricerca attivo e di notevole interesse, in cui, anche se sono già stati raggiunti
molti risultati rilevanti, vi sono ancora diversi problemi aperti ed importanti.
In seguito, presenteremo un campione di tali risultati, senza alcuna ambizione
di fornire una copertura ampia e completa dell’argomento.
Si restringerà l’attenzione ai problemi risolvibili, formulati come linguaggi
ricorsivi, poiché la complessità diventa una nozione piuttosto aleatoria se si
consentono computazioni senza fine. Inoltre, ci si baserà sul modello
computazionale delle MT multinastro usate come riconoscitori di linguaggi, che si
sono dimostrate le più astratte e quindi le più adatte per ricavare osservazioni di
carattere generale.
Grazie all’Enunciato 9.21, che ci assicura che, sotto ragionevoli ipotesi di
costo, tutti i modelli computazionali hanno funzioni di complessità (spaziali e
temporali) correlate polinomialmente, gran parte dei risultati fin qui presentati si
possono rendere indipendenti dal modello computazionale usato. Tuttavia, le
definizioni e gli enunciati della Sezione 9.2.1 fanno sorgere spontaneamente alcune
domande, quali, ad esempio, le seguenti:
- Il teorema dell’Accelerazione Lineare si può spingere oltre?
- Esiste una funzione di complessità T tale che DTIME(T) copra tutti i linguaggi
ricorsivi?
- Se T](n) < T2(«), per ogni n> n , è vera la relazione
DTIME(Ti) cz DTIMEfT))?
- Sapendo dal Teorema 4.23 che il non determinismo non cambia la potenza
delle MT, possiamo dire qualcosa di analogo in termini di complessità? In
altre parole, per ogni data funzione T, o per ogni famiglia {?)} sono vere
le relazioni DTIME(T) = NTIME(T), DTIME(. Z ) = NTIMEf Z )?
Ovviamente, DTIME(. A) significa {DTIME (Ti )} .

Tutte queste domande rivestono intuitivamente una grande importanza per le


possibili applicazioni pratiche, come del resto le domande corrispondenti relative
alla complessità spaziale. Cominciamo a rispondere alla seconda di esse, cioè se
esiste una complessità T, tale che DTIME(T) copra tutti i linguaggi ricorsivi,
mediante un teorema che si può far corrispondere, nell’ambito della teoria della
complessità, al Teorema 8.6 (indecidibilità del problema dell’arresto per le MT).

Teorema 8.22
Per ogni funzione totale e computabile T, esiste un linguaggio ricorsivo L che non
è in DTIME(T).
456 Informatica teorica

Dimostrazione
Anche la dimostrazione di questo teorema assomiglia alla dimostrazione del
Teorema 8.6: è una classica dimostrazione diagonale.
Dapprima si definisca un’enumerazione effettiva di MT multinastro, ad
esempio {Mi, i = 1,2,...}. Ciò si può ovviamente ottenere estendendo la tecnica di
enumerazione proposta nella Sezione 8.4.1. Analogamente, si consideri
*
un’enumerazione effettiva delle stringhe di VT .
Si definisca ora L = {x, | M, non accetta xf entro T(|x,-|) mosse}. Chiaramente L
è ricorsivo. Infatti, per stabilire se x, e L, è sufficiente simulare tutte le possibili
sequenze di computazione di M, la cui lunghezza non sia maggiore di T(|x;-|); tali
computazioni, fra l’altro, risultano finite.
Si supponga ora, per assurdo, che L sia in DTIME(T). Esisterà allora un z0 tale
che L = ), dove M,- ha una complessità temporale limitata da T. Ora, se
Xi e L , allora M accetta x - entro T(n^ passi, dove n0 = | x,- |. Quindi, per la
lo lo lo lo
definizione di L, x,- non è in L\ una contraddizione. D’altra parte, se x, g L,
allora M; non accetta x,- . Quindi, per definizione di L, Xj è in L: di nuovo una
lo lo lo
contraddizione. Entrambe le ipotesi conducono a contraddizioni, quindi la
complessità di M .■ non può essere limitata da T.
lo

I seguenti enunciati, anche se proposti come esercizi non troppo impegnativi al
lettore, stabiliscono alcune fondamentali proprietà nell’ambito della teoria della
complessità computazionale.
ESERCIZI
9.43 Si dimostri che il Teorema 3.22 sussiste anche per NTIME, DSPACE e
NSPACE.
9.44 Si dimostri che, per ogni/ DTIME(/) c DSPACE(/).
9.45 Si dimostri che, per ogni L in DSPACE(/), con fin)> log n, oppure in
NTIME(/), esiste una costante c tale che L sia in DTIMEfc^"/

Il Teorema 9.22 permette quindi di rispondere negativamente alla nostra domanda


e, inoltre permette di dedurre che la complessità temporale deterministica (così
come la complessità temporale non deterministica e le complessità spaziali,
deterministica e non deterministica) forniscono una gerarchia infinita di linguaggi.
Si consideri, infatti, una qualunque funzione T. Per il Teorema 9.22 esiste un
linguaggio ricorsivo L £ DTIME(T). Sia 7' la funzione max{T(n), TL(n)}, per ogni
n. Risulterà allora, ovviamente, che DTIME(T) c DTIME(T'). I linguaggi
ricorsivi si possono quindi ordinare in una gerarchia infinita in base alla loro
457
La complessità del calcolo

complessità spaziale o temporale, usando dispositivi di riconoscimento sia


deterministici che non deterministici. Con uno spirito analogo, nella Sezione 8.10
erano stati ordinati gli insiemi ricorsivamente enumerabili in base al loro grado di
irrisolubilità.
La nozione di gerarchia è uno strumento potente per la classificazione dei
problemi in base a qualche parametro di difficoltà. Ecco un altro esempio di
gerarchia.

Teorema 9.23
I linguaggi regolari sono una gerarchia infinita rispetto al numero di stati dei loro
AF riconoscitori. Più precisamente, per ogni n, esiste un linguaggio accettato da un
AF con n stati, ma da nessun AF con meno di n stati.

La dimostrazione del Teorema 9.23 è lasciata come esercizio per il lettore.
Prendiamo ora brevemente in considerazione la terza domanda sollevata in
precedenza, ossia se Tx(n) < T2(n), per ogni n>n, implichi la relazione
DTIME(Ti) c DTIME(T2). Si noti dapprima che, se per qualche c > 0,
T2(n) < c-T\(n), allora, per il Teorema 9.12, DTIME(Ti) = DTIMEfTV). Non ci
addentriamo ulteriormente in questo argomento. Informiamo soltanto il lettore
interessato che, per qualche T, può accadere che DTIME(T2) = DTIME(7), ma, “in
generale”, DTIME(7) c DTIME(TTog T). Per ulteriori informazioni su questo
argomento ci si può rifare alla bibliografìa consigliata alla fine del capitolo.
Rivolgiamoci, infine, all’ultima domanda sollevata all’inizio di questa
sezione, cioè chiediamoci come il non determinismo influenza la complessità. Tale
domanda verrà affrontata in una cornice specifica ma fondamentale. Si è affermato
che la complessità temporale polinomiale è stata convenzionalmente, ma
realisticamente, definita come il confine fra i problemi trattabili e quelli intrattabili.
Si definiscono ora:
00

<P= U DTIME (n)


i=l
e, analogamente,
00

NP = U NUME (n)
i=L
cioè la classe dei problemi risolvibili in tempo polinomiale da una MT
deterministica e la classe dei problemi risolvibili in tempo polinomiale da una MT
non deterministica, rispettivamente.
Ci si chiederà, a questo punto, se <P = 5V1P, cioè se il non determinismo non
influenza la complessità, almeno per la classe dei problemi considerati trattabili.
Questa domanda ha conseguenze enormi per la teoria della risoluzione di problemi.
Infatti, un gran numero di problemi di importanza pratica sono contenuti in 5MP.
458 Informatica teorica

quindi, 5V?Pse fosse uguale a <P, molti problemi interessanti risulterebbero trattabili.
Se ne fornisce un campione qui di seguito.

Soddisfacibilità delle formule proposizionali (SAT)


Sia W(Ai, A2, —, Am) una formula proposizionale che fa uso delle variabili logiche
At, A2, ..., Am. Esiste un assegnamento di valori logici {T, F} a Ai, A2, —, Am in
modo tale che W(Ai, A2,..., Am) sia vera? In altri termini, Wè soddisfacibile?
Per mostrare che il problema precedente appartiene a 5V<P, lo si formula
dapprima come problema di riconoscimento di un linguaggio. Il procedimento
naturale consiste nel codificare le formule proposizionali in opportune stringhe, in
modo tale da definire un linguaggio composto unicamente dalle stringhe che
codificano formule soddisfacibili.
Chiaramente, si può utilizzare un alfabeto contenente tutti i connettivi logici, e
precisamente -i, v, a, =>,<=> e le parentesi (, ). Le formule proposizionali possono
invece contenere un numero arbitrario di simboli diversi di variabili logiche.
Bisogna quindi risolvere il problema della rappresentazione di un insieme infinito
di simboli possibili mediante un alfabeto finito. La soluzione naturale consiste nel
rappresentare la variabile A, codificando (ad esempio in modo binario) il numero
naturale z. Serviranno quindi /(z) bit per rappresentare A,. Poiché una fbf W,
contenente n occorrenze delle variabili logiche (il numero di occorrenze di
variabili può essere molto maggiore del numero di variabili diverse), può contenere
un numero ®(n) di connettivi logici e di parentesi. Si conclude che la lunghezza
della stringa x che rappresenta W è ®(n-log n). Si può ora affermare il seguente
teorema.

Teorema 9.24

Il linguaggio L costituito dalle stringhe che rappresentano le fbf del è in 5V?P.


In breve, si dice che SAT è in <N<P.

Dimostrazione
Sia Wuna formula proposizionale contenente n occorrenze di m variabili logiche.
Quindi, m < n.
Si generi non deterministicamente una sequenza di m valori logici. Questa
operazione può essere compiuta in un tempo ®(zw) < ®(n) da una MT non
deterministica. Si sostituisca poi ogni occorrenza di A, con il suo valore. Ciò può
avvenire in un tempo ®(n-log rì), poiché bisogna leggere la stringa originale. Si
può tuttavia supporre che la stringa risultante, contenente solo i simboli T, F, -i,...,
), abbia lunghezza ®(n).
459
La complessità del calcolo

A questo punto si controlla la verità della formula risultante attraversandola diverse


volte ed effettuando le riduzioni (->F) => T, (—>T) => F, (F a T) => F, ecc.
Ovviamente una simile verifica si può effettuare in un tempo polinomiale rispetto a
n (è addirittura possibile progettare un algoritmo 0(n)).

Problema dei Cicli Hamiltoniani (HC)


Dato un grafo G, esiste un cammino che visita tutti i vertici del grafo esattamente
una volta, ritornando alla fine al vertice di partenza?
Come esercizio, il lettore è invitato a codificare HC come problema di
riconoscimento di linguaggio e a dimostrare che è in .

Problema del Commesso Viaggiatore (TS)

Sia Gc un grafo in cui, ad ogni arco, è associato un costo c e N. E possibile trovare


un cammino Hamiltoniano, se esiste, per il quale la somma dei costi associati a
ciascun arco del ciclo sia minima?
Formulare questo problema come problema di riconoscimento di linguaggio è più
difficile rispetto a quanto visto precedentemente. Tuttavia, esso risulta
naturalmente formulabile come funzione da calcolare. Bisogna quindi affrontare il
problema del passaggio dalla traduzione alle decisioni: un compito non facile
quando si studia la complessità, come osservato nella Sezione 9.1.
Si consideri la seguente versione decisionale del problema del commesso
viaggiatore, strettamente correlato al precedente. Sia Gc definito come in
precedenza. Esiste un ciclo Hamiltoniano il cui costo totale non sia maggiore di un
limite B?
Chiaramente le due versioni sono riducibili l’una all’altra. Inoltre, finché si
discute di complessità, si può affermare che il problema originario non è
certamente meno costoso del problema nuovo, poiché, una volta trovata una
soluzione per il problema originale, la soluzione del secondo è immediatamente
ricavabile paragonando il costo del ciclo ottimo con B. L’opposto, però, non è
garantito. In conclusione, si può affrontare la versione decisionale del problema
tenendo presente che il problema di ottimizzazione è, almeno, altrettanto difficile.
Nel seguito ci riferiremo al problema del commesso viaggiatore, intendendo
implicitamente la versione decisionale.
460 Informatica teorica

Problema della “ctìque” (CP)


Una “clique” di un grafo non orientato è un sottografo completo, ossia un
sottoinsieme dei suoi nodi tale che, per ogni coppia di vertici del sottografo, esiste
un arco che li congiunge. Il numero di nodi della clique è la sua dimensione. Una
clique si dice massima se non esistono altre clique aventi dimensioni maggiori. Il
problema che si vuole affrontare è quello della determinazione di una clique
massima10 in un grafo non orientato G.
Anche questo problema è formulato come problema di ottimizzazione. Si consideri
nuovamente il problema decisionale corrispondente: per ogni grafo non orientato
G, G possiede una clique di dimensione maggiore o uguale a K?
Si noti che, in questo caso, il problema di ottimizzazione e la sua versione
decisionale sono riducibili l’uno all’altra con complessità linearmente correlate.
Infatti, la risoluzione del problema di decisione sulla base del problema di
ottimizzazione richiede un banale confronto, mentre il problema di ottimizzazione
ha una soluzione che è ottenibile risolvendo la versione di decisione per k = 1, ...,
n. Quindi, se il primo può essere risolto in tempo €)(/), il secondo può essere risolto
in ©(/), mentre, se il secondo può essere risolto in ©(/), il primo può essere risolto
in ®(n-f).
Possiamo quindi affermare che CP è in e chiederci se sia anche in <P,
senza specificare a quale versione ci si riferisca.

Osserviamo che, per questi e moltissimi altri problemi, la dimostrazione che essi si
trovino in è abbastanza facilmente ottenuta sfruttando “la parte
nondeterministica della macchina astratta” per ipotizzare una possibile soluzione e,
successivamente, verificando in maniera deterministica, ma con complessità
polinomiale, se effettivamente la “candidata” sia una soluzione del problema. La
difficoltà nasce dal fatto che in generale l’insieme delle possibili soluzioni
generabili nondeterministicamente ha cardinalità esponenziale rispetto alle
dimensioni del problema.
Al lettore non sarà sfuggita, inoltre, la somiglianza tra questo approccio alla
ricerca della soluzione di un problema e quello adottato nel Capitolo 8 per
dimostrare la semidecidibilità di un problema; si tratta in entrambi i casi di
approcci “brute force”', si enumerano tutte le soluzioni ipotizzabili e si verifica se
tra esse ne esiste una che risolva effettivamente il problema; la differenza sta nel
fatto che nel caso della semidecidibilità le “candidate alla soluzione” sono infinite
e quindi il processo potrebbe non terminare se tra esse la soluzione reale non c’è,
mentre in questo caso le candidate sono finite, ancorché in numero potenzialmente
esponenziale rispetto alle dimensioni del problema.

10 Si noti che una clique massima può non essere unica.


461
La complessità del calcolo

Se dunque <P fosse uguale a WL tutti questi problemi e molti altri si potrebbero
classificare come “trattabili”, rafforzando quindi la speranza di trovare, per essi,
algoritmi veramente efficienti. Sfortunatamente, anche se in letteratura gli sforzi di
dimostrazione in entrambe le direzioni si susseguono (il lettore interessato può fare
riferimento alle letture consigliate nelle note bibliografiche), la questione è ancora
aperta, ma il fatto che tutti i tentativi di trovare soluzioni polinomiali a tali
problemi siano finora falliti, suggerisce una risposta negativa alla domanda
generale. Inoltre, l’Esercizio 9.45 suggerisce, ma non dimostra, che il passaggio da
computazioni non deterministiche a deterministiche può richiedere, in generale, un
numero esponenziale di “tentativi”. L’importanza della questione viene
ulteriormente enfatizzata quando le si applica il concetto di riducibilità di un
problema, presentato qui di seguito.

Definizione 9.12
Un linguaggio Li è riducibile in tempo polinomiale ad un linguaggio L2 se e solo se
esiste una MT deterministica, di complessità limitata da un tempo polinomiale e
dotata di nastro di uscita, che, per ogni x, produce un’uscita r(x) tale che r(x) e L2
se, e solo se, x e Zi.

ESERCIZIO
9.46 Si mostri che la riducibilità in tempo polinomiale è una relazione transitiva.
Avvertimento: Se Li è riducibile a L2 in pfrì) e L2è riducibile a L3 in p2(n),
allora Li non è riducibile a L3 in pfri) + p2(n)l

Intuitivamente, la definizione precedente afferma che un problema Px è riducibile


in tempo polinomiale a un problema P2 se esiste un algoritmo, eseguibile in tempo
polinomiale, che sia in grado di calcolare la soluzione di ogni istanza data di P\
(che stabilisce se x e Li) in termini della soluzione di un’opportuna istanza di P2
(che stabilisce se r(x) g L2). La nozione di riducibilità di un problema si può
arricchire con la nozione di completezza con le stesse modalità viste, nella Sezione
8.10, per gli insiemi ricorsivamente enumerabili.

Definizione 9.13
Sia L una classe di linguaggi. Un linguaggio L, non necessariamente in L, si dice
L-difficile rispetto alle riduzioni in tempo polinomiale se, e solo se, per ogni
L' e L, L' è riducibile in tempo polinomiale a L. Un linguaggio L è completo in L,
o L-completo (rispetto alle riduzioni in tempo polinomiale) se e solo se è in L ed è
L-difficile.
462 Informatica teorica

Il lettore è invitato a confrontare la presente definizione di completezza con la


Definizione 8.8. La nozione di completezza, nel nostro caso la completezza in
tempo polinomiale, diventa estremamente importante quando viene applicata alla
classe %<P. Infatti, sarebbe sufficiente trovare un algoritmo in tempo polinomiale
per un solo problema W-completo per poter concludere che P = NP. Viceversa,
una dimostrazione della non risolvibilità in tempo polinomiale di uno di questi
problemi implicherebbe la non risolvibilità in tempo polinomiale di tutti gli altri.
L’aspetto interessante e stimolante è che, a dispetto del fatto che la questione <P =
5VP sia ancora aperta, è stata identificata un’incredibile quantità di problemi 5V<P-
completi, tanto che molte riviste scientifiche si preoccupano di mantenere
aggiornato il loro elenco! Nella prossima sezione si presentano alcuni classici
problemi TW-completi. Per tutti questi problemi, ovviamente, non si conosce un
algoritmo di soluzione a complessità polinomiale, né è mai stata dimostrata
l’inesistenza di un tale algoritmo!

9.7 Alcuni classici problemi NP-completi *


SAT, cioè il problema di decidere la soddisfacibilità di una formula di logica
proposizionale, è il primo esempio storico di problema W-completo. Si è già
verificato molto facilmente che questo problema è 5VÌP. Rimane da dimostrare
perciò che sia anche -diffìcile.

Teorema 9.25
SAT è W-difficile.

Dimostrazione
L’obiettivo della dimostrazione è di fornire una computazione deterministica che,
per ogni MT non deterministica M con complessità polinomiale p e per ogni
stringa x sull’alfabeto di M, tale che |x| = n, produca come uscita una formula
proposizionale W, in tempo polinomiale p'(ri), tale che W sia soddisfacibile se e
solo sex e L(M).
Il nocciolo della dimostrazione deriva dall’osservazione che, se x e L(M),
allora esiste una sequenza di mosse, la cui lunghezza non eccede p(ri), che conduce
ad uno stato di accettazione. Quindi, al più p(n) + 1 celle di memoria possono
essere utilizzate da M durante una simile computazione. Una volta compreso che
una configurazione di una MT ha uno spazio limitato, la si può descrivere mediante
un’opportuna formula proposizionale. Ad esempio, una variabile logica Qtyk può
stabilire se, all’istante t, lo stato di M sia qk. Un’altra variabile, CW£, può stabilire
se, all’istante t, la cella z-esima contenga il simbolo ak e così via. Inoltre, opportuni
connettivi logici possono imporre che, all’istante t = 0, la formula descriva una
configurazione iniziale, che la configurazione all’istante t + 1 derivi dalla
463
La complessità del calcolo

configurazione all’istante t e che, all’istante t = p(rì), M si trovi in uno stato di


accettazione.
Procediamo ora con i dettagli della dimostrazione. In primo luogo assumiamo
che M sia una MT a nastro singolo. Come al solito, questa supposizione viene
adottata solo per semplificare la notazione. È immediato verificare che ciò non
provoca alcuna perdita di generalità. Infatti, dalla costruzione del Teorema 4.17, si
giunge immediatamente alla conclusione che, se una MT a k nastri ha una
complessità temporale p(n), allora una MT equivalente a nastro singolo ha
complessità temporale p'(n), per un opportuno polinomio p', anche nel caso non
deterministico (si veda anche l’Esercizio 9.17). Come osservazione secondaria, si
osservi che la costruzione della MT a nastro singolo è effettiva e si può effettuare
in un tempo che dipende dalle dimensioni della macchina (in termini di numero di
stati e simboli), ma non dalla lunghezza della stringa di ingresso. Si suppone anche
che il nastro di A/sia infinito solo a destra.
La formula W viene costruita come congiunzione (a logico) di diverse
sottoformule, o clausole, secondo le seguenti regole. Sia t la variabile tempo, con
0 < t < p(rì), e sia i la posizione di una cella di memoria, 0 < i < p(n).
1. Descrizione della configurazione di M.
Si definiscono i seguenti insiemi di variabili logiche.
- WO < t < p(n),0 < k < |e|-l}
Qa risulterà vera se e solo se A/si troverà nello stato all’istante t.
- 0 < t < p(rì), 0 < i < pipi)}
Ht<i risulterà vera se e solo se la testina M si troverà nella posizione i
all’istante t.
- {Cw/>| 0 < t < p(n), 0 < i < p(n), 0 < h < - 1, dove/1 è l’alfabeto
completo di M]
Cfi,h risulterà vera se e solo se, all’istante t, la cella z-esima conterrà il
simbolo ah-
Abbiamo in questo modo definito un numero
g(n) - (p(n) + 1)-1 Q | +(p(n) + l)2 + (p(n) + l)2 \A |

di variabili logiche, dove g è una funzione ®(p2). In breve, diremo che si sono
definite 0(p2(zz)) variabili.
Chiaramente, in ogni istante, M deve trovarsi esattamente in uno stato, la sua
testina deve essere esattamente sopra una cella e ciascuna cella deve contenere
esattamente un simbolo. In questo modo si ottiene un primo gruppo di
clausole (clausole di configurazione, CC) che le variabili logiche devono
soddisfare per descrivere le configurazioni di M.
Clausole di configurazione (CC):
464 Informatica teorica

A V Qt,k
0<t < p(n) ^0< £<|0|-1 ,
{M deve trovarsi in almeno uno stato alla volta}
A
A (-eU1v-gGÌ2)
0<t<p(n)
0<£,^2<|gH

{Ad ogni istante Mnon può trovarsi in due stati diversi}


A
Z
A V Ht,i
0<t< p(n)\^0< i< p(n)
A
A
0<f<p(n)

{La testina di M si trova esattamente su di una cella alla volta}


A
/ \
A V Ct,i,h
0 < t <p(n) 0 <h <| A |-1 j
0< i< p(n)

A (~^Ct,i,h V ~'Ct,i,k}

0<t< p(n)
0<i< p(n)
0<h,k<\A\-l
h^k
{In ogni istante, ciascuna cella contiene esattamente un simbolo}
Riassumendo, CC contiene il seguente numero di clausole:
w«) + i)-iei + w«) + i)-iei-(iei -i)+
+ (p(n) + l)2 + (p(n) + l)2 ■ p(n) +
+ (p(n) + l)2 • \A\ + (p(n) + l)2 • \A\ ■ (|^| -1)
dove ciascuna clausola è l’“or” logico di un numero limitato a priori di
letterali, e ciascun letterale è o una variabile logica o la sua negazione.

11 Questa è un’ovvia abbreviazione di

(20,0 V ... V 2O,|0|-1 ) A (21,0 v V 21,101-1) A ••• A (2p(n),0 V ... V 2^(n),101-1)


465
La complessità del calcolo

Quindi, CC contiene 0(/23(n)) clausole e 0 (p3(fi)) letterali (si ricordi che |g| e
P<| sono costanti).
2. Descrizione della configurazione iniziale.
All’istante t = 0, M deve trovarsi in q0, la sua testina deve trovarsi nella
posizione 0 e x = 1) deve essere immagazzinata nelle prime n celle
del nastro. Tutte le restanti celle devono risultare vuote. Ciò produce un
secondo gruppo di clausole (Clausole della configurazione iniziale, IC), qui
elencate.
Qo,O A H0,0 A CO,O,ko A ••• A C0,77-1,k„_x A C0,7,0
77<7< p(77)
(si suppone che b sia a0)
IC ha ®(p(rìfi letterali.
3. Descrizione della configurazione di accettazione.
Si adotti la convenzione per la quale, se M si ferma all’istante tH < p(n),
allora mantiene la configurazione finale per tutti gli istanti t, tH < t < p(n).
Questa convenzione provoca conseguenze sulle transizioni della macchina che
verranno descritte fra poco, ma consente di stabilire che, all’istante t =p(ri), M
deve trovarsi in uno stato di accettazione. Ciò produce un’altra clausola
(Clausola di accettazione, AC) che è descritta di seguito:
@p(n},ix V V @p(n},is
dove {qn,...,qis} = F
AC ha 0(1) letterali.
4. Descrizione della relazione di transizione.
Infine, bisogna fornire le clausole che descrivono la transizione della
configurazione dall’istante t all’istante t + 1. La funzione di transizione sia del
tipo 8(qk,Sh) = {(qk-,Sh-Jd}}, con la convenzione che, ogni volta che la 5
originale di Mrisulta indefinita per qualche qk e per qualche s/,, si pone 8(qk,s/,)
= {(qk,Sh,S)}. Indichiamo con m la cardinalità di .
Tali clausole devono imporre:
4.1. Per ogni t e per ogni i, se M si trova nello stato qk, se la sua testina è nella
posizione i e se sta leggendo s^ allora, all’istante t + 1, M si trova, in
modo mutuamente esclusivo (utilizzeremo quindi il simbolo © per
indicare “OR esclusivo”), in una configurazione in cui lo stato è qk-, la
posizione i memorizza e la sua testina si trova nella posizione i,
oppure nella i + 1, oppure nella i - 1, in dipendenza da N, se e solo se la
tema (q^, s^, A).appartiene a 8(qk, sh).
Ciò è descritto dalla seguente formula logica:
466 Informatica teorica

A feci A Hti A C,.!h ) (e,+u. A A Cl+l.!h. )


0</< p(n)
0<i<p(n)
0<k<IQI-l
0<h<[AI-l
®
(f2r+l,k" A Ht+l,i" A C(+1,/,A")

®
fef+i.t"' A A Ct+i,i,h’" )

^<qk.,Sh.,N'},{qk„,sh..,N"},..., {qk,„,Sh„,N’”} 8{qk,sh}


Dove i’ = i (rispettivamente, i + 1, i - 1) se N' = S (rispettivamente R,
Z,)12 e analogamente per N", eco.. La formulazione precedente è
abbastanza intuitiva. Tuttavia, per futuri sviluppi, si preferisce la
seguente forma normale congiuntiva, costruita secondo le pur complesse
regole indicate nella Sezione 2.2:
a ((—iO, i v —'Hv —iC,., v Q, , v ... v Q „ )a
/\ \\ t,ì t,i,h t+l k >
0<f< p(w)
0</<p(«)
0<i<|g|-l
O<A<|^|-1
V —\Hti v ^Ct. h v Qt+lk, v ... v ef+i r,., v Ht+I h„, )a
(~'Q<,k V t . v -.C,. h v Qt+l k. V ... v Ht+^h,„_t v Qt+I k,n )a

(—10,. v —\Ht. v —iC,., v Rf.., v (3. ,.v...vQ )a


\ ì~'t,k t,i t,i,n t+\,n *^r+l h '
(—10,. v —>H. . v -iC,., v H>4,,, v (3. ... v ... v Q „ ! v H „ )a
\ k t,i t,i,h t+l,k ^t+ì^k ^t+l k 1 f+1 h ‘
\ ^-t,k t,t t,i,h t+l,h' „ ì
1 v...vH t v
t+l,h" 1 Q >)a

(-10, t v —i/f t .v ^C. .. v H, v H„ v ...v Q „ ! v Q „ )a


\ *--f,k t,i t,i,n t+\,k t+\,n ^f+l k 1 ^f+l k '
f\ ... (vengono considerate tutte le congiunzioni formate dalle possibili
disgiunzioni di 0 ,, H , e C ,, prendendo uno e un solo
elemento da ciascun termine in “or esclusivo”, e vengono combinate con
l’antecedente) ...a

12 Se i = 0 e N = L, la formula è identica al caso in cui 3 è indefinita, poiché M deve


fermarsi.
467
La complessità del calcolo

'-Q,k v -^Ht . v v^Qt^k. v v^Ct+l .h, v ^2(+1>v?

v -.Hti v^Ct.h v ^2(+u, v -H,,, v -iCr+1>/„. v ~<QI+Ìt„ '

v t+i,i,h"' j
( —,Q,
^t,k. v —iHt,i
t . v-,C,.. ^~'t+l,K,„ v —,77,7 + 1,. z .„ v —iC,..
t ,J ,h v -,0,., *~'t+\,k A
. .„ v —,O
7 + 1,z,«

kvv ^<+1,;

(~>Q,.
z-'t,k v t,i ■ V -.C, .. v -.0
t,i,n z-'t + \,k. ... v —,777 + 1,7. .„ v —iC,,. . v ^t + lk „ ò
t + l,i,n
v -iH m v t+l,i
-iC t+l,i,h m . J

f^Q,
^t,kt v t,l• V -iC, .. v -,Q
t>l,h .,1 v-,C t+\,i>h „,v^g
^t+l,km 1 v —,77 t+l,i"' 1 ^t+\,km ò

4.2. Se, all’istante t, la testina si trova nella posizione z, allora, all’istante


t + 1, tutte le celle, esclusa la z-esima, hanno lo stesso contenuto che
avevano all’istante t.
/\ (-1-^t,i A CCt+l,i,h

0<t< p(n)
0<i< p(n)
0<k<\Q\-l
0< A<M|-1
che, ancora, si può riformulare nel modo seguente:
t,i v v ^t+l,i,h'
L’insieme TC di clausole relative alla transizione è definito come la
congiunzione delle formule derivate in 4.1 e 4.2. TC ha 0(p2(n)) letterali
(si noti che il numero di ‘ffi’ nelle clausole 4.1 è limitato a priori perché
così è la cardinalità degli insiemi {(qk-,sh',N)} e, di conseguenza, il
numero totale di clausole viene moltiplicato nella trasformazione in
forma normale congiuntiva per un termine che dipende - pur
esponenzialmente - solo da m).
Si pone, infine, W= CC a IC a AC a TC, che significa che W è la congiunzione di
tutte le clausole che appartengono agli insiemi precedenti. W ha quindi 0(p3(zz))
letterali.
468 Informatica teorica

A questo punto, il nucleo della dimostrazione è completo. Tuttavia, prima di


concluderlo formalmente, vale la pena sottolineare alcuni dettagli, anche se
abbastanza ovvi.
a. Un’attenta analisi della costruzione di W non dovrebbe lasciare alcun dubbio
che Co •- ^(n> CA, dove Co è la configurazione iniziale e CA è una
configurazione di accettazione, se e solo se W risulta soddisfacibile. Ad
esempio, ogni assegnamento di valori di verità che soddisfa ffl deve essere tale
da rendere vera la QOfi.
b. Abbiamo descritto una procedura deterministica che costruisce una W
contenente ®(p3(n)) letterali, cominciando da x. Per mostrare che i requisiti
formali della Definizione 9.13, e quindi della Definizione 9.12, sono
soddisfatti, bisogna codificare SAT come problema di riconoscimento di
linguaggio. Come osservato nel Teorema 9.24, una formula proposizionale
che contiene n occorrenze di variabili logiche si può codificare come una
stringa di lunghezza 0(wlog n). Quindi, per ogni x di lunghezza n, la sua
traduzione t(x) in una stringa che codifica W è certamente non più lunga di
®(pXn)). Una volta compreso che la traduzione r può essere eseguita da una
MT deterministica (anche multinastro) in un tempo ®(|t(x)|), la riducibilità in
tempo polinomiale è completamente dimostrata.
c. Si noti che non bisogna effettivamente conoscere una MT M che risolva il
problema originale. L'esistenza di tale macchina garantisce resistenza di una
macchina riducente in tempo polinomiale. Tuttavia, se si conosce una simile
macchina M e se è noto un limite di tempo polinomiale p per la sua
computazione nondeterministica, allora la dimostrazione consente
effettivamente di costruire la macchina che esegue la riduzione dei problemi.

Si noti che, durante la dimostrazione del Teorema 9.25, ci si è sforzati di
mantenere ffl in forma congiuntiva. Quello sforzo non era necessario per la
dimostrazione, ma consente ora di ottenere, gratuitamente, un importante
corollario, il quale stabilisce che anche un sottoproblema del problema originario
della soddisfacibilità delle formule proposizionali è 5W -completo. Questo
corollario, a sua volta, costituisce un utile lemma per futuri sviluppi.

Corollario 9.26
Il problema di stabilire la soddisfacibilità delle formule proposizionali in forma
normale congiuntiva è iNP-completo.
469
La complessità del calcolo

Dimostrazione
Se un problema è in W(P, ovviamente anche un suo sottoproblema si troverà in 5W.
Poiché la formula W, ricavata nella dimostrazione del Teorema 9.25, è in forma
normale congiuntiva, il corollario è già dimostrato.

La soddisfacibilità delle formule proposizionali è un risultato fondamentale
nell’ambito dei problemi W(P-completi. Da esso deriva che la -completezza di
una gran parte di questi problemi si può dimostrare, in modo naturale, riducendo
SAT ad essi, sia direttamente che indirettamente. Discutiamo ora un altro esempio
classico, cioè il problema dell’esistenza di un ciclo Hamiltoniano in un grafo (HC).
La dimostrazione dell’WCP-completezza di questo problema sfrutta la tecnica
appena descritta.

Teorema 9.27
HC è 5V?P- completo.

Dimostrazione
Poiché si è già affermato che HC è in %(P, bisogna solo mostrare che è %(P-
difficile. Ciò verrà ottenuto mostrando che SAT, in forma normale congiuntiva, è
riducibile in tempo polinomiale a HC. Per la transitività della riducibilità in tempo
polinomiale, ogni problema in sarà riducibile a HC. Naturalmente la riduzione
verrà ottenuta mediante una procedura che, per ogni formula proposizionale W in
forma normale congiuntiva, costruisce un grafo G che ammette un HC se e solo se
W risulta soddisfacibile.
Il procedimento consiste nel costruire G come aggregazione di due classi di
“pezzi”. I pezzi del primo tipo saranno associati a ciascuna variabile logica in W e,
in un certo senso, ne mostreranno tutte le occorrenze. I pezzi del secondo tipo
saranno associati a ciascuna clausola di IL e legati ai nodi che appartengono ai
pezzi del primo tipo, in modo tale che il loro attraversamento garantisca la verità
della clausola associata.
Prima di entrare nei dettagli formali della dimostrazione, illustriamone il
principio costruttore attraverso un semplice esempio. Si consideri la fbf W\
Ai a(—lTi v^2) e il grafo G di Figura 9.23.
I sottografi GAi e GA2 sono associati, rispettivamente, a Ai e A2. GCi e GC2
sono associati alle clausole C): Ai, C2: —Ai vA2. Chiaramente, Wrisulta soddisfatta
solo da Ai = T, A2 = T. Si consideri ora G. Un possibile HC per G deve
necessariamente attraversare IAi. Si noti che GC. impedisce ad HC la scelta di Fw
come nodo successivo, dopo aver visitato IAi. Infatti, dopo Fw, HC deve
raggiungere Lu (altrimenti diverrebbe inaccessibile) e poi . Tuttavia ciò rende
470 Informatica teorica

Figura 9.23. Il grafo associato alla fbf JV: I lati in neretto


identificano il ciclo Hamiltoniano. Le linee tratteggiate
racchiudono i “pezzi” costituenti.

inaccessibile Fo, quindi Fo deve necessariamente seguire IA\. Intuitivamente ciò


corrisponde al fatto che Q impone che A\ sia vera.
Dopo la scelta di Fio, viene scelto il cammino Fw, Lu, Ln , Tn, Fu, OAt, IA2,
in quanto garantisce l’inclusione di Fi0 e Fu in HC. Una volta raggiunto IA2, si
considera l’alternativa fra T20 e F20 (ossia l’assegnazione a A2 di un valore vero o
falso). La scelta di F20 forzerebbe a seguire il cammino Z22 (altrimenti l’intero GC2
non verrebbe incluso in HC), Z2i (altrimenti Z2i diventerebbe inaccessibile), Z21,
L22 , T2i. A questo punto T20 diventerebbe inaccessibile. Quindi bisogna scegliere
471
La complessità del calcolo

T20 come successore di IA2. Infatti, GC2 e Ai = T impongono A2 = T. A questo


punto HC è completato.
Prima di addentrarsi nel resto della dimostrazione, il lettore è invitato a
svolgere il seguente esercizio.

ESERCIZIO
9.47 Si costruiscano i grafi G, corrispondenti ai seguenti W,, tali che G, abbia un
HC se e solo se W, è soddisfacibile.
Wp. (AjvA2) a(AiV-A2)
fflp (Ai a A2v A-)) a(—A[VA2) a(—A2\/A-$)
W3'. (A] v —A2) a (—Ai v A2) a (—Ai v —A2) a (Ai v A2)

In caso di successo, il lettore sarà probabilmente in grado di completare la


dimostrazione senza alcun aiuto. Altrimenti, invitiamo il lettore ad esaminare la
seguente costruzione e a ritornare, in seguito, sull’esercizio.
Sia Wpari a C) a C2 a ... a Ch, dove le clausole contengono le variabili logiche
Ai,..., Am. Per ogni Ah si costruisca un grafo GAt del tipo di quello mostrato in
Figura 3.18a, in cui p, è il massimo numero fra il numero di occorrenze di A, e
quelle di —A, in W. Si osservi che ciascun GA, contiene esattamente due cammini,
da lAj a OAh che visitano tutti i suoi nodi, uno che parte da Tm e uno che parte da
Fm.
Si connetta ora OA, a IAi+l (modulo m) (si veda la Figura 9.24).
Successivamente, per ciascuna clausola Cj = Lji v L,-2 v ... v Ljk , dove Ljr è
Ajr o -a A fr per qualche variabile, si costruisca un grafo GCj del tipo di quello in
Figura 9.24(c).
Si connetta GCj al resto del grafo, secondo la seguente regola. Per ogni r, se
Ljr è Ajn si connetta il primo nodo Fjr,s di GAjr avente solo due archi uscenti a Ljr, e
Ljr a Fjr,s+i. Si noti che tali connessioni sono certamente possibili poiché p^ è
maggiore 0 uguale al numero di occorrenze di At in W.
Si consideri ora il grafo completo G ottenuto dalla precedente costruzione e si
osservi che
1. Sei GCj vengono ignorati, vi sono esattamente 2'” HC nel grafo restante, G .
Ciò corrisponde al fatto che, se W è vuota ogni assegnamento di valori di
verità, banalmente, la soddisfa.
2. Un possibile HC entrante in qualche GCj da Ljr deve lasciarlo da Ljr . Ad

esempio, un cammino che entra in GCj AaLj2, visitando Lj2 , L /1 e lasciando


infine GCj, renderebbe Lji inaccessibile. La generalizzazione a tutti i casi
possibili è immediata. Ciò non implica che un eventuale HC debba visitare
tutti i nodi di ciascun GCj consecutivamente.
472 Informatica teorica

3. Ogni possibile HC deve essere ottenuto da un HC di G sostituendo qualche


arco del tipo (F^, Tks+Ì) con un cammino che passa attraverso qualche GCj, se
HC contiene l’arco (IAh 7}0\ Viceversa, se HC contiene l’arco (LAj, Fi0), esso
deve essere ottenuto da un HC di G , sostituendo qualche arco del tipo
F.j+i) con un cammino che passa attraverso qualche GCj.
4. Il punto precedente implica che si può entrare in ogni GCj attraverso qualche
Ljr mediante un HC, solo da qualche 7}r;S se {IAjr, Fjrfi} è in HC e solo da
qualche Fjr>s se {IAjr, Tjrfi) è in HC. Ciò corrisponde al fatto intuitivo che
entrare in GCj da qualche Fjr>s significa soddisfarlo supponendo Ajr = T.
5. Si supponga ora che W sia soddisfatta da qualche assegnamento di valori di
verità a A\...Am. Si può allora costruire un HC per G nel modo seguente.
6. Dapprima si costruisca un HC per G , scegliendo il lato {IAh Ti0) se e solo se
Ai è vero nel precedente assegnamento. Successivamente, ogni volta che un
tale HC ha un arco (7);S, ({F^, Ti>s+Ì) rispettivamente) e un arco che
connette 7);J (rispettivamente Fis) a qualche GCj in cui non si è ancora entrati,
si visiti V intero GCj entrando in esso da l\s (rispettivamente F;>) e rientrando
in G da Fv+i (rispettivamente 7)>+i). Poiché, per ogni GA,, vi sono almeno
tante TitS, Fks quante sono le occorrenze di A,, —A, in W, non può succedere
che, alla fine di questa procedura, qualche GCj non sia ancora stato
attraversato, poiché ciò implicherebbe la possibilità di accedere a tutte le sue
Ljr soltanto provenendo dai “nodi sbagliati” di ciascun GA, (ossia da Tjr>s se Ajr
è vero e viceversa). Tuttavia ciò significherebbe che l’assegnamento di cui
sopra non soddisfa W.
L’affermazione opposta, ossia che 1’esistenza di un HC per G implica la
soddisfacibilità di W, dovrebbe ormai essere un facile esercizio per il lettore.
Per completare la dimostrazione, bisogna considerare la complessità della
costruzione. Sia n il numero di letterali in W. Chiaramente G ha, al più, 2-(n + 1)
+ 2-m nodi. Poiché m < n, la dimensione di G è ®(n). Il numero totale di nodi di
{GCj\j = 1,...,h} è2-n. Quindi i nodi di G sono ®(n).
Analogamente al Teorema 9.25, si fa notare che la codifica di grafi con ®(n)
nodi può richiedere stringhe di lunghezza ®((n-log n)2) (vi sono al più n2 archi in
un grafo con n nodi), senza causare alcun problema dal punto di vista della
riducibilità in tempo polinomiale. Inoltre, è piuttosto ovvio che un algoritmo
banale con complessità ®(n) possa costruire G e GCj, e che un algoritmo con
complessità ®(n2) possa facilmente completare le connessioni fra G e GC.
473
La complessità del calcolo

(a) (b)

(c)
Figura 9.24. La costruzione del grafo associato ad una fbf. (a) La parte
associata alla variabile logica Aj. (b) La connessione delle parti
{GAj}. (c) La parte GCj associata alla clausola C,.
474 Informatica teorica

Il principio della riduzione dei problemi ha vaste applicazioni. Ad esempio, nel


Capitolo 8, si è riusciti a dimostrare l’irrisolubilità di alcuni problemi relativi ai
linguaggi codificando le computazioni della MT in stringhe appartenenti ad
opportuni linguaggi. In questo modo si è riusciti a ricavare l’irrisolvibilità di altri
problemi relativi ai linguaggi riducendoli al problema originale. Analogamente,
abbiamo ora mostrato la jW-completezza di SAT riducendo le computazioni di
MT nondeterministiche limitate da tempo polinomiale ad opportune istanze di
SAT. Successivamente abbiamo dimostrato la AT’-completezza di HC riducendo
SAT ad esso. Il lettore è invitato a considerare quanto sarebbe stato più diffìcile
affrontare direttamente il problema HC. Inoltre, il lettore è invitato a valutare la sua
abilità nell’applicazione delle tecniche di riduzione cercando dimostrazioni di 5W-
completezza per i seguenti problemi.
- Il problema del commesso viaggiatore.
- Il problema del ciclo Hamiltoniano per grafi non orientati.
- Il problema della ic-colorabilità (ossia: si possono colorare i nodi di un grafo
non orientato, usando k colori diversi, in modo tale che nessuna coppia di
nodi, connessi da un lato, abbiano lo stesso colore?)
- Il problema delle clique.
- La programmazione lineare intera (ossia, sia A una matrice intera nXm e sia B
un vettore di n interi. Esiste un vettore X, di m numeri interi, tale che
AX > B!)
La programmazione lineare intera non segue il solito schema dei problemi XP-
completi. Infatti risulta facile, in genere, mostrare che un problema è mentre si
rivela più difficile mostrare che è XT -diffìcile. In questo caso, accade esattamente
l’opposto.
Molti altri problemi con interessanti applicazioni pratiche sono MP-completi.
Tra questi molti problemi utili per la progettazione di reti, quali problemi
riguardanti l’albero minimo di copertura con vincoli sul grado dei vertici e sulla
lunghezza dei cammini, problemi di tagli nei grafi, di affidabilità di una rete, di
instradamento (tra cui il già citato problema del commesso viaggiatore) e problemi
di flusso. Nell’insieme dei problemi TW-completi ci sono molti problemi correlati
alla memorizzazione (Storage) e alla ricerca (retrieval) dei dati e problemi di
scheduling su multiprocessori. Il lettore interessato può trovare riferimenti utili
nelle note bibliografiche.

9.8 Limiti inferiori di complessità


Una volta scoperto un algoritmo A, di complessità T, che risolve un problema P, si
ha un limite superiore per la complessità della risoluzione di P\ si sa, cioè, che, nel
peggiore dei casi, P si può risolvere in un tempo T. Ovviamente, in generale, si può
sperare di trovare un nuovo algoritmo A' che risolva P con una migliore
complessità T. Le precedenti esperienze mostrano che questa situazione si verifica
spesso. Oltre all’applicazione meccanica del teorema dell’Accelerazione, che può
475
La complessità del calcolo

arbitrariamente aumentare l’efficienza temporale senza cambiare ®(T), si può


usare l’intuito per progettare nuovi algoritmi, che possono anche migliorare il
comportamento asintotico.
Per questo motivo, una domanda naturale è quanto sia possibile migliorare
l’efficienza della soluzione di un problema. Ad esempio, una volta trovato un
algoritmo per P di complessità T, si può sperare di ottenere una complessità Vt” o
anche log(T) inventando un nuovo algoritmo? Appare evidente che la soluzione più
intuitiva a un problema raramente rappresenta anche la più efficiente; per ottenere
risultati migliori, infatti, è necessaria una profonda comprensione del problema in
analisi. Rimane lecito però chiedersi se una soluzione ingegnosa può essere
migliorata ulteriormente o se essa rappresenta la migliore soluzione possibile in
termini di complessità asintotica. In altre parole, ci interessa stabilire quali siano i
limiti inferiori della complessità dei problemi, in modo tale che non si debba
sprecare tempo cercando di raggiungere obiettivi impossibili.
Si richiamano qui alcuni fatti precedentemente osservati. Secondo il Teorema
9.22, per ogni funzione totale T, si può costruire un linguaggio non contenuto in
DTTME(T). Per definizione, quindi, T è un limite inferiore per la complessità del
riconoscimento di un simile linguaggio. Si noti che, se T è un limite inferiore per il
riconoscimento di un linguaggio mediante una MT deterministica multinastro,
allora 4t è un limite inferiore per il riconoscimento del medesimo linguaggio
mediante una RAM (si veda il Teorema 9.17).
I linguaggi costruiti nella dimostrazione del Teorema 9.22 sono, tuttavia, tipici
“linguaggi diagonali”, cosicché non possono essere di grande aiuto per determinare
i limiti inferiori della complessità nel riconoscimento di linguaggi del tipo
2
{anbncn } o nell’ordinamento di un vettore e così via. Bisognerebbe quindi
trovare qualche strumento adatto a ricavare i limiti inferiori per problemi concreti.
Una prima annotazione banale è la seguente. Nella maggior parte dei casi
peggiori, per decidere se una stringa appartenga, o meno, ad un linguaggio, è
necessario scandirla completamente. Ad esempio, si consideri il riconoscimento di
L = {anbn}. Quando si analizza la stringa abaaabaaab si può decidere che non
appartiene a L già dopo aver letto il terzo carattere. Quando invece si ha a che fare
con una stringa del tipo anbm, con m<n, si può decidere se m = n solo dopo aver
letto l’intera stringa. Il problema del riconoscimento di L ha quindi un limite
inferiore ®(m). Poiché si è già compreso che L può effettivamente essere
riconosciuto in un tempo ®(m), si è completamente risolto il problema della
complessità temporale di L. Quindi, per la maggior parte dei problemi, esiste un
banale limite inferiore per la complessità temporale, e precisamente ®(w).
Sfortunatamente, Tottenimento di limiti inferiori meno banali per problemi
significativi non è altrettanto semplice. Ad esempio, si consideri il problema di
riconoscere linguaggi non contestuali. Il limite superiore di complessità
476 Informatica teorica

attualmente conosciuto è ®(n2'49)13, ma non si conosce alcun limite inferiore


maggiore di ®(n), sebbene si sospetti che alcuni linguaggi non contestuali non
siano riconoscibili in tempo lineare.
Uno strumento migliore per ricavare i limiti inferiori di complessità è la 1W-
completezza, almeno sotto l’ipotesi che P 5V?P . Infatti, se vale questa
affermazione, ogni problema TW-completo deve avere una complessità non
polinomiale, e ciò è generalmente considerato come un sinonimo di intrattabilità.
Oltre alla TW-completezza, è stato dimostrato che alcuni problemi hanno
limiti inferiori esponenziali, e quindi sono certamente intrattabili. Un esempio è il
problema della raggiungibilità per le reti di Petri. Data una RP N e due marcature
mi e m2, la decisione se m} m2 richiede uno spazio e un tempo almeno pari a
©(2™), dove n è il numero di posti di N. Non si approfondirà lo studio di questo
tipo di risultati di intrattabilità, poiché i tecnicismi coinvolti sono al di fuori dello
scopo di questo testo. Il lettore interessato è rinviato alla letteratura specializzata.
I problemi più difficili compaiono generalmente quando si cercano limiti
inferiori non banali aH’intemo di P. Si supponga di sapere che un linguaggio è in P
(ad esempio i linguaggi non contestuali sono in P). Ovviamente, vi è molta
differenza fra un limite inferiore ®(n) e, ad esempio, ®(n1000). Sfortunatamente, in
questo campo, lo stato dell’arte non fornisce un insieme potente di strumenti
matematici. Si possono solo dimostrare singoli enunciati, mentre mancano risultati
di tipo generale.
Una notevole eccezione è offerta dal Teorema 9.7, in cui si è presentata la
nozione di sequenza di attraversamento per dimostrare un limite inferiore ®(n2) per
il riconoscimento di {wcwÀ} da parte di una MT a nastro singolo. Sfortunatamente,
queU’affermazione e la sua dimostrazione sono strettamente legate al modello
usato. Infatti, una MT multinastro può riconoscere lo stesso linguaggio in un tempo
®(n). Le sequenze di attraversamento sono un potente strumento per la
dimostrazione dei limiti inferiori di complessità, ma la loro generalizzazione alle
MT multinastro deve tener conto di tutte le possibili configurazioni dei nastri di
memoria cosicché diventano più adatte per limiti inferiori esponenziali.
Prima di chiudere questa sezione, si fa cenno ad un altro problema di alto
interesse pratico, cioè lo studio del limite inferiore per il problema
dell’ordinamento.

Teorema 9.28
L’ordinamento di una sequenza di n elementi non ordinati richiede, nel caso
pessimo, almeno ®(n ■ log ri) confronti a coppie.

13 Questo risultato è stato ottenuto riducendo il problema al problema della moltiplicazione


fra matrici.
La complessità del calcolo 477

Dimostrazione
Sia A = la sequenza da riordinare. Un algoritmo di ordinamento deve
produrre una permutazione ordinata di A, ad esempio Ao = {a^,a;- },
effettuando un certo numero di confronti. Ad esempio, se A è già ordinata, sono
sufficienti n - 1 confronti, in quando è sufficiente stabilire che ax < a2, a2 < a3, ...,
-1 <
L’insieme di tutte le possibili esecuzioni di un algoritmo di ordinamento può
essere efficacemente rappresentato da un albero di decisione, in cui ciascun nodo
interno rappresenta un confronto, il suo figlio sinistro (rispettivamente, destro)
rappresenta la conseguenza del risultato positivo (rispettivamente, negativo), e
ciascuna foglia rappresenta la permutazione di A ottenuta come risultato dei
confronti. Quindi, un cammino dalla radice ad una foglia rappresenta tutti i
confronti effettuati daH’algoritmo per ottenere la foglia come risultato. La Figura
9.25 rappresenta un albero di decisione per l’ordinamento di 3 elementi,
applicando l’algoritmo di ordinamento per inserzione diretta fornito nella Sezione
8.3.

Figura 9.25 L’albero di decisione per ordinare tre elementi mediante


l’algoritmo di ordinamento per inserzione diretta. Gli apici
indicano i nuovi valori assunti dagli elementi ah come
conseguenza degli assegnamenti. I confronti con l’elemento fittizio
a[0] sono stati saltati.
478 Informatica teorica

L’altezza dell’albero di decisione (ossia la lunghezza del più lungo cammino dalla
radice alle foglie) è il numero di confronti effettuati dall’algoritmo nel caso
peggiore.
Si noti ora che il numero di foglie per un simile albero è pari a ni, poiché
questo è il numero di possibili risultati diversi dell’algoritmo. L’albero di decisione
è un albero binario. Un albero binario di altezza h ha al più 2h foglie (si verifichi
questa affermazione come esercizio). Perciò, l’altezza di un albero binario con m
foglie è, almeno, uguale a log2 m. In conclusione, l’altezza di un albero di
decisione corrispondente all’esecuzione di un algoritmo di ordinamento deve
essere almeno pari a log(w!).
Infine 0(log ni) è almeno 0(wlog n) poiché, per n > 1,
n
( n) ( n)2
n\> n(n - l)(n - 2)... — > —

n I
e quindi log( /?!)>— log — , che è 0(w log n).
2 <2 )
D’altra parte, log( n!) = log n + log( n - 1) + ..., cosicché log(w!) < wlog(w).

Sono richiesti, quindi, almeno 0(wlog ri) confronti per ordinare n elementi. Se si
suppone che il tempo richiesto per effettuare un confronto sia costante e che la
quantità totale di tempo impiegata da un algoritmo di ordinamento sia
proporzionale al numero di confronti effettuati, allora si può stabilire il limite
inferiore 0(wlog ri) per gli algoritmi di ordinamento. Si noti che l’utilizzo di
confronti multipli, ma finiti, influenza solo la base del logaritmo.
Il lettore scettico potrebbe obiettare all’ipotesi che un algoritmo di
ordinamento debba necessariamente passare attraverso una sequenza di confronti.
In effetti quando ordiniamo un mazzo di carte possiamo usare anche un algoritmo
che non fa uso di confronti: ad esempio possiamo posizionare ogni carta “al suo
posto” (l’asso di cuori in posizione 1, il due di cuori in posizione 2 ecc.); tuttavia
estendere questa tecnica a insiemi in cui gli elementi da ordinare siano molti meno
delle “posizioni” ossia dei possibili valori degli elementi dell’insieme, già
preordinati (ad esempio un insieme di parole su un certo alfabeto) comporterebbe
un intollerabile spreco di spazio. Assumiamo perciò che un algoritmo di
ordinamento sia basato, tranne casi eccezionali, su un meccanismo di confronto.
Ciò giustifica l’affermazione che, in pratica, l’ordinamento richieda almeno un
tempo 0(wlog ri).
Si osservi che esistono diversi algoritmi, quali il Mergesort e l’Heapsort, che
sono in grado di risolvere il problema dell’ordinamento con complessità temporale
©(«■log ri). Il risultato del Teorema 9.29, garantisce che tali algoritmi non possono
essere migliorati e forniscono la soluzione migliore, in termini di complessità
asintotica, al problema dell’ordinamento.
479
La complessità del calcolo

In generale, quando si trova un algoritmo che risolve un problema con una


complessità asintotica pari a quella del limite inferiore dimostrato si dice che il
problema è chiuso, in caso contrario, si dice che il problema è aperto.
Oltre al problema dell’ordinamento, un altro problema chiuso è quello della
ricerca di un elemento sia in una sequenza ordinata che in una sequenza non
ordinata. Per tali problemi, usando una tecnica simile a quella mostrata per il
problema di ordinamento, si può dimostrare che i limiti inferiori sono
rispettivamente 0(log ri) e 0(h), raggiunti nel primo caso dalla ricerca binaria e nel
secondo dalla ricerca sequenziale. Per analizzare ulteriormente questo concetto si
consideri il seguente esempio.

Esempio 9.21
Si supponga di avere un array A contenente un insieme di n numeri interi tutti
distinti e disposti in un ordine non precisato. Consideriamo il problema di costruire
un albero di ricerca binario14, che contenga tutti gli elementi di A e sia bilanciato,
cioè abbia profondità minima.
Prima di provare a risolvere il problema, che non è di interesse per questo
testo, è piuttosto interessante chiedersi se sia possibile identificare un limite
inferiore alla complessità per risolvere il problema.
Si osservi che a partire da un albero di ricerca binario è possibile produrre una
sequenza ordinata del contenuto dell’albero mediante una semplice visita
dell’albero in pre-ordine sinistro. Tale visita ha costo lineare. È quindi intuitivo
dedurre che n log n costituisce un limite inferiore per il problema originale. Infatti
se si potesse costruire un albero binario di ricerca con costo inferiore a n log n,
sarebbe anche possibile risolvere il problema dell’ordinamento a costo inferiore,
ma abbiamo già dimostrato che ciò non è possibile.

9.9 Consigli e conclusioni


In questo capitolo si è sottolineata la necessità dell’analisi di complessità per il
progetto degli algoritmi. Si è visto, tuttavia, che la validità di molti risultati è
limitata dalla dipendenza da diversi dettagli implementativi, che sono spesso
sconosciuti al momento del progetto. Inoltre, i modelli teorici discussi hanno forti
conseguenze sul progetto pratico degli algoritmi. La situazione assomiglia, in un
certo senso, a quanto accade in altri campi dell’ingegneria. Nel corso del progetto
di un ponte, i formalismi matematici non consentono di raggiungere una
conoscenza perfettamente dettagliata sul fatto che il ponte crolli o meno in certe

14 Si ricordi che un albero binario di ricerca è un albero binario in cui per ogni nodo i valori
contenuti nel sottoalbero sinistro sono minori del valore del nodo stesso e quelli contenuti
nel sottoalbero destro sono maggiori.
480 Informatica teorica

condizioni. Tuttavia il loro uso, integrato con l’esperienza pratica, accrescerà di


molto l’affidabilità del progetto risultante.
Analogamente, si supponga di dover progettare un algoritmo per risolvere un
problema P con il vincolo che un simile algoritmo debba essere eseguito da una
macchina dotata di una memoria principale di dimensioni limitate. Molti risultati
teorici possono essere d’aiuto durante il progetto. In primo luogo può accadere che
in letteratura sia già stata dimostrata l’esistenza di un limite inferiore esponenziale
per la complessità della soluzione di P. In questo caso, bisognerebbe abbandonare
il problema P (come se fosse indecidibile) o cercare qualche caso particolare più
semplice per il quale esista una soluzione migliore. Viceversa, si supponga di
essere riusciti a capire che il calcolo di P, mediante una MT multinastro, richiede
una complessità Tcon ®(p\) < ®(T) ®(pì), doveri, sono due polinomi.
In tal caso, la soluzione di P risulterà probabilmente computabile in un tempo
accettabile. Si può quindi cominciare a progettare un algoritmo che risolva P
(certamente non sulla base delle MT, ma in termini di un linguaggio a più alto
livello). Dopo aver progettato un simile algoritmo, si può eseguire un’effettiva
analisi di complessità sulla base di ragionevoli ipotesi di costo. Ad esempio, se è
noto che i valori coinvolti nella computazione normalmente non eccederanno la
dimensione della parola del calcolatore, si può usare il criterio di costo uniforme
per l’accesso alle variabili e per le operazioni aritmetiche. Diversamente, si
potrebbe assegnare un prezzo più alto alle operazioni che consumano memoria e a
quelle di I/O, poiché la possibile uscita dal limite della memoria potrebbe
richiedere operazioni di movimento di blocchi di memoria molto costose in termini
di tempo. Come risultato, si ottiene una stima ragionevole del costo per eseguire
l’algoritmo dalla macchina disponibile.
In alcuni casi, potrebbe anche succedere che un algoritmo con tempo di
esecuzione quadratico venga eseguito più velocemente di uno in tempo lineare.
Questa situazione si verifica in alcuni assemblatori o compilatori a singola passata,
che vengono eseguiti completamente nella memoria principale, diversamente da
altri -ormai quasi del tutto abbandonati- che adottavano invece una strategia a più
passate, memorizzando i risultati intermedi su file ausiliari. Molto spesso, la prima
soluzione ha complessità quadratica (ma ciò avviene in casi pessimi molto distanti
dalla normalità), mentre la seconda ha complessità lineare, pur usando molte più
istruzioni di I/O che comportano un notevole dispendio di tempo.
Come osservato aH’inizio di questo capitolo, la complessità computazionale è
solo un aspetto parziale del problema della stima del costo di un programma per
calcolatore. Le sole componenti di costo prese in considerazione dalla complessità
computazionale sono legate all’uso del calcolatore (ossia il tempo di esecuzione e
la memoria richiesta). In pratica, i costi umani sono, alla lunga, il fattore
dominante. Ciò comprende lo sforzo umano richiesto per progettare il programma
la prima volta, lo sforzo richiesto per collaudarlo, per correggerlo e, sopratutto, per
mantenerlo.
Questi aspetti, molto più legati al “fattore umano” devono essere
necessariamente modellizzati con obiettivi e tecniche differenti da quelli tipici
481
La complessità del calcolo

dell’informatica teorica. Essi sono quindi al di fuori degli scopi di questo testo e
sono affrontati nell’ambito dell’ingegneria del software15.

RIASSUNTO DEL CAPITOLO


La complessità computazionale è una misura del costo che bisogna pagare per la
risoluzione meccanica dei problemi. Si possono considerare molti tipi di
complessità e molti fattori possono influenzare le misure di complessità. In questo
capitolo, l’attenzione è stata focalizzata principalmente sulla complessità spaziale e
temporale, sull’analisi del caso pessimo e sui problemi di riconoscimento dei
linguaggi.
Lo studio della complessità computazionale è cominciato dagli automi, e
precisamente dalle MT, dagli AF e dagli AP. All’interno di questa cornice sono
stati ricavati alcuni concetti generali, come le classi di complessità, la possibilità di
migliorare linearmente le misure di complessità e le gerarchie di complessità.
Successivamente si è rivolta l’attenzione ad altri modelli computazionali, più vicini
ai reali dispositivi di calcolo. Si è mostrato come le misure di complessità possono
differire da modello a modello, pur restando correlate almeno polinomialmente.
Sono state inoltre considerate le computazioni non deterministiche: le due
fondamentali classi di problemi, <P (la classe dei linguaggi riconoscibili in tempo
polinomiale da dispositivi deterministici) e 5V(P (la classe dei linguaggi
riconoscibili in tempo polinomiale da dispositivi non deterministici) sono state
studiate in un certo dettaglio. La nozione di riducibilità di un problema è stata
applicata alle classi di complessità e ciò ha condotto al concetto fondamentale di
WL-completezza. E stato descritto anche qualche classico problema WL-completo.
Sono stati fomiti alcuni suggerimenti per affrontare il difficile problema della
dimostrazione dei limiti inferiori di complessità, ovviamente per i casi non banali.
E stato infine presentato e commentato qualche esempio di analisi di
complessità per problemi di interesse più pratico.

ALTRI ESERCIZI

9.48 Siano L\ e L2 due linguaggi ricorsivi contenuti rispettivamente in


DTlME(/j)_ e DTlMEfjS). A quale classe appartengono Zi u L2,
Li n L2, Lj ? Che cosa succede se si sostituisce DTIME con NTIME?
9.49 Si consideri la MTU Mv costruita nella Sezione 8.4.2 o qualunque altra
MTU. Per ogni data MT M, con complessità temporale T^n), si ricavi la
complessità Ti/M, ri) di Mv simulando M. Si noti che Tv dipende sia da n
che dalla dimensione della descrizione di M.

15 Ciò non significa che i vari modelli non abbiano radici matematiche in comune (ad
esempio la statistica) e che non possano o debbano essere usati in pratica in maniera
integrata.
482 Informatica teorica

9.50 La macchina RASP (Random Access Stored Program, ossia ad accesso


diretto e programma memorizzato) è una modifica della macchina RAM in
cui il programma è contenuto nella memoria principale della macchina in
modo molto simile all’architettura classica di Von Neumann. In un certo
senso, la macchina RASP si può considerare una macchina RAM
universale.
a. Si dia una definizione della macchina RASP.
b. Si mostri che nella macchina RASP l’indirizzamento indiretto è
superfluo.
c. Si stabiliscano le relazioni di complessità temporali fra le
macchine RAM e RASP.
9.51 Si modifichi la grammatica SMALG data in Figura 9.13 allo scopo di
consentire l’uso di vettori multidimensionali e di espressioni aritmetiche
arbitrariamente complesse (come in Pascal). Si modifichino di conseguenza
le regole date nella Tabella 3.3 per valutare la complessità temporale dei
programmi SMALG.

Soluzione schematiche di esercizi scelti

3.2 0(/ì) < = 0(Zj) (ossiajS è ©(/))) < 0(4), fa non è confrontabile con ogni
z = 1, ..., 4.
3.17 Si consideri la costruzione schematizzata in Figura 9.6. Una MT a nastro
singolo M', che simula la MT 1-nastro M schematizzata in Figura 9.6b, può
avere il suo unico nastro organizzato come in Figura 9.26. La lunghezza
della porzione non vuota del suo nastro non eccede n + S!./n), con
S^n) < Tj^ri).
Quindi, la simulazione di una singola mossa di M da parte di M', richiede al
più n + S!./n) passi da parte di M', con Q(n + S^n)) < ©(T^ri)) se
IVn) >n. Quindi TM(rì) è 0 (T^- ).

Ingresso Contenuto del nastro di memoria

Figura 9.26. L’aspetto del singolo nastro di M’.

3.40 Se si implementa l’operazione “inserisci (vj,vk) in PS e PT' mediante il


codice 0(1) “fflffl = mm + 1; PS\mm\ = i; PT[mm] = le”, il programma
risultante può anche non terminare. Infatti, si supponga che esista un ciclo di
lunghezza 1 sul vertice v. Quando l’esecuzione raggiunge uno stato per cui z
483
La complessità del calcolo

= j e h = t, il cammino (v,v), costituito da un unico lato, viene aggiunto


indefinitamente alla lista dei cammini esistenti.
3.48 fi è in DTIME(/i) poiché la medesima MT che decide L| si può usare per
decidere fi, con alcune leggere modifiche che non influenzano la finizione
di complessità.
£[ u E e L2 sono in DTIME(/i + fi), che è lo stesso di DTIME(/),
dove/n) = max {fin), fin)}.
Nel caso non deterministico, la risposta è la stessa per fi 'U fi e fi n fi,
poiché una macchina che decide l’unione o l’intersezione di due linguaggi
richiede solo la simulazione delle due macchine che decidono Li e fi.
Nel caso di fi, si può dimostrare che, se Li è in NTIME(/|), allora Li è in

DTIME( c ) per un opportuna c, costruendo una MT che enumera tutte le


possibili computazioni delle macchine non deterministiche che riconoscono
fi. .
3.50 b. In una macchina RASP, un’istruzione è il contenuto di qualche cella di
memoria. Quindi può essere modificata come qualsiasi altro valore
contenuto in una cella. Ad esempio, se la cella 10 contiene l’istruzione ADD
13, il risultato dell’esecuzione delle istruzioni
LOAD 10
ADD= 1
STORE 10
è che l’istruzione precedente è modificata in ADD 14. Ciò rende
chiaramente superfluo l’indirizzamento indiretto, almeno dal punto di vista
della potenza computazionale.
c. Se una macchina RASP ha una funzione di complessità temporale T, si
può allora costruire una macchina RAM equivalente che sia 0(7) e
viceversa. Ciò vale per entrambi i criteri di costo. Infatti è facile simulare
l’esecuzione di una singola istruzione RAM mediante una sequenza limitata
di istruzioni RASP, e viceversa. Si noti che, anche se una macchina RASP
deve dapprima leggere il programma per simulare una macchina RAM
qualsiasi, ciò richiede sempre un tempo limitato da una funzione 0(1).

Note bibliografiche
La complessità computazionale è un ramo relativamente giovane dell’informatica
teorica e non è stato ancora adeguatamente formalizzato in una cornice coerente e
sistematica. Mancano ancora molti anelli fondamentali e qualche risultato
matematico non presenta una validità assoluta, richiedendo un’interpretazione
piuttosto attenta. Tuttavia, la letteratura più recente sta facendo importanti
progressi in tutto il settore.
I risultati fondamentali, ormai classici, in questo campo sono presentati negli
scritti di Hartmanis e Stearns (1965) (Teorema dell’Accelerazione), Cook (1973)
(la gerarchia DTIME), Cook e Reckhow (1973) (il modello RAM), e Cook (1971)
484 Informatica teorica

e Karp (1972) (%<P-completezza). La nozione di sequenza di attraversamento e la


sua applicazione per derivare i limiti inferiori di complessità sono dovute a Hennie
(1965). L’equivalenza fra le strutture di controllo delle RAM e di SMALG si può
reperire in Bòhm e Jacopini (1966). La complessità esponenziale del problema
della raggiungibilità per le reti di Petri è stato dimostrato da Lipton (1976).
L’algoritmo polinomiale che verifica la primalità di un numero è stato
pubblicato da Agrawal, Kayal e Saxena(2004).
Più recentemente sono stati ottenuti alcuni risultati interessanti nel campo
della complessità computazionale applicando il concetto di algoritmi e di macchine
probabilistiche. Come già affermato nel Capitolo 3, non si entra in questo
argomento. Alcuni riferimenti, suggeriti al lettore interessato, sono Luecker (1981),
Rabin (1976, 1977), Hocroft (1981) e Uehara (1998).
Karp (1986a, 1986b) fornisce una rassegna storica ed esamina lo stato
dell’arte in questo campo.
Sono stati pubblicati molti testi sulla complessità computazionale: essi si
possono raccomandare per una lettura più avanzata. Si menziona Savage (1976),
Lewis e Papadimitriou (1981), Hopcroft, Ratwani e Ullman (2006), Lewis e
Papadimitriou(1996), Garey e Johnson (1978), e Arora e Barak (2006).
L’applicazione degli strumenti della complessità computazionale all’analisi degli
algoritmi per calcolatore è ben illustrata in Aho, Hopcroft e Ullman (1974) e in van
Leeuwen(1990). Sedgewick(1998) e Cormen(2001) includono una descrizione e
analisi delle strutture dati più dignificative e degli algoritmi più comuni. Harrison
(1978) è specializzato per i linguaggi formali e, in particolare, per quelli non
contestuali. Kolence (1985) affronta molti aspetti della complessità che non sono
stati considerati qui a causa della mancanza di una appropriata formalizzazione.
La presentazione di questo capitolo è stata ispirata da molti dei testi sopra
citati, in particolare da Hopcroft e Ullman (2001) per le Sezione 9.3 e 9.7.

Potrebbero piacerti anche