Dispense per il corso di
Dicembre 2001—Luglio 2003
Fondamenti dell’Informatica:
Linguaggi Formali e Calcolabilità
Agostino Dovier
Dipartimento di Matematica ed Informatica
Università degli Studi di Udine
Via delle Scienze, 206, Loc. Rizzi
33100 Udine, Italy
dovier@dimi.uniud.it
Roberto Giacobazzi
Dipartimento di Informatica
Università degli Studi di Verona
Strada Le Grazie 15
37134 Verona, Italy
giaco@sci.univr.it
1
Contents
Chapter 1. Introduzione
1. Il quadro storico
2. Una prima classificazione
3. Il presente volume
7
7
9
10
Chapter 2. Notazione e concetti di base
1. Insiemi
2. Relazioni e induzione ben fondata
3. Funzioni
4. Cenni di Logica Matematica
5. Cardinalità di insiemi
6. Ordinali
7. Il problema dell’informatica
13
13
14
16
19
20
23
26
Part 1.
29
Linguaggi formali
Chapter 3. Automi a stati finiti
1. Alfabeti e Linguaggi
2. Automi
3. Automi deterministici
4. Automi non-deterministici
5. Equivalenza tra DFA e NFA
6. Automi con ε-transizioni
7. Equivalenza di ε-NFA e NFA
8. Automi con output
31
31
32
33
35
36
37
38
39
Chapter 4. Espressioni regolari
1. Operazioni sui linguaggi
2. Definizione formale
3. Equivalenza tra DFA e ER
41
41
41
42
Chapter 5. Proprietà dei linguaggi regolari
1. Il “Pumping Lemma”
2. Proprietà di chiusura
3. Risultati di decidibilità
47
47
49
50
3
4
CONTENTS
4. Il teorema di Myhill-Nerode
5. Minimizzazione di DFA
51
53
Chapter 6. Grammatiche libere dal contesto
1. Definizione formale
2. Linguaggio generato
3. Alberi di derivazione
4. Ambiguità delle derivazioni
5. Semplificazione
6. Forma normale di Chomsky
7. Forma normale di Greibach
57
57
58
59
60
61
65
65
Chapter 7. Automi a pila
69
Chapter 8. Proprietà dei linguaggi liberi dal contesto
1. Il pumping lemma per i linguaggi CF
2. Proprietà di chiusura
3. Algoritmi di decisione
75
75
76
77
Chapter 9. Le grammatiche regolari e la gerarchia di Chomsky
1. Grammatiche Regolari
2. Grammatiche di tipo 0
3. Grammatiche di tipo 1
4. Gerarchia
79
79
81
81
82
Part 2.
85
Teoria della calcolabilità
Chapter 10. Nozione intuitiva di algoritmo
1. Requisiti di un algoritmo
2. Funzioni calcolabili
3. Algoritmi e Programmi
87
87
89
90
Chapter 11. Macchine di Turing
1. Descrizione modellistica e matematica
2. Funzioni calcolabili da MdT
3. MdT generalizzate
91
91
96
98
Chapter 12. Funzioni parziali ricorsive di Kleene & Robinson
1. Funzioni primitive ricorsive
2. Diagonalizzazione
3. Funzioni parziali ricorsive
4. Equivalenza tra MdT e funzioni parziali ricorsive
99
99
105
107
109
Chapter 13. Tesi di Church-Turing
113
Chapter 14. Aritmetizzazione e universalità
117
CONTENTS
1. Enumerazione delle MdT
2. Macchina di Turing Universale
3. Il Teorema s-m-n
5
117
121
122
Chapter 15. Problemi insolubili
125
Chapter 16. Calcolabilità e Linguaggi di Programmazione
1. Il linguaggio While
2. Strutture dati
3. Sintassi
4. Semantica
5. Espressività di While e Turing completezza
6. For-calcolabilità e funzioni primitive ricorsive
7. Interpreti e Metaprogrammazione
8. Specializzatori e Proiezioni di Futamura
129
129
130
131
131
134
139
141
144
Part 3.
149
Teoria matematica della ricorsione
Chapter 17. Insiemi ricorsivi e ricorsivamente enumerabili
151
Chapter 18. I Teoremi di Ricorsione
1. Il primo Teorema di ricorsione
2. Il secondo Teorema di ricorsione
3. Il Teorema di Rice
4. Proprietà di programmi
161
161
162
163
167
Chapter 19. Riducibilità funzionale e gradi di risolvibilità
1. La relazione di riducibilità
2. Insiemi creativi e produttivi
3. Insiemi semplici
171
172
175
179
Part 4.
181
Complessità Computazionale
Chapter 20. Classi di complessità e principali risultati
1. Problemi, insiemi, linguaggi
2. Classi di complessità in tempo e Tesi di Church computazionale
3. Il non determinismo
4. Una inclusione stretta
5. Complessità in spazio
183
184
185
188
190
192
Chapter 21. Riduzioni e NP-completezza
1. Riduzioni tra problemi
2. I Teoremi di Cook
3. Problemi NP-completi
195
195
197
199
6
Bibliography
CONTENTS
205
CHAPTER 1
Introduzione
’informatica come scienza si fonda sulla teoria della calcolabilità effettiva
L sviluppata
nell’ambito dello studio dei fondamenti della matematica, intorno
agli anni ’30. Tale avventura intellettuale fornisce non solo le basi concettuali
e teoriche dell’informatica, ma anche rappresenta un affascinante viaggio verso i
limiti dell’informatica stessa. Ogni disciplina scientifica si definisce pienamente
nel momento in cui essa viene delimitata da una teoria in grado di evidenziarne i
limiti e le potenzialità. È cosı̀ per la fisica classica e quantistica, per la psicoanalisi, per la chimica etc. Anche l’informatica ha, come ogni scienza, una teoria
che ne definisce in modo universale i limiti e le potenzialità. Tale disciplina, la
cui nascita si può far risalire agli anni ’30 in un effervescente panorama culturale
e scientifico di inizio secolo—sono di quel periodo gli studi sulla materia, sulla
meccanica quantistica e sui fondamenti della matematica—si sviluppa pienamente
indipendentemente dalla realizzazione, avvenuta solo successivamente negli anni
’40, del primo calcolatore elettronico. Possiamo quindi affermare, in modo forse
provocatorio, che il successo attuale dell’informatica realizzata mediante dispositivi elettronici piuttosto che biomolecolari, quantistici o altro, sia stato più un
caso che ha voluto la maturazione contemporanea dell’informatica come scienza e
dell’elettronica intorno agli anni ’40 e ’50, piuttosto che una necessità intrinseca del
processo di calcolo. L’indipendenza da una particolare macchina fisica è uno dei
punti di forza dell’informatica come scienza. L’errore comunemente compiuto di
identificare l’informatica con il computer moderno, o almeno con l’architettura che
conosciamo oggi, ancora basata sulle idee di von Neumann-Turing, limita questa
disciplina ad una mera programmazione di particolari macchine. Al contrario,
l’informatica prescinde dal particolare strumento di calcolo, sia esso il computer
moderno, un insieme di molecole (DNA-computing [24]) o particelle (Quantumcomputing [12]), o sia esso definito da un mero calcolo simbolico (λ-calcolo [3, 11]).
In questo senso l’informatica può definirsi a pieno titolo come una scienza universale dell’informazione, ovvero di come l’informazione possa essere codificata,
manipolata, valutata, analizzata e misurata.
1. Il quadro storico
Questa affascinante avventura intellettuale inizia negli anni ’30 grazie al contributo di eminenti studiosi quali Church, Gödel, Kleene, Post e Turing. Il quadro
7
8
1. INTRODUZIONE
storico-scientifico dell’epoca vede il progressivo sgretolarsi delle teorie classiche del
’700 e ’800, sia in ambito fisico che matematico che medico. Non è un caso che
la scoperta di leggi fisiche nuove che superano le leggi classiche di Newton nello
studio di fenomeni atomici, la relatività di Einstein, la scoperta della psicoanalisi
di Freud come strumento per investigare l’inconscio, e gli studi sui fondamenti
della matematica compiuti da Hilbert, Russell, Weyl, e Gödel, siano tutti riconducibili ad un medesimo quadro storico: gli inizi del 1900. In questo contesto,
si sviluppa in particolar modo la teoria della calcolabilità effettiva. Essa si può
ragionevolmente collocare nell’ambito degli studi sui fondamenti della matematica, in particolar modo dell’aritmetica. La necessità di capire profondamente la
natura di ciò che è effettivamente costruibile mediante una sequenza di passi elementari di calcolo nasce dalla volontà di costruire effettivamente teorie complesse,
quali l’aritmetica. Questo ambizioso programma inizia già nel 1879 con i lavori di
Frege [8] aprendo l’era della ricostruzione logica della matematica culminata con
i Principia Mathematica di Russell. Le prime critiche a questo programma emergono già con la scoperta dei paradossi. Il paradosso di Russell (1902-3) è destinato
a minare alle fondamenta l’impianto teorico su cui si fonda la teoria degli insiemi
di Cantor. Esso riguarda l’insieme di tutti gli insiemi che non sono membri di sé
stessi. Se chiamiamo T questo insieme, si ha che se T ∈ T allora T 6∈ T e analogamente se T 6∈ T allora T ∈ T . Più volgarmente: consideriamo il barbiere di un
villaggio che rade solo coloro del villaggio che non si radono da soli. Il barbiere del
villaggio si rade? Paradossi simili si riscontrano già nell’antica grecia, ad esempio il paradosso del mentitore o di Epimenide si fonda su un simile ragionamento
“circolare”: L’affermazione: I cretesi sono bugiardi, è attribuita ad Epimenide
di Creta VI B.C. È vera questa affermazione? [17] A seguito dell’emergere di
paradossi che minano apparentemente alla base il tentativo di ricostruire in chiave puramente logica l’intera matematica, è emersa la necessità di definire una
assiomatizzazione completa per la matematica, in particolar modo la teoria degli
insiemi su cui essa si fonda. Assiomatizzare una teoria significa definire un insieme
di assiomi universalmente validi ed esenti da contraddizioni per quella teoria ed
essere in grado di derivare i teoremi che ne conseguono a partire da quegli assiomi. Il contributo di Hilbert in questo quadro è fondamentale. Secondo Hilbert,
la formalizzazione di una teoria, mediante la definizione di assiomi e regole di
derivazione, comporta una astrazione dal significato degli oggetti manipolati e la
definizioni di principi e metodi per studiare il sistema formale risultante. Nel
programma di Hilbert è possibile formalizzare in modo completo la matematica,
riducendo la matematica stessa ad un mero calcolo simbolico a partire da una sua
formalizzazione, ovvero da un insieme di assiomi e regole di inferenza. Il risultato fondamentale che apre definitivamente la strada alla nascita dell’informatica
è dovuto a Gödel (1931) [10]. In questo fondamentale lavoro, Gödel dimostra
che esistono teoremi dell’aritmetica che non sono decidibili nell’aritmetica stessa.
In particolare la verità di una formula non risulta dimostrabile in modo effettivo
(ovvero decisa finitamente) nell’aritmetica. L’importanza di questo risultato sta
2. UNA PRIMA CLASSIFICAZIONE
9
nel fatto che esso è del tutto indipendente dal particolare sistema formale scelto,
purché sufficientemente espressivo da rappresentare l’aritmetica stessa. In questo
senso i risultati di incompletezza di Gödel risultano validi per tutti i sistemi formali derivanti da una assiomatizzazione degli insiemi, aggiungendo un qualsiasi
numero finito di assiomi, purché questi non generino inconsistenze. L’impatto di
questo risultato è enorme e di portata universale: Gödel dimostra l’esistenza di
questioni riguardanti i numeri che non sono decidibili nella teoria stessa ed in ogni
sua finita estensione consistente. In questo senso, Gödel stabilisce i limiti stessi
della formalizzazione di teorie quali l’aritmetica, e pone le basi per il successivo
ragionamento su ciò che è effettivamente costruibile. In questo contesto nasce
quindi la necessità di approfondire la natura stessa del calcolo logico-formale e di
come da questo sia possibile derivare in modo sistematico enunciati la cui verità sia
decidibile. Il fallimento quindi dell’idea di ridurre l’intera matematica ad un mero
calcolo automatico a partire da assiomi e regole di inferenza, costituisce il terreno
ideale per lo studio di ciò che effettivamente è calcolabile in questo senso, ovvero
stabilisce i limiti di quella disciplina che oggi si chiama informatica. La necessità
di formalizzare il processo di calcolo, sia mediante la definizione di una macchina
calcolatrice che attraverso sistemi formali di calcolo quali il λ-calcolo, ha portato
all’analisi della calcolabilità di Turing e Church nel 1936 [4, 30]. Dai loro lavori
scaturisce di fatto la teoria presentata in modo didattico nel presente volume e si
definiscono le basi per i successivi sviluppi pratici e teorici del calcolo mediante
calcolatore. Ad esempio è possibile garantire in modo formale l’esistenza di una
macchina universale (calcolatore programmabile) o caratterizzare rigorosamente la
nozione di programma come sequenza di istruzioni, a prescindere dall’esistenza di
una macchina fisica, insieme di circuiti e apparecchiature, in grado di eseguire tali
programmi. Si dimostra l’esistenza di funzioni non calcolabili e, di conseguenza,
l’esistenza di problemi non risolvibili in modo completo e automatico mediante
calcolatore.
2. Una prima classificazione
Una prima classificazione dei problemi tra quelli che possono essere risolti
mediante algoritmi e quelli che non ammettono tale soluzione, è ottenibile applicando il Teorema di Cantor. Vedremo nel seguito, nel capitolo 2, questo teorema
applicato alla determinazione della cardinalità di insiemi di funzioni. Tuttavia,
è possibile darne una dimostrazione del tutto informale, ricorrendo a semplici
nozioni intuitive, senza ricorrere a notazioni più complesse sui numeri. Questa
dimostrazione permette di dare una prima classificazione, piuttosto grossolana,
degli insiemi e distinguere, se non altro per quanto riguarda la loro “dimensione”,
tra i problemi che possono essere definiti matematicamente e quelli che invece
ammettono una soluzione effettiva o algoritmica.
Supponiamo di avere a disposizione un linguaggio per programmare una macchina.
Tale linguaggio è caratterizzato da un insieme finito di istruzioni in grado di far eseguire operazioni più o meno complesse alla macchina. I dati sono semplici numeri
10
1. INTRODUZIONE
naturali. Un algoritmo è quindi una sequenza finita di istruzioni nel linguaggio
della macchina. Secondo queste ipotesi, è quindi possibile mettere in sequenza
tutti i possibili algoritmi, ovvero enumerare gli algoritmi cosı̀ come è possibile
enumerare i numeri naturali:
P0 , P1 , P2 , . . . , Pn , . . .
Se un algoritmo è destinato a manipolare dati in input , producendo dati in output ,
allora un problema è rappresentabile come una funzione ovvero una associazione
tra dati in input e dati in output. Nell’esempio, un problema è quindi una funzione
sui numeri naturali: f : N −→ N. Un problema ammette soluzione algoritmica se
esso è programmabile da un algoritmo, ovvero se esiste un n tale che Pn programma
la funzione del problema: per ogni x ∈ N, Pn (x) = f(x). Costruiamo allora il
seguente problema h : N −→ N:
input: n
determina il programma Pn
calcola Pn (n)
output: Pn (n) + 1
Se il linguaggio è sufficientemente potente per poter programmare il precedente
problema, allora esso corrisponderà ad una sequenza di istruzioni del linguaggio,
ovvero esisterà n0 tale che Pn0 = h. Calcoliamo ora h(n0 ):
h(n0 ) = Pn0 (n0 ) + 1 = h(n0 ) + 1
Questo è un assurdo, poiché nessun numero è uguale al suo successore. Da questo
assurdo segue il fatto che esistono problemi, ovvero funzioni input/output, non
programmabili, ovvero per le quali non è possibile costruire un algoritmo che le
risolva. Le ipotesi molto deboli del precedente ragionamento lo rendono applicabile
a tutti i linguaggi di programmazione noti.
Questa prima osservazione stabilisce già una ripartizione tra i problemi definibili matematicamente come funzioni e quelli effettivamente risolvibili mediante una
macchina calcolatrice. Dimostrazioni come quella appena vista saranno ricorrenti
nel presente volume.
3. Il presente volume
In questo volume presenteremo le principali nozioni e risultati che riguardano
la teoria della calcolabilità effettiva e della ricorsione. La conoscenza di queste
nozioni è indispensabile per intraprendere l’avventura intellettuale che ha portato
alla nascita dell’informatica, per conoscerne i limiti e le potenzialità e per poterne
tracciare i confini nel panorama delle scienze. Studiare la calcolabilità significa
capire se un dato problema è risolvibile mediante calcolatore. Al contrario dello
studio della complessità (cf. [9, 23]), presentato nella parte finale del testo, che
studia le condizioni per cui un dato problema è risolvibile o meno avendo a disposizione una quantità limitata di risorse, siano esse il tempo o la memoria (spazio),
la calcolabilità non pone restrizioni alle risorse disponibili. Capire se un problema
3. IL PRESENTE VOLUME
11
è calcolabile significa capire se esso ammette una soluzione algoritmica indipendentemente da quanto tempo/spazio è necessario per risolverlo. Gli strumenti
utilizzati per studiare la calcolabilità sono per molti aspetti simili a quelli utilizzati per studiare la complessità dei problemi (ad esempio il concetto di riduzione
funzionale è presente nella definizione di completezza sia per classi di ricorsività
che per classi di complessità). In questo senso, la teoria della calcolabilità è di
fatto propedeutica ad ogni altra teoria che studi modelli per il calcolo automatico,
sia essa la complessità, la semantica etc. . . Scopo di questo testo è quello di fornire
i risultati principali che permettono di analizzare un problema, classificandolo in
termini della sua risolvibilità algoritmica. L’approccio seguito in questo testo non
corrisponde allo sviluppo storico dei concetti presentati. Partendo da semplici
macchine a stati finiti, raggiungeremo i confini di ciò che è calcolabile arricchendo
via via le nostre macchine con strutture dati opportune per memorizzare informazioni. Scopriremo poi che l’architettura del computer moderno corrisponde in
tutto e per tutto all’architettura ideale della Macchina di Turing (1937) [30], ottenibile arricchendo semplici automi a stati finiti con opportune strutture dati.
In questo modo arriveremo allo studio astratto (matematico) della calcolabilità
effettiva attraversando la teoria dei linguaggi formali e delle macchine preposte al
loro riconoscimento. Lo studio formale dei linguaggi generabili a partire da un
dato alfabeto è alla base dello sviluppo delle tecniche di traduzione dei moderni
linguaggi di programmazione, quali interpreti e compilatori. Lo studio della complessità fornisce metodologie che permettono al programmatore di stabilire a priori
il grado di difficoltà in termini di tempo di esecuzione e/o consumo di memoria
del problema da risolvere, che rappresenta il limite da raggiungere dall’algoritmo
sviluppato per risolverlo. Tali metodologie e risultati devono costituire, per un
informatico, ma più in generale per chi utilizza professionalmente un calcolatore,
una base di conoscenze indispensabile come le nozioni fondamentali di algebra e
di analisi lo sono per il matematico.
In letteratura vi sono molti ottimi testi sull’argomento (si consulti la Bibliografia). La riorganizzazione a “moduli” di 4–6 crediti (30–50 ore di didattica
frontale a seconda delle scelte della sede) del sistema universitario italiano rende
tuttavia molti di questi testi sovrabbondanti oppure troppo avanzati oppure orientati ad un pubblico con solide basi matematiche (per esempio i testi di HopcroftUllman [13], di Rogers [27] o di Cutland [6]). Si è pertanto cercato di fornire una
presentazione il più possibile adatta a studenti del Corso di Laurea in Informatica
orientata a uno o due possibili corsi di 6 crediti del II–IV anno. Gli unici prerequisiti sono una conoscenza di base della teoria elementare degli insiemi e di logica
che vengono comunque richiamati nel Capitolo 2. Riteniamo pertanto che il testo
possa essere utilizzato in un corso di Fondamenti teorici dell’Informatica nei Corsi
di laurea in Informatica, Matematica, Fisica, Statistica e Ingegneria.
Il testo è diviso in 4 parti, per ciascuna delle quali sono necessarie poco meno
di 30 ore. Complessivamente, pertanto, il testo è complessivamente strutturato
per due corsi di 6 crediti l’uno.
12
1. INTRODUZIONE
Per quanto gli autori comprendano ma non condividano la scelta di un unico
corso di Fondamenti da 6 crediti, ritengono che il testo possa comunque venire
utilizzato anche in tale contesto. Il contenuto di diversi capitoli della Parte 2, ed i
principi generali della parte 4, infatti, vengono talvolta anticipati in altri corsi (per
esempio Programmazione, Linguaggi di Programmazione o Algoritmi e Strutture
Dati). Questo consente di ridurre a meno di 20 ore la parte 2 e, eventualmente, di
omettere la parte 4 o di riassumerne i principali risultati in circa 4 ore. Omettendo
inoltre parte delle dimostrazioni dei paragrafi 6.5, 6.6 e 6.7, la Parte 1 può a sua
volta essere svolta in circa 20 ore. Le ore rimanenti del corso possono essere
impiegate per i principali risultati della parte 3, in particolare per il Capitolo 17.
I Teoremi di ricorsione e la riducibilità funzionale possono eventualmente essere
omessi in un unico corso di Fondamenti di 6 crediti.
Il presente volume è corredato da un relativo eserciziario, con esercizi risolti,
realizzato dalla Dott.ssa Isabella Mastroeni.
Si intende ringraziare gli studenti del corso di laurea in Informatica dell’Università
di Verona per i loro commenti che hanno aiutato a chiarire e correggere alcune parti
di questo testo. Si ringraziano Isabella Mastroeni, Mila Dalla Preda, Carla Piazza,
Andrea Fusiello e Nicola Vitacolonna per le discussioni ed osservazioni relative alla
redazione del presente volume, e tutti gli studenti del III e IV anno del Corso di
Laurea in Informatica dell’Università di Verona, A.A. 1998–99, per il loro attivo
contributo alla realizzazione di questo testo. Un grazie particolare ad Antonella
Meneghetti per l’attenta rilettura delle bozze.
Gli autori saranno grati a tutti coloro che avessero la gentilezza di segnalare
sviste ed errori o volessero comunque far pervenire i loro commenti sul contenuto
e la forma del testo.
Dicembre 2001—Settembre 2003
Agostino Dovier
Roberto Giacobazzi
CHAPTER 2
Notazione e concetti di base
questo breve capitolo viene stabilita la notazione impiegata nel testo relativaI nmente
ai concetti base di matematica e logica utilizzati. Con ciò non si intende
fornire una presentazione auto-contenuta di tali concetti, per la quale si rimanda
ai testi classici, quali ad esempio [7, 20, 16].
1. Insiemi
Con lettere latine maiuscole denoteremo in genere insiemi di oggetti. Alcuni
insiemi hanno un nome particolare che li identifica univocamente: ad esempio N
rappresenta l’insieme dei numeri naturali (N = {0, 1, 2, 3, . . .}).
x ∈ A significa che x è un elemento dell’insieme A. Mediante la rappresentazione intensionale di insiemi {x | E(x)} si identifica l’insieme costituito dagli x
che soddisfano (rendono vera) una data espressione E, la quale dipende da x ed
ha valori booleani. Se gli elementi di un insieme hanno un indice e sono tutti rappresentabili come una sequenza (anche infinita) di elementi xi al variare di i ∈ I,
allora l’insieme costituito da questi oggetti è rappresentato con {xi }i∈I .
A ⊆ B indica che A è un sottoinsieme di B, ovvero che ogni elemento di A
è anche elemento di B. Con la notazione A ⊂ B si denoterà l’inclusione stretta
ovvero vale che A ⊆ B e A 6= B.
Con ℘(A) si denota l’insieme delle parti dell’insieme A, ovvero l’insieme costituito da tutti i suoi sottoinsiemi: ℘(A) = {X | X ⊆ A}.
Unione ed intersezione di insiemi sono rispettivamente denotati con i simboli ∪
e ∩. Il simbolo \ rappresenta la differenza insiemistica: A\B = {x | x ∈ A ∧ x 6∈ B}.
Ā denota il complemento di A, ovvero x ∈ Ā se e solo se x ∈
/ A. Se assumiamo
(come faremo spesso nel seguito) che gli insiemi sono insiemi di numeri naturali,
allora considerando N come insieme universo, si ha che Ā = N \ A.
Dati due o più insiemi, è possibile costruire coppie, triple, etc. di oggetti. In
generale una n-upla di oggetti x1 , . . . , xn di un insieme A è rappresentata con
hx1 , . . . , xn i. L’insieme delle n-uple di elementi di A è rappresentato con An e
corrisponde al prodotto cartesiano di A n-volte. Insiemi diversi possono essere
messi in prodotto cartesiano:
A1 × A2 × · · · × An = {hx1 , . . . , xn i | x1 ∈ A1 , . . . , xn ∈ An }
indica l’insieme delle n-uple di oggetti appartenenti rispettivamente agli insiemi
A1 , . . . , An .
13
14
2. NOTAZIONE E CONCETTI DI BASE
2. Relazioni e induzione ben fondata
Una relazione (binaria) è un sottoinsieme del prodotto cartesiano di (due)
insiemi; dati A e B, R ⊆ A × B è una relazione su A e B. Ad esempio, la relazione
di ordinamento sui numeri naturali “≤” ⊆ N × N è definita nel modo seguente:
≤ = {hx, yi | x ∈ N, y ∈ N, x ≤ y}. Il fatto che hx, yi ∈ ≤ viene solitamente
indicato con x ≤ y. Una relazione binaria su un insieme, R ⊆ S × S, stabilisce
una precedenza tra gli oggetti di S: se ha, bi ∈ R diremo che a precede b. R verrà
spesso chiamata relazione di precedenza. R è detta:
riflessiva: se per ogni a ∈ S si ha che a R a,
simmetrica: se per ogni a, b ∈ S si ha che se a R b allora b R a, e
transitiva: se per ogni a, b, c ∈ S si ha che se a R b e b R c allora a R c.
Una relazione R di equivalenza è una relazione binaria riflessiva, simmetrica e
transitiva. Per ogni relazione R ⊆ S × S, la chiusura transitiva di R è il più piccolo
insieme R∗ tale che: ha, bi ∈ R∗ ∧ hb, ci ∈ R → ha, ci ∈ R∗ . Trattandosi di una
definizione induttiva, la chiusura transitiva di una relazione R è ottenibile come
unione delle seguenti relazioni Ri per i ∈ N:
R1
= R
Ri+1 = ha, ci : ha, bi ∈ Ri ∧ hb, ci ∈ R
S
ovvero R∗ = i∈N Ri . Una relazione binaria R ⊆ S × S è un preordine se essa è
riflessiva e transitiva. R è un ordine parziale se è un preordine ed è antisimmetrica,
ovvero
aRb ∧ bRa → a=b
Useremo nel seguito i simboli ⊑, , ≤, . . . per rappresentare relazioni d’ordine
parziale. Un insieme S con una relazione di precedenza (non necessariamente
una relazione d’ordine) ≺⊆ S × S è rappresentato nel modo seguente: (S, ≺). Se
≺ è una relazione d’ordine parziale, (S, ≺) è un insieme parzialmente ordinato.
(S, ) è un ordinamento totale se, per ogni x, y ∈ S, o x y oppure y x. Sia
(S, ≺) un insieme con relazione di precedenza ≺. L’elemento x ∈ S è minimale
rispetto a ≺ se ∀y ∈ S. y 6≺ x. Gli elementi massimali di un insieme con relazione
di precedenza, quando esistono, sono definiti invertendo la relazione ≺. Ovvero
x ∈ S è massimale rispetto a ≺ se ∀y ∈ S. x 6≺ y.
Esempio 2.1. Sia N l’insieme dei numeri naturali, e ≺ la relazione definita da
def
x ≺ y = y = x + 1. In altri termini, x ≺ y se e solamente se y è il successore di x.
L’unico elemento minimale rispetto a ≺ è 0. Analogamente, possiamo estendere
def
≺ all’insieme Z dei numeri interi stipulando che (x ≺ y = y = x + 1) anche per
i numeri negativi. Non esistono, però, in Z elementi minimali rispetto a ≺. Un
esempio di relazione di precedenza non banale su N è dato invece dalla seguente
definizione di ❁:
def
x ❁ y = x 6= y ∧ divide(x, y)
2. RELAZIONI E INDUZIONE BEN FONDATA
15
dove divide(x, y) esprime il fatto che x è un divisore di y. L’unico elemento minimale di (N, ❁) è 1. Cos’è la chiusura transitiva di ≺?
Una relazione binaria R ⊆ S × S è ben-fondata se per ogni insieme non vuoto
Y ⊆ S esiste z ∈ Y tale che hy, zi 6∈ R per ogni y ∈ Y \ {z}. Se la relazione in
questione è una relazione di ordine parziale , allora è ben-fondata se ogni
insieme non vuoto Y ⊆ S ha un elemento minimale. Se R ⊆ S × S è ben-fondata,
allora si dirà pure che S (o, con abuso di notazione, (S, R)) è un insieme benfondato.
Veniamo all’enunciato del principio di induzione ben fondata, che generalizza
i principi di induzione sui naturali. Sia S un insieme. Il seguente principio di
induzione ben fondata permette di dimostrare proprietà di insiemi generici, ovvero
non solo di numeri naturali, equipaggiati con una relazione ben fondata.
Teorema 2.2 (Induzione Ben Fondata). Sia (S, ≺) un insieme ben fondato e
sia φ(x) una proprietà su S. Allora vale il seguente asserto:
se
allora
∀x ∈ S. ∀y, y ≺ x. φ(y) → φ(x)
∀x ∈ S. φ(x).
Una lettura informale del principio di induzione ben fondata può essere data
come segue: per dimostrare che un asserto φ(x) vale per ogni elemento x di un
insieme ben fondato (S, ≺) è sufficiente dimostrare che φ vale su un generico
elemento x di S, nell’ipotesi che φ valga su ogni elemento y di S che precede
x secondo ≺. Fissato un generico x in S, la formula
∀y, y ≺ x. φ(y)
viene detta ipotesi induttiva rispetto a x.
Si noti che, in pratica, la dimostrazione di un asserto φ basata sul principio di
induzione ben fondata avviene in due passi. Analogamente al caso dell’induzione
sui naturali, il primo passo della dimostrazione viene comunemente detto caso
base, mentre il secondo viene detto caso induttivo.
Caso Base: si dimostra che φ vale su tutti gli elementi di S minimali
rispetto a ≺ (ricordiamo che dato (S, ≺), un elemento x ∈ S si dice
minimale se non esiste nessun elemento y ∈ S che preceda x). Infatti,
se x è minimale, l’insieme degli y ∈ S tali che y ≺ x è vuoto, e dunque
l’ipotesi induttiva rispetto a x si riduce a True. Di conseguenza la formula
(∀y, y ≺ x. φ(y)) → φ(x) si riduce semplicemente a φ(x).
Caso induttivo: detto x un generico elemento di S, non minimale rispetto
a ≺, si dimostra la validità di φ(x) utilizzando, laddove si renda necessaria, l’ipotesi induttiva rispetto a x, ovvero l’ipotesi che φ stessa vale su
tutti gli elementi di S che precedono x.
16
2. NOTAZIONE E CONCETTI DI BASE
Si osservi come il principio di induzione ben fondata enunciato sopra, generalizzi entrambi i principi di induzione matematica sui naturali, ed induzione
completa. Il principio di induzione matematica sui naturali coincide con il principio di induzione ben fondata rispetto all’insieme con precedenza ben fondato
(N \ {0, . . . , n0 − 1}, ≺), dove ≺ è definita come segue:
def
x ≺ y = y = x + 1.
Il principio di induzione completa corrisponde invece al principio di induzione ben
fondata rispetto alla chiusura riflessiva e transitiva di ≺, ovvero rispetto all’insieme
ben fondato (N \ {0, . . . , n0 − 1}, <) (vedi esempio 2.1).
Prima di vedere alcuni esempi di uso del principio di induzione ben fondata,
ne diamo una dimostrazione informale.
Dimostrazione: Sia (S, ≺) un insieme ben fondato e sia φ una proprietà
su S per cui l’ipotesi del teorema di induzione ben fondata è verificata.
Consideriamo l’insieme X = {x | ¬φ(x)}. Quindi, X 6= ∅ se e solo se
esiste un elemento x0 ∈ S per cui non vale φ. Supponiamo per assurdo
che esista x0 ∈ X. Se x0 è minimale abbiamo immediatamente una
contraddizione con la validità del principio di induzione ben fondata,
visto che, come abbiamo appena osservato, tale principio garantisce la
validità di φ su tutti gli elementi minimali di S (Caso Base). Dunque
x0 non è un elemento minimale: deve allora esistere un elemento x1 ∈
S tale che x1 ≺ x0 e x1 6= x0 , per il quale non vale φ. Se cosı̀ non
fosse, infatti, avremmo di nuovo una contraddizione con la validità del
principio di induzione ben fondata, che garantisce la validità di φ su ogni
elemento non minimale x, nell’ipotesi che φ valga su tutti gli elementi
che precedono x. Possiamo ora ripetere questo ragionamento su x1 . Di
nuovo quest’ultimo non può essere un elemento minimale e deve esistere
un elemento di S, sia esso x2 che precede x1 (x2 ≺ x1 e x2 6= x1 ), e su
cui non vale φ, etc. In questo modo possiamo costruire una sequenza
infinita di elementi, tutti distinti tra loro, tali che
· · · ≺ xk ≺ xk−1 ≺ · · · ≺ x2 ≺ x1 ≺ x0
e tali che, per ogni xi nella sequenza, φ(xi ) non vale. Ma, osservando
che la sequenza cosı̀ costruita è una catena decrescente infinita, contraddiciamo l’ipotesi che (S, ≺) sia un insieme ben fondato, ovvero che non
contenga catene decrescenti infinite.
3. Funzioni
Consideriamo una relazione n-aria, ovvero R ⊆ A1 ×· · ·×An . Sia k < n. Una
relazione R n-aria è una funzione di k argomenti se, dati x1 ∈ A1 , . . . , xk ∈ Ak , esiste uno ed un solo zk+1 ∈ Ak+1 , . . . , zn ∈ An tale che hx1 , . . . , xk , zk+1 , . . . , zn i ∈
3. FUNZIONI
17
R. In questo caso, la funzione è definita in A1 × · · · × Ak ed ha valori in
Ak+1 × · · · × An , ovvero è del tipo:
R : A1 × · · · × Ak −→ Ak+1 × · · · × An
Nel caso n = 2 e k = 1, otteniamo la definizione usuale di funzione di un argomento: R : A1 −→ A2 come relazione binaria R ⊆ A1 × A2 . A1 è detto dominio
di R mentre A2 è il suo co-dominio. Mentre una funzione è anche a sua volta
una relazione, il viceversa non vale sempre. È sempre possibile invece associare
una funzione ad una relazione, modificandone il codominio: Sia R ⊆ A × B una
relazione, definiamo fR : A −→ ℘(B) come segue:
fR (x) = y : x R y
Denoteremo come ~x una generica n-upla (vettore) di oggetti. La lunghezza di tale
n-upla si evincerà dal contesto. Una funzione è iniettiva se f(~x1 ) = f(~x2 ) implica
che ~x1 = ~x2 per ogni ~x1 , ~x2 ; è suriettiva se per ogni elemento ~y del codominio (nel
caso sopra Ak+1 ×· · ·×An ) esiste ~x nel dominio (nel caso sopra A1 ×· · ·×Ak ) tale
che f(~x) = ~y; è biiettiva se è iniettiva e suriettiva. Sia f : A −→ B una funzione.
Con f(D) indichiamo l’immagine di D secondo f. L’immagine f(D) è definita per
ogni D ⊆ A come l’insieme:
f(D) = y ∈ B : y = f(x) ∧ x ∈ D
Con f−1 indichiamo l’immagine inversa di f. L’immagine inversa di una funzione
è un insieme cosı̀ definito per ogni D ⊆ B:
f−1 (D) = x ∈ A : f(x) ∈ D
Una funzione può non essere definita su alcuni dei suoi argomenti, ovvero se la
funzione di k argomenti è data dalla relazione R ⊆ A1 × · · · × An , con k < n,
allora possono esistere degli x ∈ Ai argomenti di R (per i ≤ k) tali che nessuna
n-upla h. . . , x, . . .i avente x nella i-esima posizione appartiene a R. In questo caso
diremo che la funzione R è parziale. Al contrario, una funzione sempre definita
su ogni argomento è detta totale. Con la notazione A −→ B si intende l’insieme
delle funzioni totali da A a B.
Nota 2.3. Normalmente si usa parlare di funzioni f : A −→ B e f(x) è definita
per ogni elemento x dell’insieme A (dominio). Ad esempio, la funzione f(x) =
1000 div x, ove div ritorna il quoziente della divisione intera, è definita da A =
N \ {0} a B = N. In questo testo, per mantenere un’analogia con le funzioni
calcolabili mediante un calcolatore (che potrebbe non fornire risultato per certi
valori di input), si preferisce parlare di f : N −→ N e parlare di funzioni parziali.
Nel seguito faremo largo uso di funzioni parziali e totali. Al fine di evitare
confusione, saremo soliti rappresentare le funzioni secondo la notazione usuale:
R : A1 × · · · × Ak −→ Ak+1 × · · · × An
18
2. NOTAZIONE E CONCETTI DI BASE
identificando in questo modo gli insiemi su cui vengono considerati gli argomenti e
gli insiemi su cui la funzione restituisce risultati. In particolare, rappresenteremo
con lettere minuscole latine le funzioni totali; ad esempio:
f : A1 × · · · × Ak −→ Ak+1 × · · · × An
rappresenta una funzione totale di k argomenti, mentre rappresenteremo con lettere minuscole greche le funzioni parziali:
ϕ : A1 × · · · × Ak −→ Ak+1 × · · · × An
rappresenta una funzione parziale di k argomenti. Poiché nell’insieme delle funzioni parziali vi sono anche le funzioni totali, nel caso non vi siano ipotesi sulla
totalità della funzione considerata, rappresenteremo con lettere greche minuscole
una generica funzione.
Per indicare che una funzione ϕ è definita in x, scriveremo ϕ(x) ↓. Analogamente, per indicare che una funzione ϕ non è definita in x, scriveremo ϕ(x) ↑,
o equivalentemente ϕ(x) = ↑. Il simbolo ↑ rappresenta la non definizione della
funzione ϕ. Supponiamo quindi di ammettere come possibile valore della funzione
anche il valore indefinito ↑. Questo ci permette di vedere le funzioni parziali come
funzioni totali del seguente tipo:
ϕ : A1 × · · · × Ak −→ (Ak+1 ∪ {↑}) × · · · × (An ∪ {↑})
Data dunque una generica funzione (parziale o totale) ϕ chiameremo dominio
della funzione ϕ l’insieme su cui ϕ è definita, ovvero l’insieme:
dom(ϕ) = {x | ϕ(x) ↓}
Analogamente chiameremo immagine o range di ϕ l’insieme dei risultati definiti
della funzione, ovvero:
range(ϕ) = {x | ∃z. ϕ(z) = x 6=↑}
Un valore della funzione ϕ è un elemento di range(ϕ).
In genere la definizione di una funzione f(X, Y) = X + Y crea un’associazione
(relazione) tra il nome f della funzione ed il corpo della funzione stessa X + Y.
Eseguire f non significa altro che eseguire il suo corpo. In alcuni contesti è utile
potersi riferire ad una funzione senza essere costretti ad associare ad essa un nome;
il concetto di funzione ha infatti un suo significato a prescindere dal nome che gli
viene assegnato con la definizione. A questo scopo viene introdotta la cosiddetta
λ-notazione. Per esempio la funzione che riceve due argomenti e ne restituisce la
somma può essere definita con: λX, Y . X + Y dove λ è un simbolo speciale, generalmente seguito da una lista di variabili separate da una virgola (i parametri
della funzione). Il corpo della funzione segue il punto: si tratta di un’espressione
in cui possono apparire le variabili introdotte dopo il simbolo λ. Queste variabili
risultano legate all’interno dell’espressione (bound variables), mentre si definiscono libere quelle variabili che appaiono nel corpo e non appaiono dopo il simbolo
lambda.
4. CENNI DI LOGICA MATEMATICA
19
L’applicazione di una funzione definita con la λ-notazione ha la seguente forma:
alla definizione della funzione racchiusa fra parentesi seguono tante espressioni
quanti sono i parametri. Ad esempio: (λX, Y . X + Y)7 4 associa 7 a X e 4 a Y e
restituisce 11.
4. Cenni di Logica Matematica
Un linguaggio del prim’ordine L è costituito da
• un insieme di simboli relazionali o predicativi,
• un insieme di simboli funzionali,
• un insieme di simboli di costante e da
• un insieme di simboli logici.
I simboli logici presenti in ogni linguaggio sono:
• le parentesi “)”, “(” e la virgola “,”
• un insieme numerabile di variabili v0 , v1 , . . .
• un insieme di connettivi: ¬, ∧, ∨, →, ↔
• i quantificatori ∀ (per ogni) ed ∃ (esiste).
Si ricorda che l’implicazione e la doppia implicazione sono superflue in quanto
ϕ → ψ è equivalente a (¬ϕ)∨ψ mentre ϕ ↔ ψ è equivalente a (ϕ → ψ)∧(ψ → ϕ).
Anche uno tra ∨ e ∧ è superfluo (si vedano le leggi di De Morgan più avanti),
mentre uno solo dei due quantificatori è sufficiente. Tuttavia tali operatori, di uso
comune, sono usualmente accettati per fornire maggiore chiarezza alle formule.1
Un termine di un linguaggio del prim’ordine è definito ricorsivamente nel
seguente modo:
(1) una variabile v è un termine;
(2) un simbolo costante c è un termine;
(3) se t1 , . . . , tm sono termini e f è un simbolo funzionale m-ario, allora
f(t1 , . . . , tm ) è un termine.
Se p è un simbolo relazionale n-ario di L e se t1 , . . . , tn sono termini allora
p(t1 , . . . , tn ) è una formula atomica (o atomo). Una formula di un linguaggio del
prim’ordine è definita ricorsivamente nel seguente modo:
(1) una formula atomica è una formula;
(2) se ϕ è una formula allora (¬ϕ) è una formula;
(3) se ϕ e ψ sono formule, anche (ϕ ∨ ψ), (ϕ ∧ ψ) e (ϕ → ψ) sono formule;
(4) se ϕ è una formula e v è una variabile allora ∀v(ϕ) e ∃v(ϕ) sono formule.
Per semplificare la notazione, si assumono le comuni convenzioni per eliminare,
qualora ciò non generasse ambiguità, alcune coppie di parentesi dalle formule.
Il significato (la semantica) di una formula del prim’ordine è stabilito qualora
venga fornita una interpretazione, associata ad un insieme non vuoto detto dominio
(ad esempio, l’insieme dei numeri naturali N)
1 Un
solo operatore, ad esempio il nor definito come ∨(ϕ, ψ) = ¬(ϕ ∨ ψ) è in realtà sufficiente, in quanto ¬ϕ = ∨(ϕ,ϕ) e ϕ ∨ ψ = ∨(∨(ϕ,ψ), ∨(ϕ,ψ)).
20
2. NOTAZIONE E CONCETTI DI BASE
• per i simboli di costante (ad esempio, la costante c è il numero 5, d il
numero 10),
• per i simboli di funzione (ad esempio, il simbolo funzionale binario f sta
a significare il ‘+’ tra numeri interi) e
• venga assegnata un’interpretazione ai predicati sul dominio, ovvero, in
parole povere, un valore di verità (vero oppure falso) agli atomi costruiti
con i simboli predicativi e gli oggetti del dominio (ad esempio, f(5, 5) = 10
è vero).
Fissata una interpretazione per un linguaggio del prim’ordine, usando la semantica degli operatori, è possibile estendere l’interpretazione ad ogni formula chiusa
(in cui ogni variabile che vi occorre è quantificata) del linguaggio. Ad esempio,
nell’interpretazione accennata sopra, la formula ∃v(v = f(c, c)) è vera in quanto
per v = 10 è soddisfatta, mentre la formula ∀v(v = f(c, c)) è falsa (per esempio, si
prenda v = 11).
Per quanto concerne l’utilizzo della logica del prim’ordine nel prosieguo, sarà
fondamentale saper negare una formula logica, ovvero data una formula ϕ, scrivere
in modo equivalente ma più leggibile la formula ¬ϕ.
Relativamente alle formule che non hanno quantificatori come simboli più
“esterni”, le regole da utilizzare sono le seguenti:
Doppia negazione: ¬(¬ϕ) = ϕ
De Morgan 1: ¬(ϕ ∧ ψ) = (¬ϕ) ∨ (¬ψ)
De Morgan 2: ¬(ϕ ∨ ψ) = (¬ϕ) ∧ (¬ψ)
Si osservi in particolare che¬(ϕ → ψ) è equivalente a ϕ∧¬ψ. Per quanto riguarda
la semplificazione della negazione di quantificatori valgono:
• ¬∃ϕ = ∀¬ϕ
• ¬∀ϕ = ∃¬ϕ
Pertanto, ad esempio,
¬∀n∃y(r(y) ∧ y > n ∧ ∃u∃v(y = u + v ∧ ∀i(r(u + vi ))))
ove r è un simbolo unario di predicato, risulta essere:
∃n∀y(¬r(y) ∨ y ≤ n ∨ ∀u∀v(y 6= u + v ∨ ∃i(¬r(u + vi ))))
Esplicitando le implicazioni, quest’ultima diventa:
∃n∀y((r(y) ∧ y > n) → ∀u∀v(y = u + v → ∃i(¬r(u + vi ))))
5. Cardinalità di insiemi
Nel seguito, se S è un insieme, rappresentiamo la sua cardinalità con il simbolo
|S|. In questa sezione richiamiamo i concetti di base in relazione alla cardinalità
di insiemi. Diciamo che due insiemi A e B sono equipotenti se esiste una funzione
biiettiva f : A −→ B. Se A e B sono equipotenti, scriveremo A ≈ B. |A| ≤ |B|
se esiste una funzione iniettiva f : A −→ B. Si osservi che la funzione f stabilisce una corrispondenza tra gli elementi dei due insiemi. L’iniettività assicura
5. CARDINALITÀ DI INSIEMI
21
che la corrispondenza è stabilita elemento per elemento, mentre la suriettività assicura che la quantità degli oggetti nei due insiemi coincide. Per insiemi finiti, la
cardinalità sarà quindi un numero naturale, corrispondente al numero di oggetti
contenuti nell’insieme. Per insiemi infiniti invece, dobbiamo ricorrere ad una generalizzazione. Più formalmente quindi |A| rappresenta la collezione degli insiemi Y
tale che Y ≈ A. Tale collezione è detta appunto cardinalità di A. È evidente che
se A ⊆ B allora |A| ≤ |B|.
Nel seguito saremo particolarmente interessati ad insiemi la cui cardinalità è
infinita e numerabile, ovvero che possono essere enumerati, stabilendo un primo,
secondo, terzo, etc. elemento. Questo interesse è giustificato dal fatto che i dati
manipolati in informatica sono enumerabili, ovvero è possibile metterli in corrispondenza biunivoca con i numeri naturali, essendo essi rappresentati da numeri.
Un insieme A è detto enumerabile se esso è equipotente all’insieme dei numeri naturali N: A ≈ N. La cardinalità degli insiemi infiniti enumerabili è comunemente
denotata ℵ0 . Un insieme A è finito se |A| < ℵ0 . Un insieme A è numerabile se
|A| = ℵ0 .
Il seguente teorema ha come conseguenza che esistono insiemi non enumerabili.
Teorema 2.4 (Cantor). Sia S un insieme. |S| < |℘(S)|.
Proof.
Per assurdo assumiamo esista una funzione f : S −→ ℘(S) biiettiva.
Sia A = x ∈ S : x ∈
/ f(x) . Poiché A ∈ ℘(S) e f è per ipotesi suriettiva, deve
esistere a ∈ S tale che f(a) = A. Ora, chiediamoci se a appartenga o meno ad A:
• se a ∈ A allora, per definizione di A, a ∈
/ f(a) = A;
• se a ∈
/ A = f(a) allora, per definizione di A, a ∈ A.
In entrambi i casi si giunge ad una contraddizione: l’unica ipotesi che abbiamo
fatto è quella dell’esistenza di una f biiettiva, che pertanto non può essere vera.
Come conseguenza immediata si ha che: |N| < |℘(N)| e che per ogni insieme S,
|S| < |S −→ {0, 1}|. Questo segue dal fatto che è possibile definire un isomorfismo
c : ℘(S) −→ (S −→ {0, 1}) tale che
1 se a ∈ X
c(X)(a) =
0 se a 6∈ X
Inoltre, dal teorema di Cantor segue che è possibile definire una successione di
insiemi di cardinalità sempre maggiore:
|N| < |℘(N)| < |℘(℘(N))| < |℘(℘(℘(N)))| < · · ·
Il seguente teorema stabilisce invece una relazione tra la cardinalità dell’insieme
dei numeri reali R e la cardinalità delle funzioni sui naturali.
Teorema 2.5. |R| = |℘(N)|.
22
2. NOTAZIONE E CONCETTI DI BASE
Traccia della Dimostrazione. Si dimostra prima l’equicardinalità tra |℘(N)|
e l’intervallo [0, 1) di R. La rappresentazione in aritmetica binaria di ogni elemento
dell’intervallo [0, 1) è costituita da una sequenza infinita di bits della forma:
0.b0 b1 b2 b3 b4 b5 . . .
con bi ∈ {0, 1}. Ad esempio “0.0000 . . . ” rappresenta lo 0, “0.1000 . . . ” rappresenta
1
2 , e cosı̀ via. Ogni sequenza b0 b1 b2 . . . è inoltre il grafico di una funzione c(X)
di quelle descritte sopra che identifica un insieme X ⊆ N e ogni funzione di quel
tipo è rappresentabile in questo modo. Esiste pertanto una biiezione tra |℘(N)| e
l’intervallo [0, 1).
Per completare la dimostrazione bisogna mostrare che l’intervallo [0, 1) è equipotente a tutto R. Questa parte è lasciata per esercizio (si suggerisce di leggere prima
l’intera pagina seguente).
Le cardinalità di insiemi si possono sommare e moltiplicare tra loro. Intuitivamente, la somma della cardinalità di due insiemi A e B è data dalla cardinalità
della loro unione A ∪ B. Analogamente, il prodotto della cardinalità di due insiemi
A e B è data dalla cardinalità dell’insieme prodotto A × B:
|A| + |B| = |A ∪ B|
|A| · |B| = |A × B|
È importante osservare che coppie, triple, etc. di numeri sono anch’esse numerabili,
ovvero vale il seguente risultato:
Teorema 2.6.
(1) ℵ0 + ℵ0 = ℵ0
(2) ℵ0 · ℵ0 = ℵ0
(3) ∀n ∈ N. ℵn
0 = ℵ0 .
Proof. Dimostrare il punto (1) per esercizio. Per quanto riguarda il punto
(2), consideriamo l’insieme N × N. Per questo insieme vogliamo trovare una enumerazione, ovvero un insieme S = hni , mi i : ni , mi , i ∈ N tale che S ≈ N. Ordiniamo N × N nel modo seguente: Il primo elemento è la coppia hn0 , m0 i = h0, 0i.
Quindi ordiniamo le coppie hn, mi tali che ni + mj = 1 in base alla prima componente: hn1 , m1 i = h0, 1i, hn2 , m2 i = h1, 0i. Quindi in modo analogo ordiniamo
le coppie hn, mi tali che ni + mj = 2: hn3 , m3 i = h0, 2i, hn4 , m4 i = h1, 1i,
etc. È facile vedere che la funzione ι : N −→ N × N definita nel modo seguente:
ι(i) = hni , mi i è un isomorfismo tra N e N × N. La seguente funzione è l’inversa
di ι [dimostrare per esercizio] ;
δ(n, m) = n +
n+m+1
X
(ℓ + 1) = n +
ℓ=0
Il punto (3) segue per induzione dal punto (2).
(n + m)(n + m + 1)
2
6. ORDINALI
23
Abbiamo visto che ℵ0 corrisponde alla cardinalità degli insiemi enumerabili
infiniti, ovvero dei numeri naturali. Inoltre, ∀n ∈ N. ℵn
0 = ℵ0 e, per il Teorema
di Cantor: ℵ0 = |N| < |℘(N)| = |R|. Ci chiediamo quindi se |℘(N)| è la più piccola
cardinalità non enumerabile. Questa congettura è nota con il nome di ipotesi del
continuo:
Ipotesi del continuo (CH): Ogni sottoinsieme di R è o numerabile o di cardinalità |℘(N)|.
Assumendo valida l’ipotesi del continuo2 , è quindi possibile affermare che un insieme A è non-numerabile se |A| ≥ |℘(N)| > ℵ0 .
6. Ordinali
In questo paragrafo parleremo del concetto di (insieme) ordinale. Mentre
i cardinali sono introdotti per misurare in qualche modo il numero di elementi
di un dato insieme, gli ordinali sono introdotti per confrontare tra loro insiemi
bene ordinati. Gli ordinali sono introdotti anche per poter definire un’aritmetica
all’interno della teoria degli insiemi.
Un insieme x si dice transitivo se e solo se ogni elemento di x è sottoinsieme
di x. In breve, se ∀y ∈ x.∀z ∈ y.z ∈ x. Per esempio:
• ∅, {∅}, {∅, {∅}} sono transitivi.
• {{∅}} non è transitivo.
• Se Ω è un insieme tale che Ω = {Ω}, allora Ω è transitivo.
Si noti però che in quest’ultimo insieme l’appartenenza non è una relazione ben
fondata su di esso (vale Ω ∈ Ω che permette di generare catene discendenti infinite
di appartenenza). Questo insieme è un insieme non ben fondato [1].
Un insieme x è un ordinale se x è transitivo e l’appartenenza è ben fondata su
x (ovvero, (x, ∈) è ben fondato). Per esempio:
• ∅, {∅}, {∅, {∅}} sono ordinali.
• {{∅}} e Ω non sono ordinali.
Teorema 2.7 (Paradosso Burali-Forti). Non esiste l’insieme ON = x :
x è un ordinale .
La dimostrazione di questo teorema è lasciata per esercizio. La parte tecnica
è quella di dimostrare che se ON esistesse, allora sarebbe un ordinale. Ottenuto
ciò il paradosso emerge immediatamente.
Dati due insiemi con una relazione (A, R) e (B, S), un isomorfismo tra (A, R) e
(B, S) è una funzione biiettiva f : A −→ B tale che ∀x ∈ A.∀y ∈ A.x R y ↔ f(x) S
f(y). Se esiste un isomorfismo tra (A, R) e (B, S), diremo che sono isomorfi e lo de∼ (B, S). Quando X è un ordinale, scriveremo semplicemente
noteremo con (A, R) =
X in luogo di (X, ∈).
2 L’indipendenza dell’ipotesi del continuo dalla formulazione classica della teoria degli insiemi
ZF, è dovuta a Gödel e Cohen. Rimandiamo a [16] per maggiori dettagli su questa affascinante
branca della moderna teoria degli insiemi.
24
2. NOTAZIONE E CONCETTI DI BASE
Vale il seguente:
Teorema 2.8. Se (A, R) è un buon ordine, allora esiste un unico ordinale C
∼ C.
tale che (A, R) =
Tale unico ordinale viene indicato come type(A, R) e vuole rappresentare una
misura di quel buon ordine. Gli ordinali sono tutti basati su insiemi puri (ovvero
∅, {∅, {∅}} etc.) e sulla relazione di appartenenza e sono confrontabili tra loro,
come vedremo nel seguito. Il poter assegnare univocamente un ordinale ad ogni
buon ordine permette pertanto di confrontare buoni ordini basati su insiemi qualunque
(per esempio {mario, luigi, . . . }) e su relazioni d’ordine ben fondate qualunque (per
esempio l’ordine lessicografico tra i codici fiscali).
Più precisamente, dati due ordinali α e β, diciamo che α < β se α ∈ β;
diciamo che α ≤ β se α < β oppure α = β.
Se X è un insieme
di ordinali, definiamo come sup(X) l’unione unaria di X
ovvero l’insieme z : ∃y ∈ X.z ∈ y . Si può dimostrare che sup(X) è il minimo
ordinale maggiore o uguale a tutti gli elementi di X.
Se α è un ordinale, allora l’ordinale successore di α è definito come S(α) =
α ∪ {α}. Per ogni ordinale α si ha che S(α) è un ordinale. β è detto un ordinale
successore se esiste un ordinale α tale che β = S(α). α è invece un ordinale limite
se è un ordinale diverso da ∅ e non è un ordinale successore.
A partire da queste nozioni siamo in grado di definire alcuni ordinali familiari:
0
def
=
∅
1
def
S(0) = ∅ ∪ {∅} = {∅} = {1}
2
def
3
..
.
def
=
..
.
S(2) = {∅, {∅}, {{∅, {∅}}} = {0, 1, 2}
..
.
n+1
=
S(n) = n ∪ {n} = {0, 1, . . . , n}
=
=
S(1) = {∅} ∪ {{∅}} = {∅, {∅}} = {0, 1}
Questa successione corrisponde alla successione dei numeri naturali. Si osservi
che la definizione di < data sopra per gli ordinali è consistente con quella usuale
sui numeri naturali. In teoria degli insiemi (p. es., [18]) si definiscono i numeri
naturali esattamente in questo modo: un ordinale α è un numero naturale se
∀β ≤ α.(β = ∅ ∨ β è un ordinale successore).
Per quanto riguarda gli ordinali limite, il primo ordinale limite è ω definito
come ω = sup α : α è un naturale ovvero
ω = 0 ∪ 1 ∪ 2 ∪ · · · = {0, 1, 2, . . . }
Abbiamo visto come gli insiemi ordinali permettano di definire in qualche
modo i numeri naturali e la relazione d’ordine <. Vedremo ora alcune nozioni di
aritmetica degli ordinali.
6. ORDINALI
25
Dati due ordinali α e β l’ordinale somma α + β è l’ordinale
type(α × {0} ∪ β × {1}, R)
dove R è la relazione:
R =
hhx, 0i, hy, 0ii : x < y < α ∪
hhx, 1i, hy, 1ii : x < y < β ∪
((α × {0}) × (β × {1}))
A parole, gli elementi di α e β vengono messi in sequenza. Prima tutti quelli di
α, poi quelli di β. Ad esempio, 2 + 3 risulta il type dell’insieme costituito dalle
coppie (ordinate da sinistra a destra):
{h0, 0i, h1, 0i, h0, 1i, h1, 1i, h2, 1i}
ovvero all’ordinale 5 = {0, 1, 2, 3, 4} ad esso isomorfo. Dunque, 2 + 3 fa proprio 5
come si sperava avvenisse.
Per l’aritmetica ordinale valgono le seguenti proprietà:
Teorema 2.9.
(1) α + (β + γ) = (α + β) + γ
(2) α + 0 = α
(3) α + 1 = S(α)
(4) α + S(β) = S(α + β)
(5) Se β è ordinale limite, allora α + β = sup α + ξ : ξ < β .
Si osservi però che il + non è in generale commutativo (lo è, per fortuna, sui
numeri naturali). Studiamo infatti chi sono 1 + ω e ω + 1.
• 1 + ω è il type dell’insieme: {0} × {0} ∪ ω × {1} ovvero:
{h0, 0i, h0, 1i, h1, 1i, h2, 1i, h3, 1i, . . . }
che altri non è se non ω stesso. Dunque 1 + ω = ω.
• ω + 1 è il type dell’insieme: ω × {0} ∪ {0} × {1} ovvero:
{h0, 0i, h1, 0i, h2, 0i, h3, 0i, h4, 0i, . . . , h0, 1i}
Si tratta di un ordine diverso da ω. Vi è prima una quantità numerabile
di elementi ordinati come ω. Poi c’è un unico elemento che sta sopra
tutti loro. L’ordinale corrispondente è S(ω) = ω ∪ {ω}.
Studiamo ora la moltiplicazione tra ordinali. Dati due ordinali α e β l’ordinale
prodotto α · β è l’ordinale
type(β × α, R)
dove R è la relazione d’ordine lessicografico su β × α, ovvero:
hx, yi R hx ′ , y ′ i sse x < x ′ ∨ (x = x ′ ∧ y < y ′ )
26
2. NOTAZIONE E CONCETTI DI BASE
In parole povere, viene ricopiato α tante volte quante β. Ad esempio, 2 · 3 risulta
il type dell’insieme costituito dalle coppie (ordinate da sinistra a destra):
{h0, 0i, h0, 1i, h1, 0i, h1, 1i, h2, 0i, h2, 1i}
ovvero all’ordinale 6 = {0, 1, 2, 3, 4, 5} ad esso isomorfo. Dunque, 2 · 3 fa proprio 6
come si sperava avvenisse.
Per la moltiplicazione valgono le seguenti proprietà:
Teorema 2.10.
(1) α · (β · γ) = (α · β) · γ
(2) α · 0 = 0
(3) α · 1 = α
(4) α · S(β) = α · β + α
(5) α · (β + γ) = α · β + α · γ
(6) Se β è ordinale limite, allora α · β = sup α · ξ : ξ < β .
Anche in questo caso la commutatività vale solo per i naturali. Studiamo 2 · ω
e ω · 2.
• 2·ω è il type dell’insieme: ω × {0, 1} ordinato lessicograficamente, ovvero:
{h0, 0i, h0, 1i, h1, 0i, h1, 1i, h2, 0i, h2, 1i, . . . }
che altri non è se non ω stesso. Dunque 2 · ω = ω.
• ω · 2 è invece il type dell’insieme: {0, 1} × ω ovvero:
{h0, 0i, h0, 1i, h0, 2i, h0, 3i, h0, 4i, . . . , h1, 0i, h1, 1i, h1, 2i, h1, 3i, h1, 4i, . . . , }
Si tratta di un ordine diverso da ω. Vi è prima una quantità numerabile
di elementi ordinati come ω. Poi c’è un un elemento che sta sopra tutti
loro (h1, 0i) ed infine una quantità numerabile
di elementi sopra ad esso.
L’ordinale corrispondente è ω · 2 = sup ω + n : n < ω .
Non è da confondere la nozione di ordinale con quella di cardinalità. Ad
esempio, è facile osservare in base al Teorema 2.6, che |ω · 2| = |ω + 1| = |ω| =
ℵ0 . Tuttavia, vista la corrispondenza tra il primo ordinale limite infinito ω e
l’insieme dei numeri naturali N, faremo abuso di notazione identificando ω con
ℵ0 . Pertanto, scriveremo |A| = ω per affermare che A è un insieme infinito
enumerabile.
7. Il problema dell’informatica
Abbiamo visto come la cardinalità dell’insieme delle funzioni sui naturali sia
strettamente superiore alla cardinalità dei numeri naturali stessi (Teorema 2.4).
Già questo risultato permette di stabilire una prima distinzione tra ciò che possiamo formulare come funzione (problema) e ciò che possiamo dare come algoritmo
(soluzione algoritmica del problema).
Se pensiamo che i programmi di un calcolatore siano tutti rappresentabili come
sequenze finite di istruzioni date in un determinato linguaggio Σ, allora l’insieme
dei programmi che possiamo scrivere è equipotente all’insieme dei numeri naturali.
7. IL PROBLEMA DELL’INFORMATICA
27
Infatti, se Σ è l’alfabeto con cui possiamo scrivere i nostri programmi, allora un
programma non è altro che una sequenza finita di simboli di Σ:
s1 s2 s3 . . . sn
con n ∈ N. Gli algoritmi o programmi che possiamo descrivere sono quindi tanti
quante le sequenze finite di simboli di Σ. Chiamiamo Alg l’insieme degli algoritmi
che possiamo descrivere in un dato linguaggio. La seguente funzione stabilisce una
biiezione tra algoritmi e numeri:
π : Alg −→ N
P
n−i
dove, se P ∈ Alg e P = s1 s2 s3 . . . sn , allora π(P) = n
dove p(si ) è
i=1 p(si ) · 2
il numero primo associato in modo univoco al simbolo si .
Se supponiamo inoltre che i dati manipolabili dai programmi siano anch’essi
numerabili, ovvero siano numeri, o sequenze di simboli in un alfabeto Σ ′ (ad esempio immagini come sequenze di bytes che rappresentano il colore, la luminosità ed
il contrasto per ogni pixel dell’immagine) allora un problema di tipo informatico
è descrivibile come una funzione sui dati D:
f : D −→ D
che associa ad ogni dato in input un dato in output. Come nel ragionamento sopra
si ha che: |D| = |N|. Ne consegue dal Teorema di Cantor che l’insieme dei problemi
è equipotente all’insieme delle funzioni D −→ D che in cardinalità è strettamente
maggiore dell’insieme di tutti i possibili programmi che possiamo scrivere in un
dato alfabeto:
|D −→ D| > |Alg|
In particolare, ed in modo un po’ provocatorio [22], possiamo affermare che, come
l’analisi, lo studio della ricorsività coincide con lo studio di |D −→ D|, ovvero di
un insieme equipotente ai numeri reali. I successivi capitoli permetteranno quindi
di appropriarci degli strumenti per analizzare tale insieme ed in particolare la
relazione tra problemi e loro soluzioni algoritmiche (programmi).
Part 1
Linguaggi formali
CHAPTER 3
Automi a stati finiti
capitolo sarà presentato il concetto di automa a stati finiti. Si partirà
I ndalquesto
formalismo più semplice (automi a stati finiti deterministici) per poi presentare diverse tipologie di automa, che si dimostreranno essere tutte equivalenti
dal punto di vista delle potenzialità del loro utilizzo, ovvero del riconoscimento di
un dato linguaggio.
1. Alfabeti e Linguaggi
Un simbolo è un’entità primitiva astratta che non sarà definita formalmente
(come punto, linea, etc.). Lettere e caratteri numerici sono esempi di simboli.
Una stringa (o parola) è una sequenza finita di simboli giustapposti (uno dietro
l’altro). Ad esempio, se a, b, c sono simboli, abcba è una stringa.
La lunghezza di una stringa w, denotata come |w|, è il numero di occorrenze di
simboli che compongono una stringa. Ad esempio, |abcba| = 5. La stringa vuota,
denotata con ε è la stringa costituita da zero simboli: |ε| = 0.
Sia w = a1 · · · an una stringa. Ogni stringa della forma:
• a1 · · · aj , con j ∈ {1, . . . , n} è detta un prefisso di w;
• ai · · · an , con i ∈ {1, . . . , n} è detta un suffisso di w;
• ai · · · aj , con i, j ∈ {1, . . . , n}, i ≤ j, è detta una sottostringa di w;
• ε è sia prefisso che suffisso che sottostringa di w.
Si osservi che se n = 0, allora w = ε. Dunque ε è sia prefisso che suffisso di ε.
Ad esempio, i prefissi di abc sono ε, a, ab, e abc. I suffissi sono ε, c, bc, e abc.
Le sottostringhe sono:
ε
a ab
b
abc
bc
c
Un prefisso, un suffisso o una sottostringa di una stringa, quando non sono la
stringa stessa, sono detti propri.
Esercizio 3.1. Sia data una stringa w di lunghezza |w| = n. Quanti sono i
suoi prefissi, i suoi suffissi e le sue sottostringhe?
31
32
3. AUTOMI A STATI FINITI
La concatenazione di due stringhe v e w è la stringa vw che si ottiene facendo
seguire alla prima la seconda. La concatenazione è una operazione (associativa)
che ammette come identità la stringa vuota ε.
Un alfabeto Σ è un insieme finito di simboli. Un linguaggio formale (in breve
linguaggio) è un insieme di stringhe di simboli da un alfabeto Σ. L’insieme vuoto
∅ e l’insieme {ε} sono due linguaggi formali di qualunque alfabeto. Con Σ∗ verrà
denotato il linguaggio costituito da tutte le stringhe su un fissato alfabeto Σ.
Dunque
Σ∗ = {a1 · · · an : n ≥ 0, ai ∈ Σ}
Ad esempio, se Σ = {0}, allora Σ∗ = {ε, 0, 00, 000, . . . }; se Σ = {0, 1}, allora Σ∗ =
{ε, 0, 1, 00, 01, 10, 11, 000, . . . }.
Esercizio 3.2. Si provi che se Σ 6= ∅, allora Σ∗ è numerabile. Definendo
inoltre:
Σ0 = {ε}
Σn+1 = {ax : a ∈ Σ, x ∈ Σn }
S
si osservi che Σ∗ = i≥0 Σi . Qual è la cardinalità di Σi ?
2. Automi
Un automa a stati finiti è un modello matematico di un sistema avente input,
ed eventualmente output, a valori discreti. Il sistema può essere in uno stato tra un
insieme finito di stati possibili. L’essere in uno stato gli permette di tener traccia
della storia precedente. Un buon esempio di automa a stati finiti è costituito
dall’ascensore: l’input è la sequenza di tasti premuti, mentre lo stato è il piano in
cui si trova.
Un automa a stati finiti si può rappresentare mediante una testina che legge,
spostandosi sempre nella stessa direzione, un nastro di lunghezza illimitata contenente dei simboli. La testina si può trovare in un certo stato; a seconda dello
stato q e del simbolo si letto, la testina si porta in un altro stato (o rimane nello
stesso) e si sposta a destra per apprestarsi a leggere il simbolo sucessivo:
s1
s2
s3
···
⇑
q
Quando la lettura dei simboli termina, a seconda dello stato raggiunto dalla testina,
l’automa fornisce un risultato di accettazione o di refutazione della stringa (parola)
letta.
Il comportamento dell’automa si definisce in maniera univoca mediante una tabella,
detta matrice di transizione, come ad esempio:
3. AUTOMI DETERMINISTICI
a
b
q0
q1
q2
q1
q1
q0
q2
q1
q0
33
Un’automa siffatto si rappresenta bene anche con un grafo della forma seguente:
✛ ✘
★✥
✤✜
a
q1 ✛ ✙
✒ ✧✦
✣✢
❅
■
a
❅
b
★✥
★✥
❅
✠
b
✲ q2
q0 ✛
✧✦
✧✦ b
a
Nel prossimo paragrafo si fornirà una definizione formale del concetto ora esposto,
necessaria per una trattazione precisa dell’argomento.
Nota 3.3. Il fatto che, a differenza delle MdT (Capitolo 11.1), la testina non
possa produrre degli output (eventualmente sul nastro) non è essenziale (si vedano,
ad esempio, le macchine di Moore e di Mealy (Capitolo 8 e [13]). Intuitivamente,
anche se scrivesse qualcosa sul nastro, non lo potrebbe più riutilizzare.
Neppure il permettere la bidirezionalità aumenterebbe di fatto la potenzialità del
dispositivo (two-way automata [13]). Intuitivamente, la testina andrebbe su e giù
ma sempre sugli stessi dati e con un controllo finito.
E’ la somma delle due caratteristiche che permette di passare da questo formalismo
al più potente formalismo di calcolo costituito dalla MdT.
3. Automi deterministici
Un automa a stati finiti deterministico (DFA) è una quintupla hQ, Σ, δ, q0 , Fi
dove:
•
•
•
•
•
Q è un insieme finito di stati;
Σ è un alfabeto (alfabeto di input);
δ : Q × Σ −→ Q è la funzione di transizione;
q0 è lo stato iniziale;
F ⊆ Q è l’insieme degli stati finali.
34
3. AUTOMI A STATI FINITI
Notazione 3.4. Useremo p, q, r con o senza pedici per denotare stati, P, Q, R, S
per insiemi di stati, a, b con o senza pedici per denotare simboli di Σ, x, y, z, u, v, w
sempre con o senza pedici per denotare stringhe.
Dalla funzione δ si ottiene in modo univoco la funzione ^δ : Q × Σ∗ −→ Q nel modo
seguente:
^δ(q, ε) = q
δ(q,
^ wa) = δ(^δ(q, w), a)
Una stringa x è detta essere accettata da un DFA M = hQ, Σ, δ, q0 , Fi se ^δ(q0 , x) ∈
F. Il linguaggio accettato da M, denotato come L(M) è l’insieme delle stringhe
accettate, ovvero:
L(M) = {x ∈ Σ∗ : ^δ(q0 , x) ∈ F} .
Un linguaggio L è detto regolare se è accettato da qualche DFA, ovvero se esiste
M tale che L = L(M).
Esempio 3.5. ∅ e Σ∗ sono linguaggi regolari. Sia Σ = {s1 , . . . , sn }: un automa
M0 che riconosce il linguaggio ∅ (ovvero: nessuna stringa è accettata) è il seguente:
q0
s1
...
sn
q0
...
q0
ove F = ∅. Infatti, poiché ∀x x ∈
/ ∅, si ha che:
(∀x ∈ Σ∗ )(^δ(q0 , x) ∈
/ F) .
Un automa per Σ∗ , è invece l’automa M1 :
q0
s1
...
sn
q0
...
q0
ove F = {q0 }. Si dimostra facilmente infatti, per induzione su |x| che
(∀x ∈ Σ∗ )(^δ(q0 , x) = q0 ) .
Esercizio 3.6. Si determini il linguaggio accettato dai seguenti automi rapresentati mediante la matrice di transizione (in tutti F = {q1 }).
(1)
0
1
q0
q1
q2
q1
q1
q1
q2
q1
q0
4. AUTOMI NON-DETERMINISTICI
35
(2)
0
1
q0
q2
q0
q1
q2
q0
q2
q1
q2
Esercizio 3.7. Si verifichi che i seguenti linguaggi, con Σ = {0, 1}, sono regolari:
(1)
(2)
(3)
(4)
l’insieme di tutte le stringhe aventi tre 0 consecutivi;
l’insieme di tutte le stringhe tali che il penultimo simbolo è 0;
l’insieme di tutte le stringhe tali che il terzultimo simbolo è 0;
l’insieme di tutte le stringhe tali che, se interpretate come numero intero
(binario), sono divisibili per 2;
(5) l’insieme di tutte le stringhe tali che, se interpretate come numero intero
(binario), sono divisibili per 4;
(6) l’insieme di tutte le stringhe tali che, se interpretate come numero intero
(binario), sono divisibili per 5.
Esercizio 3.8. Si dimostrino le seguenti proprietà
(1) Siano vx e wx due stringhe e M = hQ, Σ, δ, q0 , Fi un DFA. Allora,
^
δ(q0 , v) = ^
δ(q0 , w) implica ^δ(q0 , vx) = ^δ(q0 , wx).
(2) Siano u e v due stringhe e q uno stato. Allora ^δ(q, uv) = ^δ(^δ(q, u), v).
(3) Siano u e v due stringhe e q uno stato. Se ^δ(q, u) = q1 e ^δ(q, uv) = q2
allora ^
δ(q1, v) = q2 .
(4) Sia L ⊆ Σ∗ un linguaggio accettato da un DFA M. Allora esiste M ′ =
hQ ′ , Σ ′ , δ ′ , q0′ , F ′ i tale che Σ ′ = Σ e L(M ′ ) = L(M).
4. Automi non-deterministici
Un automa a stati finiti non-deterministico (NFA) è una quintupla hQ, Σ, δ, q0 , Fi
dove Q, Σ, q0 e F ⊆ Q mantengono il significato visto per gli automi deterministici,
mentre la funzione di transizione δ è ora definita
δ : Q × Σ −→ ℘(Q) .
Si osservi che è ora ammesso: δ(q, a) = ∅ per qualche q ∈ Q ed a ∈ Σ. Anche per
gli NFA dalla funzione δ si ottiene in modo univoco la funzione ^δ : Q×Σ∗ −→ ℘(Q)
nel modo seguente:
^
δ(q, ε) = {q}
S
^
δ(p, a)
δ(q, wa) =
^
p∈δ(q,w)
36
3. AUTOMI A STATI FINITI
Una stringa x è accettata da un NFA M = hQ, Σ, δ, q0 , Fi se ^δ(q0 , x) ∩ F 6= ∅.
Il linguaggio accettato da M è l’insieme delle stringhe accettate, ovvero:
L(M) = {x ∈ Σ∗ : ^δ(q0 , x) ∩ F 6= ∅} .
Mentre la rappresentazione mediante tabella degli automi non deterministici è
profondamente diversa da quella per i deterministici (in ogni casella si deve inserire
ora un insieme di stati), la rappresentazione a grafo rimane pressoché immutata.
L’unica differenza è che da un nodo possono uscire più archi (o nessuno) etichettati
dallo stesso simbolo. Dal punto di vista invece del modello con testina e nastro, il
non-determinismo va immaginato come la possibilità contemporanea di procedere
la computazione in ognuno degli stati raggiunti. Si apre dunque un gran numero
di computazioni virtuali parallele.
5. Equivalenza tra DFA e NFA
In questo paragrafo si mostrerà che i linguaggi accettati dai DFA e dagli NFA
coincidono. Poiché un DFA si può vedere come un NFA in cui δ(q, a) restituisce
sempre insiemi costituiti da un solo stato (detti anche singoletti), si ha che ogni
linguaggio regolare è un linguaggio accettato da un qualche NFA. Per l’implicazione
inversa ci serve il seguente teorema:
Teorema 3.9 (Rabin-Scott, 1959). Sia M = hQ, Σ, δ, q0 , Fi un NFA. Allora
esiste un DFA M ′ tale che L(M) = L(M ′ ).
Proof. Definisco M ′ = hQ ′ , Σ ′ , δ ′ , q0′ , F ′ i come segue:
• Σ ′ = Σ;
• Q ′ = ℘(Q) (sarebbe più preciso definire Q ′ = {q1 , . . . , q2|Q| } e poi stabilire una corrispondenza biunivoca fra tali stati e gli elementi di ℘(Q).
Tuttavia, con tale abuso sintattico la dimostrazione diventa molto più
snella);
• q0′ = {q0 };
• F ′ = {P ⊆ Q
S : P ∩ F 6= ∅};
• δ ′ (P, a) = p∈P δ(p, a), per P ∈ ℘(Q).
Mostriamo per induzione sulla lunghezza della stringa di input x che
^δ(q0 , x) = ^δ ′ (q0′ , x)
Base: Per |x| = 0 il risultato è banale, poiché q0′ = {q0 } e x = ε.
Passo: Supponiamo che l’ipotesi induttiva valga per tutte le stringhe x tali che
|x| ≤ m. Sia xa una stringa di lunghezza m + 1. Allora:
^
δ ′ (q0′ , xa) =
=
=
=
δ ′ (^δ ′ (q0′ , x), a)
Def. di ^· nei DFA
δ (^δ(q0 , x), a)
Ip. ind.
S
′
^ 0 ,x) δ(p, a) Def. di δ
p∈δ(q
′
^
δ(q0 , xa)
Def. di ^· nei NFA
6. AUTOMI CON ε-TRANSIZIONI
37
Il teorema segue dal fatto che:
x ∈ L(M)
^ 0 , x) ∩ F 6= ∅
sse δ(q
def. di linguaggio NFA
sse δ
proprietà sopra
^′
(q0′ , x)
∩ F 6= ∅
sse δ^ ′ (q0′ , x) ∈ F ′
sse x ∈ L(M ′ )
def. di F ′
def. di linguaggio DFA
Esercizio 3.10. Si determini il DFA equivalente al NFA:
0
1
q0
{q0 }
{q0 , q1 }
q1
{q1 }
{q0 , q2 }
q2
{q1 , q2 } {q0 , q1 , q2 }
ove F = {q2 }. Qual è il linguaggio accettato?
Esercizio 3.11. Si descriva un NFA a 4 stati che riconosce il linguaggio delle
stringhe di 0 e 1 con terzultimo elemento a 0. Si passi poi al DFA equivalente e lo
si confronti con quello ottenuto nell’esercizio 3.7(3).
6. Automi con ε-transizioni
In questo paragrafo sarà presentato un terzo tipo di automa che estende il
modello non-deterministico ma che, come sarà mostrato nel Teorema 3.12, ne è
equivalente dal punto di vista dei linguaggi accettati.
Un NFA con ε-transizioni è una quintupla hQ, Σ, δ, q0 , Fi dove: Q, Σ, q0 e F ⊆
Q sono come per gli automi non deterministici, mentre la funzione di transizione
δ è ora definita
δ : Q × (Σ ∪ {ε}) −→ ℘(Q) .
L’idea è che da uno stato è permesso passare ad un altro stato anche senza “leggere”
caratteri di input.
La costruzione della funzione ^δ : Q × Σ∗ −→ ℘(Q) nel caso dei ε-NFA risulta
leggermente più complessa che nei casi precedenti. Per far ciò si introduce la
funzione ε-closure che, applicata ad uno stato, restituisce l’insieme degli stati
raggiungibili da esso (compreso sè stesso) mediante ε-transizioni. La costruzione
di tale funzione è equivalente a quella che permette di conoscere i nodi raggiungibili
da un nodo in un grafo e può facilmente essere calcolata a partire dalla funzione
δ (un arco p → q si ha quando q ∈ δ(p, ε)). Il concetto di ε-closure si estende in
modo intuitivo ad insiemi di stati:
[
ε-closure(P) =
ε-closure(p)
p∈P
38
3. AUTOMI A STATI FINITI
^δ si può ora definire nel modo seguente:
^
δ(q, ε) = ε-closure(q)
S
^
ε-closure(δ(p, a))
δ(q, wa) =
^
p∈δ(q,w)
Si noti che in questo caso ^
δ(q, a) può essere diverso da δ(q, a). Ad esempio,
nell’automa:
✛✘ ✛✘ ✛✘
a ✲
ε ✲
q
q′
q ′′
✚✙
✚✙
Si ha che δ(q, a) = {q ′ }, mentre ^δ(q, a) =
S
^
p∈δ(q,ε)
✚✙
ε-closure(δ(p, a)) = {q ′ , q ′′ }.
Definiamo dunque il linguaggio accettato dall’automa L(M) = {x ∈ Σ∗ :
^δ(q0 , x) ∩ F 6= ∅}. Si osservi come per questa classe di linguaggi si potrebbe
assumere che l’insieme F abbia esattamente un elemento.
7. Equivalenza di ε-NFA e NFA
Ogni NFA è, per definizione, un caso particolare di un ε-NFA. Con il seguente
teorema mostreremo che la classe dei linguaggi riconosciuti dagli automi ε-NFA
non estende propriamente quella dei linguaggi riconosciuti da automi NFA:
Teorema 3.12. Sia M = hQ, Σ, δ, q0 , Fi un ε-NFA. Allora esiste un NFA M ′
tale che L(M) = L(M ′ ).
Proof. Definisco M ′ = hQ ′ , Σ ′ , δ ′ , q0′ , F ′ i come segue:
• Q ′ = Q,
• Σ ′ = Σ,
• q0′ =
q0 ,
F ∪ {q } Se ε-closure(q ) ∩ F 6= ∅
0
0
• F′ =
F
altrimenti
′
^ a).
• δ (q, a) = δ(q,
Dobbiamo mostrare che
^
δ(q0 , x) ∩ F 6= ∅ sse
^δ ′ (q0′ , x) ∩ F ′ 6= ∅
(1)
^ 0 , ε) = ε-closure(q0 ) per definizione.
Se x = ε, si ha che: δ(q
D’altro canto: ^
δ ′ (q0 , ε) = {q0 }.
Se ε-closure(q0 )∩F 6= ∅ allora, per def. di F ′ vale che q0 ∈ F ′ ; dunque {q0 }∩F ′ 6= ∅.
Sia ora {q0 } ∩ F ′ 6= ∅. Allora q0 ∈ F ′ . Due casi sono possibili: se q0 ∈ F allora
ε-closure(q0 ) ∩ F 6= ∅ (in quanto q0 ∈ ε-closure(q0 )). Altrimenti, se q0 ∈ F ′ \ F
per definizione di F ′ si ha che ε-closure(q0 ) ∩ F 6= ∅.
Mostreremo per induzione su |x| ≥ 1 che ^δ ′ (q0′ , x) = ^δ(q0 , x).
8. AUTOMI CON OUTPUT
39
Base: |x| = 1. Allora x = a per qualche simbolo a ∈ Σ. Ma allora ^δ ′ (q0′ , a) =
^ 0 , a) per definizione.
δ ′ (q0′ , a) = δ(q
Passo: Assumiamo che la tesi valga per tutte le stringhe x tali che 1 ≤ |x| ≤ m.
Sia xa una stringa di lunghezza m + 1. Allora:
S
′
^
def. di δ^ ′
δ ′ (q0′ , xa) =
^ ′ (q ′ ,x) δ (p, a)
p∈δ
0
S
δ(p, a)
def. di δ ′
=
^ ′ (q ′ ,x) ^
p∈δ
0
S
^
ip. ind.
=
^ 0 ,x) δ(p, a)
p∈δ(q
S
^ e (∗)
=
^ 0 ,x) ε-closure(δ(p, a)) def. di δ
p∈δ(q
=
^
δ(q0 , xa)
def. di δ^
(∗) si osservi che per definizione di ^δ, la ε-closure(p) è contenuta in ^δ(q0 , x).
Per concludere la dimostrazione, si deve mostrare la proprietà (1), ovvero che
^δ ′ (q0′ , x) contiene uno stato di F ′ sse ^δ(q0 , x) contiene uno stato di F.
Poiché F ⊆ F ′ e ^
δ ′ (q0′ , x) = ^
δ(q0 , x) l’implicazione (→) deriva immediatamente.
Per l’altra implicazione, l’unico problema si avrebbe nel caso:
(1) q0 ∈ ^
δ ′ (q0′ , x),
′
(2) q0 ∈ F ′ ,
(3) q0 ∈
/ F.
^ ′ (q ′ , x) = δ(q
^ 0 , x), si ha che q0 ∈ δ(q
^ 0 , x). Ma, per definizione di δ,
^
Poiché δ
0
^
anche ogni elemento della sua ε-closure appartiene a δ(q0 , x). Tale ε-closure,
poiché q0 ∈ F ′ , interseca F.
Corollario 3.13. Le classi di linguaggi riconosciute da DFA, NFA e ε-NFA
coincidono (linguaggi regolari).
Proof. Immediato dai Teoremi 3.9 e 3.12.
8. Automi con output
Gli automi visti fin qui, tutti equivalenti dal punto di vista della classe di
linguaggi accettati (riconosciuti) non sono in grado di fornire alcun tipo di output
se non il messaggio (booleano) del raggiungimento di uno stato finale o meno.
Esistono in letteratura due tipi di automi provvisti di output: le macchine di
Moore e quelle di Mealy. Tali macchine sono automi a stati finiti deterministici
(e dunque ereditano la classe di linguaggi accettati) che possiedono un alfabeto di
output:
• le macchine di Moore forniscono in output un simbolo in funzione di ogni
stato raggiunto. Poiché per una stringa di n elementi si raggiungono al
più n + 1 stati (compreso quello iniziale), si può ottenere un output di
(al più) n + 1 caratteri.
40
3. AUTOMI A STATI FINITI
• le macchine di Mealy forniscono in output un simbolo ogni volta che
avviene una transizione δ(q, a). In questo caso al più n caratteri saranno
forniti in output.
Gli output possono essere utili per differenziare stati finali. Cosı̀, ad esempio,
una stringa accettata può anche avere una più precisa classificazione (si veda
l’Esempio 3.14).
Le macchine di Moore e Mealy vengono impiegate per la sintesi di reti sequenziali e sono formalismi tra loro equivalenti. Per maggiori dettagli, si veda [13].
Esempio 3.14. Il seguente automa è una macchina di Moore che, oltre a
riconoscere se una stringa sia o meno un elemento di una espressione numerica,
segnala se si tratta di un numero intero o di un identificatore di variabile. L’output
è rappresentato dalle scritte (Idf. di variabile, etc.) posizionate accanto ai vari
stati.
✓
✏
✓✏
✲ q 1 Idf. di variabile
✒
✒✑
✒
❅
❅‘ ’
a| · · · |z
❅ ✓✏
✓✏
Idf. non valido
❘
❅
‘ ’
✲ q3 ✛ ✏
q0
✒✑
✒✑
✒✒ ✑
❅ 0| · · · |9
a| · · · |z|0| · · · |9| ‘
❅
✓✏
❅
a| · · · |z| ‘ ’
❘ q ✛ ✏
❅
2
Cost. numerica
✒✑
✒ ✑
a| · · · |z|0| · · · |9
’
0| · · · |9
Si osservi come esso racchiuda in sè due DFA privi di output.
CHAPTER 4
Espressioni regolari
questo capitolo verrà illustrato un modo alternativo per descrivere i linguaggi
I naccettati
da automi finiti (linguaggi regolari). definiremo un algebra di operazioni per manipolare linguaggi regolari.
1. Operazioni sui linguaggi
Sia Σ un alfabeto e L, L1 , L2 insiemi di stringhe di Σ∗ . La concatenazione di
L1 e L2 , denotata come L1 L2 è l’insieme:
L1 L2 = {xy ∈ Σ∗ : x ∈ L1 , y ∈ L2 } .
Definiamo ora
L0
=
{ε}
Li+1
=
LLi
Si osservi che L0 = {ε} 6= ∅. La chiusura (di Kleene) di L, denotata come L∗ è
l’insieme:
[
L∗ =
Li
i≥0
S
mentre la chiusura positiva di L, denotata come L+ è l’insieme: L+ = i≥1 Li (si
verifica immediatamente che L+ = LL∗ , dunque tale operatore, seppur comodo,
non è essenziale). Si osservi come la definizione sia consistente con la nozione di
Σ∗ fin qui adoperata.
2. Definizione formale
Sia Σ un alfabeto. Le espressioni regolari su Σ e gli insiemi che esse denotano
sono definiti ricorsivamente nel modo seguente:
(1) ∅ è una espressione regolare che denota l’insieme vuoto.
(2) ε è una espressione regolare che denota l’insieme {ε}.
(3) Per ogni simbolo a ∈ Σ, a è una espressione regolare che denota l’insieme
{a}.
(4) Se r e s sono espressioni regolari denotanti rispettivamente gli insiemi R
ed S, allora (r + s), (rs), e (r∗ ) sono espressioni regolari che denotano gli
insiemi R ∪ S, RS, e R∗ rispettivamente.
41
42
4. ESPRESSIONI REGOLARI
Se r è una espressione regolare, indicheremo con L(r) il linguaggio denotato da r.
Tra espressioni regolari valgono delle uguaglianze che permettono la loro manipolazione algebrica. Varrà che r = s se e solo se L(r) = L(s).
Notazione 4.1. Qualora non si crei ambiguità, nello scrivere espressioni regolari saranno omesse le parentesi. Le precedenze degli operatori sono le stesse
dell’algebra, interpretando ∗ come esponente e la concatenazione come prodotto (si
veda Esercizio 4.2). E’ ammesso usare l’abbreviazione r+ per l’espressione regolare
rr∗ .
Esercizio 4.2. Si provino o refutino le seguenti identità tra espressioni regolari:
(1)
(2)
(3)
(4)
(5)
(6)
(7)
(8)
(9)
(10)
(11)
(12)
(13)
(14)
r + s = s + r;
(r + s) + t = r + (s + t);
r(st) = (rs)t;
r(s + t) = rs + rt;
(r + s)t = rt + st;
∅∗ = ε;
(r∗ )∗ = r∗ ;
(ε + r∗ r) = r∗ ;
(ε + r)∗ = r∗ ;
(r∗ s∗ )∗ = (r + s)∗ ;
(rs + r)∗ r = r(sr + r)∗ ;
s(rs + s)∗ r = rr∗ s(rr∗ s)∗ ;
(r + s)∗ = r∗ + s∗ ;
(r∗ + s∗ )∗ = (rs)∗ .
3. Equivalenza tra DFA e ER
Teorema 4.3 (McNaughton & Yamada, 1960). Sia r una espressione regolare.
Allora esiste un ε-NFA M tale che L(M) = L(r).
Proof. Costruiremo un ε-NFA siffatto, con un unico stato finale, per induzione sulla complessità strutturale dell’espressione regolare r.
Base: Ci sono tre casi base:
• l’automa:
✓✏
❵
✒✑
q0
riconosce il linguaggio {ε};
• l’automa
riconosce il linguaggio ∅;
✓✏ ✓✏
❵
q 0❵
q1
✒✑ ✒✑
3. EQUIVALENZA TRA DFA E ER
• l’automa
43
✓✏
✓✏
❵
a
✲ q1
q 0❵
✒✑ ✒✑
riconosce il linguaggio {a}.
Passo: Anche qui abbiamo tre casi da analizzare:
• r = r1 + r2 . Per i = 1, 2, sia Mi , con stato iniziale qi0 e stato finale
qif l’automa che riconosce L(ri ). L’esistenza di tali automi è assicurata
dall’ipotesi induttiva. Il seguente automa, con stato iniziale q0 e stato
finale qf riconosce il linguaggio L(r) = L(r1 ) ∪ L(r2 ):
✬
✓✏
✒✒✑
ε
✫
✓✏
q1
0
M1
✬
✒✑
◗
ε ◗
✓✏
◗
s 2
◗
M2
q0
✒✑
✫
q0
✩
✓✏
✒✑
❅ ε
✪
❅
❘✓✏
❅
q1
f
✒✑
✩
✒
ε
✓✏
qf
✒✑
✪
q2
f
• r = r1 r2 . Per i = 1, 2, sia Mi , con stato iniziale qi0 e stato finale
qif l’automa che riconosce L(ri ). L’esistenza di tali automi è assicurata
dall’ipotesi induttiva. Il seguente automa, con stato iniziale q0 e stato
finale qf riconosce il linguaggio L(r1 )L(r2 ):
✬
✓✏ ✓✏
ε
✲ q 10
q0
M1
✒✑ ✒✑
✫
✬
✩
✓✏
✓✏
ε
✲
q2
M2
q1
0
f
✒✑
✒✑
✫
✪
✩
✓✏
✓✏
ε
✲ qf
q2
f
✒✑
✒✑
✪
• r = r∗1 . Sia M1 , con stato iniziale q10 e stato finale q1f l’automa che riconosce L(r1 ). L’esistenza di tale automa è assicurata dall’ipotesi induttiva. Il seguente automa, con stato iniziale q0 e stato finale qf riconosce
L(r) = (L(r1 ))∗ :
44
4. ESPRESSIONI REGOLARI
ε
✬
✬
✓✏ ✓✏
❄
ε
✲ q 10
q0
M1
✒✑ ✒✑
✫
ε
✫
✩
✩
✓✏
✓✏
ε
✲ qf
q1
f
✒✑
✒✑
✪ ✻
✪
Le dimostrazioni che tali automi riconoscono esattamente i linguaggi a loro assegnati è lasciata per esercizio.
Esercizio 4.4. Si costruisca l’ε-NFA per l’espressione regolare: (0 + 1)∗ 0(0 +
1)(0 + 1). Si passi poi automaticamente al NFA e quindi al DFA equivalente. Lo
si confronti dunque con l’automa dell’esercizio 3.11.
Si vuole ore mostrare il contrario, ovvero che preso a caso un automa M (senza
perdita di generalità è sufficiente considerare un DFA), il linguaggio accettato da
M è denotato da una espressione regolare. Si darà una dimostrazione costruttiva
di questo fatto. Per una dimostrazione alternativa (e più rigorosa), si veda [13].
Teorema 4.5. Sia M un DFA. Allora esiste una espressione regolare r tale
che L(M) = L(r).
Proof. (Traccia) Si costruirà, a partire da M = hQ, Σ, δ, q0 , Fi un sistema
di equazioni in cui ad ogni stato qi viene associata una variabile Qi . Si fornirà
una metodologia per calcolare una soluzione a tale sistema. La soluzione calcolata assegnerà una espressione regolare ri ad ogni variabile Qi , che ne denota il
linguaggio. Se F = {qj1 , . . . , qjp }, avremo che:
r = rj1 + · · · + rjp
e L(r) è il linguaggio accettato dall’automa.
Per ogni qi ∈ Q, siano:
• qi1 , . . . , qih gli stati diversi da qi tali che esiste aij ∈ Σ t.c. δ(qij , aij ) =
qi ;
• bi1 , . . . , bik i simboli tali che δ(qi , bij ) = qi .
Assegnamo a qi l’equazione:
Qi
=
(Qi1 ai1 + · · · + Qih aih )(bi1 + · · · + bik )∗
Nel caso che qi fosse q0 bisogna aggiungere +ε alla parte destra.
Un tale sistema si può risolvere mediante manipolazioni algebriche sulle espressioni
regolari presenti ed usando il metodo di sostituzione (standard) qualora (come accade all’inizio) Qi non compaia nella parte destra dell’equazione che lo definisce.
Se non fosse cosı̀, poiché le variabili Qj compaiono sempre all’inizio delle espressioni presenti nelle parti destre delle equazioni, ci si riesce a ricondurre, mediante
3. EQUIVALENZA TRA DFA E ER
45
manipolazioni algebriche, ad una equazione della forma:
Qi = Qi s1 + s2
per opportuni s1 ed s2 in cui Qi non compare. La soluzione di questa equazione
è Qi = s2 s∗1 ; infatti:
+
∗
(s2 s∗1 )s1 + s2 = s2 s+
1 + s2 = s2 (s1 + ε) = s2 s1 .
Si applichi dunque la sostituzione Qi = s2 s∗1 al resto del sistema.
Esercizio 4.6. Mediante la costruzione del Teorema 4.3 si identifichi un εNFA che riconosca il linguaggio denotato dall’espressione regolare ((0+1)∗ 0011(0+
1)∗ )∗ . Si passi poi, mediante le trasformazioni suggerite nei Teoremi 3.12 e 3.9 al
corrispondente DFA. Di quanti stati si ha bisogno? Si cerchi di costruire direttamente un DFA che riconosca lo stesso linguaggio.
Esercizio 4.7. Si determinino le espressioni regolari associate ai DFA finora
presentati nel testo o descritti per risolvere gli esercizi.
CHAPTER 5
Proprietà dei linguaggi regolari
2 e 4 si è imparato che se un linguaggio è riconosciuto da un automa
D aia Capitoli
stati finiti (equivalentemente, DFA, NFA, ε-NFA) allora è un linguaggio
regolare. Argomenti di questo capitolo saranno i principali risultati e metodi utili
per stabilire se un dato linguaggio sia o meno regolare.
1. Il “Pumping Lemma”
Il primo metodo che proponiamo e che viene spesso usato per mostrare che
un linguaggio non è regolare deriva dal seguente risultato noto come Pumping
Lemma:
Lemma 5.1 (Bar-Hillel, Perles, Shamir, 1961). Sia L un linguaggio regolare.
Allora esiste una costante n ∈ N tale che per ogni z ∈ L tale che |z| ≥ n esistono
tre stringhe u, v, w tali che:
(1) z = uvw,
(2) |uv| ≤ n,
(3) |v| > 0, e
(4) per ogni i ≥ 0 vale che uvi w ∈ L.
Proof. Sia M = h{q0 , . . . , qn−1 }, Σ, δ, q0 , Fi DFA tale che L = L(M) (esiste
poiché L è regolare). Sia z = a1 · · · am , m ≥ n, z ∈ L (si noti l’arbitrarietà
della scelta di z; se una tale z non esistesse, il lemma varrebbe banalmente).
Per i = 1, . . . , n (n ≤ m) si consideri l’evoluzione degli stati ^δ(q0 , a1 · · · ai ). In
tal modo, considerando anche lo stato iniziale q0 , si raggiungono in tutto n + 1
stati. Poiché gli stati dell’automa sono n per ipotesi, esiste (almeno) uno stato q̄
raggiunto (almeno) due volte.
Dunque avremo una situazione del tipo
^
δ(q0 , a1 · · · ai1 ) =
=
q̄
^δ(q0 , a1 · · · ai1 · · · ai2 ) i2 > i1
A questo punto, si prenda u = a1 · · · ai1 , v = ai1 +1 · · · ai2 , w = ai2 +1 · · · am .
δ(q0 , u) = q̄ e ^δ(q0 , uv) = q̄. Usando l’esercizio 3.8 si
Sappiamo dunque che ^
δ(q̄, v) = q̄.
ha che ^
Inoltre, sappiamo per ipotesi che ^δ(q0 , uvw) = q ′ per qualche q ′ ∈ F; assieme
al fatto che ^
δ(q0 , uv) = q̄, usando l’esercizio 3.8 si ha che ^δ(q̄, w) = q ′ .
47
48
5. PROPRIETÀ DEI LINGUAGGI REGOLARI
Per induzione su n ≥ 0 si mostra che ^δ(q0 , uvi ) = q̄. Con l’ultima proprietà
vista, si ha dunque che ^
δ(q0 , uvwi ) = q ′ ∈ F.
|uv| ≤ n per costruzione.
Corollario 5.2. n del Lemma 5.1 può essere preso come il numero di stati
del più piccolo automa riconoscente L.
Proof. Immediato dalla dim. del Lemma.
Si osservi come il lemma asserisca la veridicità di una formula logica quantificata non banale, ovvero, per ogni linguaggio L, se L è regolare, allora vale:
(z ∈ L ∧ |z| ≥ n) → ∃u, v, w
∃n ∈ N ∀z
(1.1)
z = uvw ∧ |uv| ≤ n ∧ |v| > 0∧
∀i (i ∈ N → uvi w ∈ L)
Il Pumping lemma viene largamente usato per mostrare che un dato linguaggio
non è regolare (qualora non lo sia!). Per mostrare ciò, si assume che L non sia
regolare e si deve mostrare che vale la negazione della formula (1.1), ovvero (si
veda il Capitolo 4 per le tecniche di complementazione di una formula logica):
z ∈ L ∧ |z| ≥ n∧
(1.2)
∀n ∈ N ∃z ∀u, v, w (z = uvw ∧ |uv| ≤ n ∧ |v| > 0
→ ∃i (i ∈ N ∧ uvi w ∈
/ L)
Nel prossimo esempio si illustrerà un corretto uso di questa tecnica, che dato
l’annidamento di quantificazioni, può indurre ad errore.
Esempio 5.3. Il linguaggio L = {0i 1i : i ≥ 0} non è regolare. Supponiamo,
per assurdo, sia regolare e cerchiamo di verificare la formula (1.2). Si prenda un
numero naturale n arbitrario e si scelga (questo è il punto in cui bisogna avere la
“fortuna” di scegliere una stringa “giusta” tra tutte quelle di lunghezza maggiore
o uguale a n) la stringa z = 0n 1n . Per tale stringa vale che z ∈ L e |z| ≥ n.
Rimane da dimostrare che comunque si suddivida la stringa z in sottostringhe uvw
(in modo tale cioè che z = uvw) tali da soddisfare i vincoli |uv| ≤ n e |v| > 0 esiste
almeno un i ≥ 0 tale per cui uvi w ∈
/ L.
I modi per partizionare z sono molti (per esercizio, si provi a pensare quanti)
però, il fatto che |uv| ≤ n permette di ricondursi al seguente unico “schema di
suddivisione”:
• u = 0a ,
• v = 0b ,
• w = 0c 1n ,
con a + b + c = n, b > 0. “Pompando” v si ottengono stringhe al di fuori del
linguaggio, da cui l’assurdo. Ad esempio, con i = 0 vale che uv0 w = 0a 0c 1n ∈
/ L,
poiché a + c < n.
2. PROPRIETÀ DI CHIUSURA
49
Esercizio 5.4. Dimostrare che i seguenti linguaggi non sono regolari:
n
(1) {02 : n ≥ 1};
n
(2) {0 : n è primo};
(3) {0m 1m+1 0m+2 : m ≥ 0};
(4) {0m 1n 0m+n : m ≥ 1, n ≥ 1};
(5)
{0m 1n : m, n ≥ 0, m < n}.
(6) x ∈ {0, 1}∗ : # di 0 in x = # di 1 in x
2. Proprietà di chiusura
La definizione di linguaggio è una definizione di tipo insiemistico (L ⊆ Σ∗ ).
Pertanto è lecito parlare di unione, intersezione, complementazione (etc.) di linguaggi. In questo paragrafo mostreremo che la proprietà di essere un linguaggio
regolare si conserva sotto queste usuali operazioni.
Teorema 5.5. I linguaggi regolari sono chiusi rispetto alle operazioni di unione,
concatenazione e chiusura di Kleene.
Proof. Immediato dalla definizione di espressione regolare e dai Teoremi 4.3
e 4.5.
Teorema 5.6. I linguaggi regolari sono chiusi rispetto alla operazione di complementazione. Ovvero, se L ⊆ Σ∗ è regolare, anche L̄ = Σ∗ \ L è regolare.
Proof. Sia M = hQ, Σ ′ , δ, q0 , Fi il DFA che riconosce L. Per l’esercizio 3.8.4,
possiamo assumere Σ ′ = Σ. Allora, banalmente, M ′ = hQ, Σ, δ, q0 , Q\Fi riconosce
L̄.
Corollario 5.7. I linguaggi regolari sono chiusi rispetto all’intersezione.
Proof. Immediato dal fatto che L1 ∩ L2 = (L̄1 ∪ L̄2 ).
Esercizio 5.8. Dati due DFA M1 e M2 , si costruisca l’automa che riconosce
M1 ∩ M2 in maniera diretta. Di quanti stati avete bisogno?
Esercizio 5.9. Si dimostri che l’insieme delle stringhe di 0 e 1 tali che:
• non vi sono mai 3 “0” consecutivi e non vi sono mai tre “1” consecutivi,
oppure
• vi sono almeno 2 “0” seguiti da 2 “1”
è un linguaggio regolare (Suggerimento: si suddivida il problema in problemi elementari e si usino i risultati di questo paragrafo per combinarli).
Esercizio 5.10. Si dimostri che il linguaggio composto da stringhe di 0 e 1
tale che:
• contengono almeno 5 “0” consecuitivi, e
• se vi sono 3 “1” consecutivi, allora essi sono seguiti da almeno due “0”
è regolare.
50
5. PROPRIETÀ DEI LINGUAGGI REGOLARI
3. Risultati di decidibilità
In questo paragrafo saranno mostrati alcuni risultati relativi al problema di
effettuare delle decisioni riguardanti linguaggi regolari. Un primo risultato scontato in questa classe di linguaggi (meno in altre!) è la decidibilità del problema
dell’appartenenza, ovvero: data una descrizione del linguaggio L (mediante e.r.
o automa) e una stringa x, decidere se x ∈ L o meno. Se è data l’e.r., possiamo prima calcolare l’automa. Dato l’automa il problema diventa banale: tale
problema è evidentemente decidibile. Vediamo ora altri problemi.
Teorema 5.11 (Vuoto-infinito). L’insieme delle stringhe accettate da un DFA
M con n stati è:
(1) non vuoto se e solo se accetta una stringa di lunghezza inferiore a n;
(2) infinito se e solo se l’automa accetta una stringa di lunghezza ℓ, n ≤ ℓ <
2n.
Proof.
(1) (←) Se accetta una stringa (di lunghezza di inferiore a n)
allora è banalmente non vuoto.
(→) Supponiamo sia non vuoto. Allora, per definizione M accetta almeno
una stringa z, |z| = m.
Se m < n, la tesi è provata. Altrimenti come conseguenza del il pumping
lemma, z = uvw con |v| ≥ 1, e uv0 w = uw è accettata da M. Se
|uw| < n la tesi è provata, altrimenti si proceda iterativamente ripartendo
con z = uw (che ha lunghezza strettamente minore a m). Dopo al più
m − n iterazioni si otterrà una stringa di lunghezza inferiore a n;
(2) (←) Supponiamo M accetti una stringa z di lunghezza ℓ, n ≤ ℓ < 2n.
Allora, per il pumping Lemma, z = uvw, |v| ≥ 1 e {uvi w : i ∈ N} ⊆ L(M).
Ma {uvi w : i ∈ N} è un insieme infinito.
(→) Sia L(M) infinito. Allora esiste z ∈ L(M) tale che |z| = m ≥ 2n. Per
il pumping lemma z = uvw con |uv| ≤ n e |v| ≥ 1 (e dunque |uw| ≥ n).
Per il pumping lemma, z ′ = uw ∈ L(M). Se |z ′ | < 2n, allora la tesi è
dimostrata. Altrimenti, si reiteri il procedimento partendo dalla stringa
(più corta) z ′ = uw. In un numero finito di passi si trova la stringa
cercata.
Corollario 5.12. Sia M DFA. Allora i problemi ‘L(M) = ∅’ e ‘L(M) è infinito’ sono entrambi decidibili (cioè esiste una procedura effettiva per determinare
la loro veridicità o falsità).
Teorema 5.13 (Equivalenza). Dati due DFA M1 e M2 , il problema di stabilire se L(M1 ) = L(M2 ) è decidibile.
Proof. L(M1 ) = L(M2 ) è insiemisticamente equivalente a
(L(M1 ) ∩ L(M2 )) ∪ (L(M1 ) ∩ L(M2 )) = ∅ .
4. IL TEOREMA DI MYHILL-NERODE
51
Dai Teoremi 5.5 e 5.6 sappiamo esistere un automa M3 tale che
L(M3 ) = (L(M1 ) ∩ L(M2 )) ∪ (L(M1 ) ∩ L(M2 )) .
Il risultato segue dalla decidibilità del problema del vuoto (Corollario 5.12).
4. Il teorema di Myhill-Nerode
Iniziamo il paragrafo richiamando alcune definizioni. Dato un insieme S, una
relazione di equivalenza R ⊆ S × S (si veda il Capitolo 2) induce (univocamente, a
meno di ridenominazione degli indici) una partizione di S = S1 ∪ S2 ∪ . . . ove per
ogni i Si 6= ∅ e per ogni i, j, i 6= j, si ha che:
(1) Si ∩ Sj = ∅;
(2) (∀a, b ∈ Si )a R b;
(3) (∀a ∈ Si )(∀b ∈ Sj )¬(a R b).
Le varie Si sono dette classi di equivalenza. Se a ∈ Si allora con la notazione [a]R
(o semplicemente [a] quando il contesto è chiaro) si denota la classe Si . Se S è
partizionato in un numero finito di classi S1 ∪ S2 ∪ · · · ∪ Sk allora R si dice di indice
finito (k) su S. Date due relazioni di equivalenza R1 e R2 sullo stesso insieme S,
R1 è un raffinamento di R2 se ogni classe di equivalenza della partizione indotta
da R1 è sottoinsieme di qualche classe di equivalenza della partizione indotta da
R2 .
Ad esempio, se S = {2, 3, 4, 5}, P1 = {{2, 3, 5}, {4}} e P2 = {{2}, {3, 5}, {4}} denotano le
partizioni di S ottenute a partire dalle relazioni
(R1 ) xR1 y
sse
x e y sono entrambi primi o uguali tra loro
(R2 ) xR2 y
sse
x e y sono entrambi primi e dispari, o uguali tra loro
allora R2 è un raffinamento di R1 .
Esercizio 5.14. Dato un insieme S finito, quanti sono i modi distinti per
partizionarlo?
Dato un linguaggio L ⊆ Σ∗ , gli associamo la relazione RL ⊆ Σ∗ × Σ∗ definita
come segue:
x RL y sse
(∀z ∈ Σ∗ )(xz ∈ L ↔ yz ∈ L) .
Similmente, se M = hQ, Σ, δ, q0 , Fi è un DFA, gli associamo la relazione RM ⊆
Σ∗ × Σ∗ definita come:
x RM y sse
^δ(q0 , x) = ^δ(q0 , y) .
Definiamo come Lq = {x ∈ Σ∗ : ^δ(q0 , x) = q} il linguaggio associato allo stato q
dell’automa. Le classi di equivalenza di RM sono esattamente i linguaggi associati
ad ogni stato dell’automa M.
Lemma 5.15. RL e RM sono relazioni di equivalenza.
52
5. PROPRIETÀ DEI LINGUAGGI REGOLARI
Proof. (Esercizio)
Una relazione R ⊆ Σ∗ × Σ∗ che gode della proprietà:
x R y implica
(∀z ∈ Σ∗ )(xz R yz)
si dice invariante a destra (rispetto alla concatenazione). Dall’esercizio 3.8.1, si
evince che RM è invariante a destra. Anche RL è invariante a destra: siano x, y ∈ Σ∗
tali che xRL y. Sia z ∈ Σ∗ : dobbiamo mostrare che xzRL yz. Se cosı̀ non fosse,
allora esisterebbe w tale che xzw ∈ L e yzw ∈
/ L (o viceversa). Ma in tal caso, con
z ′ = zw, mostreremmo che x 6RL y: assurdo.
Teorema 5.16 (Myhill-Nerode, 1957–58). I seguenti enunciati sono equivalenti:
(1) L ⊆ Σ∗ è accettato da un qualche DFA;
(2) L è l’unione di classi di equivalenza di Σ∗ indotte da una relazione invariante a destra e di indice finito;
(3) RL è di indice finito.
Proof. (1) → (2) Sia L accettato da un DFA M = hQ, Σ, δ, q0 , Fi. Mostreremo che RM è la relazione che soddisfa il punto (2). Per definizione di linguaggio
riconosciuto da un automa:
[
L=
{x ∈ Σ∗ : ^δ(q0 , x) = q}
q∈F
Per l’esercizio 3.8.1, RM è invariante a destra, dunque i vari insiemi:
Lq = {x ∈ Σ∗ : ^δ(q0 , x) = q}
costituiscono le classi di equivalenza della partizione indotta da RM .
(2) → (3) Mostreremo che ogni relazione di equivalenza R che soddisfa (2)
è un raffinamento di RL . Sia x ∈ Σ∗ , vogliamo mostrare che [x]R ⊆ [x]RL . Sia
y ∈ [x]R (dunque x R y). Poiché R è invariante a destra per ipotesi, allora xz R yz
per ogni z ∈ Σ∗ . Poiché L è unione di classi di equivalenza di R, ciò implica che
ogni qualvolta vRw si ha che v ∈ L sse w ∈ L. Pertanto per ogni z ∈ Σ∗ , xz ∈ L
sse yz ∈ L. Ma allora x RL y per definizione, dunque y ∈ [x]RL . L’indice di RL è
minore di quello di R, che per ipotesi è finito.
(3) → (1) Si costruisce un DFA M ′ = hQ ′ , Σ ′ , δ ′ , q0′ , F ′ i che riconosce L. Sia
• Q ′ l’insieme (finito per ipotesi) di classi di equivalenza di RL ,
• Σ ′ lo stesso di L,
• δ ′ ([x], a) = [xa] (la definizione ha senso indipendentemente dalla scelta
di x in quanto RL è invariante a destra),
• q0′ = [ε],
• F ′ = {[x] : x ∈ L}.
5. MINIMIZZAZIONE DI DFA
53
Si tratta di mostrare che L(M ′ ) = L. Si verifica per induzione su |y| ≥ 0 che
^δ ′ ([x], y) = [xy]. Pertanto si ha che:
^
δ ′ (q0′ , x) = δ^ ′ ([ε], x) = [εx] = [x]
e dunque
x ∈ L(M ′ ) sse
^
δ ′ (q0′ , x) ∈ F ′
sse [x] ∈ F sse x ∈ L .
5. Minimizzazione di DFA
Il Teorema 5.16 ha, tra le sue varie conseguenze, quella di stabilire che se un
linguaggio L è riconosciuto da un DFA M allora ne esiste uno minimo M ′ (nel
senso del numero degli stati) equivalente a M.
Teorema 5.17. Per ogni linguaggio regolare L esiste un automa M con minimo numero di stati tale che L = L(M), unico a meno di isomorfismo (ovvero
ridenominazione di stati).
Proof. Sia L accettato da un DFA M. Σ∗ è partizionato dalla relazione di
equivalenza RM negli insiemi di linguaggi accettati dai singoli stati di M.
Dal Teorema 5.16 (1 → 2) sappiamo che RM raffina RL e pertanto il numero di
stati di M è maggiore o uguale a quello di M ′ costruito come nella dimostrazione
(3 → 1) del Teorema 5.16. Dunque l’automa M ′ ha il minor numero di stati
possibile. Supponiamo che tale numero coincida con il numero di stati di M.
Definiamo la funzione f tra gli insiemi di stati dei due automi nel modo
seguente:
f(q) = [x] sse ^δ(q0 , x) = q .
Mostriamo che questa è una buona definizione. Vogliamo essere sicuri che f(q) è
univoca. Supponiamo, per assurdo, f(q) = [y1 ] e f(q) = [y2 ]. Allora ^δ(q0 , y1 ) = q
e^
δ(q0 , y2 ) = q. Ma allora y1 RM y2 . Poiché RM è raffinamento di RL si ha anche
[y1 ] = [y2 ].
La funzione è suriettiva, in quanto per ogni x ∈ Σ∗ si ha che esiste q = ^δ(q0 , x)
tale che f(q) = [x]. Poiché |Q| = |Q ′ | ciò implica che f è biiettiva, dunque vi è una
funzione di rinomina dei nodi.
Per mostrare che i due automi sono isomorfi rimane da mostrare che il seguente
diagramma commuta, per ogni q ∈ Q e per ogni a ∈ Σ:
q
↓a
q
′
f
−→
f
[x]
↓a
−→ [xa]
In altri termini, dati f(q) = [x], δ(q, a) = q ′ , δ ′ ([x], a) = [xa], dobbiamo mostrare
che f(q ′ ) = [xa]:
54
5. PROPRIETÀ DEI LINGUAGGI REGOLARI
f(q ′ ) = [xa] sse q ′ = ^δ(q0 , xa)
def. di f
sse q ′ = δ(^δ(q0 , x), a) def. di ^δ
sse q ′ = δ(q, a)
vero per ipotesi.
In Fig. 1 è presentato un algoritmo che permette di ottenere automaticamente
l’automa minimo corrispondente ad uno dato.
Esempio 5.18. La minimizzazione dell’automa:
0
1
q0
q1
q3
q1
q2
q3
q2
q0
q3
q3
q3
q3
0
1
q0
q0
q3
q3
q3
q3
F
conduce all’automa:
F
in un solo passo. Invece la minimizzazione dell’automa:
0
1
q0
q0
q1
q1
q1
q2
q2
q2
q3
q3
q3
q3
F
restituisce l’automa stesso. Sono però necessarie 3 (= |Q| − 1) iterazioni.
Esercizio 5.19 (Facoltativo). Si dimostri che l’algoritmo di Fig. 1 permette
effettivamente di calcolare un automa equivalente e minimo. (Suggerimento: dato
M = hQ, Σ, δ, q0 , Fi e p, q ∈ Q, diciamo che p è distinguibile da q se esiste una
stringa x tale che ^
δ(p, x) ∈ F ↔ ^δ(q, x) ∈
/ F. Per il pumping lemma, sappiamo che
per verificare la distinguibilità di due stati è sufficiente provare tutte le stringhe x
di lunghezza minore a |Q|. Si tratta di mostrare che due stati sono non distinguibili
se e solo se sono equivalenti per RL(M) e che con al più |Q| iterazioni dell’algoritmo
tutti gli stati distinguibili sono scovati.)
5. MINIMIZZAZIONE DI DFA
55
procedure minimizza;
M = hQ, Σ, δ, q0 , Fi;
input:
output: M ′ = hQ ′ , Σ, δ ′ , q0′ , F ′ i t.c. L(M) = L(M ′ ) e
(∀M ′′ = hQ ′′ , Σ, δ ′′ , q0′′ , F ′′ i)(L(M) = L(M ′′ ) → |Q ′ | ≤ |Q ′′ |);
begin
Πnew := {Q − F, F};
repeat
Π := Πnew ;
Πnew := ∅;
for S in Π do
begin
partiziona S in S1 , . . . , Sm
usando il valore di m più piccolo possibile
t.c. p, q ∈ Si sse
(∀a ∈ Σ)(∃S ′ ∈ Π)(δ(p, a) ∈ S ′ ↔ δ(q, a) ∈ S ′ )
Πnew := Πnew ∪ {S1 , . . . , Sm }
end
until Πnew = Π;
for S in Π let S̄ = mini {qi ∈ S};
Q ′ := {S̄ : S ∈ Π};
δ ′ (S̄, a) := mini {qi : qj ∈ S, δ(qj , a) = qi };
F ′ := {[S] : S ∈ Π, (∃q ∈ S)(q ∈ F)};
q0′ := q0 ;
si eliminino ricorsivamente da Q ′ e F ′ gli stati diversi da q0′ privi di archi entranti e
si eliminino da δ gli archi uscenti da essi.
end.
Figure 1. Algoritmo di minimizzazione
56
5. PROPRIETÀ DEI LINGUAGGI REGOLARI
Esercizio 5.20. Si concretizzi in un linguaggio di programmazione ad alto
livello l’algoritmo di Fig. 1 in modo tale che il problema sia risolto con complessità
O(|Q|2 |Σ|). Si mediti su come arrivare alla complessità O(|Q| log |Q||Σ|).
Esercizio 5.21. Si determini, usando le tecniche di trasformazione viste finora, il DFA minimo equivalente all’automa:
✛✘0
✛✘
✲
q0 ✛
q2
0 ✚
❃
✚✙
✚✙
◗
✚
✻
◗ ✚✚
✚
◗
1
0
0
0 ✚✚
✚ ✚◗◗1 ✛✘
❄
❄
✛✘
s ✓✏
◗
✚✚
❂✚ 0
✲
q3
q1
✒✑
1
✚✙
✚✙
Si determini inoltre, dimostrando in modo formale la propria affermazione, il linguaggio riconosciuto dal DFA calcolato.
Esercizio 5.22. Si determini l’automa minimo per il linguaggio denotato
dall’espressione regolare:
(0∗ + 1∗ + (01)∗ )
CHAPTER 6
Grammatiche libere dal contesto
na grammatica è, intuitivamente, un insieme di regole che permettono di
U generare
un linguaggio. Un ruolo fondamentale tra le grammatiche è costituito dalle grammatiche libere dal contesto mediante le quali vengono solitamente
descritti i linguaggi di programmazione.
1. Definizione formale
Una grammatica libera dal contesto (CF) è una quadrupla G = hV, T, P, Si, ove:
• V è un insieme finito di variabili (dette anche simboli non terminali),
• T è un insieme finito di simboli terminali (V ∩ T = ∅),
• P è un insieme finito di produzioni; ogni produzione è della forma A → α,1
ove:
– A ∈ V è una variabile, e
– α ∈ (V ∪ T )∗ .
• S ∈ V è una variabile speciale, detta simbolo iniziale.
Esempio 6.1. Sia G la grammatica seguente:
G = {{E}, {or, and, not, (, ), 0, 1}, P, E}
ove P è costituito da:
E 7→ 0
E 7→ 1
E 7→ (E or E)
E 7→ (E and E)
E 7→ (not E)
Come vedremo, G genererà le possibili espressioni booleane.
Notazione 6.2. Per le grammatiche saranno utilizzate le seguenti notazioni:
lettere maiuscole A, B, C, D, E, S denotano variabili, S—a meno che non sia esplicitamente detto il contrario—il simbolo iniziale. a, b, c, d, e, 0, 1 denotano simboli
terminali; X, Y, Z denotano simboli che possono essere sia terminali che variabili;
1 Oppure,
equivalentemente, si può vedere come una coppia hA, αi ∈ V × (V ∪ T)∗ .
57
58
6. GRAMMATICHE LIBERE DAL CONTESTO
u, v, w, x, y, z denotano stringhe di terminali, α, β, γ, δ stringhe generiche di simboli (sia terminali che non). Se A → α1 , . . . , A → αn ∈ P, esprimeremo questo
fatto scrivendo: A → α1 | · · · |αn . Con A-produzione denoteremo una generica
produzione con la variabile A a sinistra.
La grammatica dell’esempio 6.1 può essere dunque schematicamente rappresentata da:
E 7→ 0|1|(E or E)|(E and E)|(not E) .
2. Linguaggio generato
Le grammatiche servono a generare un linguaggio. Daremo ora le definizioni
necessarie a definire il linguaggio generato da una grammatica G = hV, T, P, Si.
G G
G
Definiremo le relazioni ⇒, ⇒i , ⇒∗ ⊆ (V ∪ T )∗ × (V ∪ T )∗ nel seguente modo:
G
• se A → β ∈ P e α, γ ∈ (V ∪ T )∗ , allora αAγ ⇒ αβγ. Diremo in questo
caso che da αAγ deriva immediatamente αβγ;
• se α1 , . . . , αi ∈ (V ∪ T )∗ , i ≥ 1, e
i−1
^
j=1
G
G
αj ⇒ αj+1 ,
allora α1 ⇒i αm . In questo caso diremo che da α1 deriva αm in i passi.
G
Per ogni α, diremo anche che α ⇒0 α;
G
G
• se esiste i tale per cui α ⇒i β, allora α ⇒∗ β. Diremo in tal caso che da
α deriva β.
G
G
Si noti che ⇒∗ è la chiusura transitiva e riflessiva della relazione ⇒. In partiG
colare vale che α ⇒∗ α per ogni α ∈ (V ∪ T )∗ .
Il linguaggio generato da G è:
G
L(G) = {w ∈ T ∗ : S ⇒∗ w} .
L è un liguaggio libero dal contesto (CF) se esiste una grammatica CF G tale che
L = L(G). Due grammatiche G1 e G2 sono equivalenti se L(G1 ) = L(G2 ).
Esempio 6.3. Σ∗ è un linguaggio CF. Infatti, sia Σ = {s1 , . . . , sn }, allora Σ∗ è
generato da:
S → ε|s1 S| · · · |sn S .
Esempio 6.4. La grammatica schematicamente definita da:
genera il linguaggio
S → ASB | ε, A → 0, B → 1
{0n 1n : n ≥ 0}
(dimostrare ciò per induzione su n come esercizio).
3. ALBERI DI DERIVAZIONE
59
Per quanto dimostrato nell’esempio 5.3 usando il pumping lemma, il linguaggio
{0n 1n : n ≥ 0} non è un linguaggio regolare. Pertanto l’insieme dei linguaggi
regolari e quello dei linguaggi CF non sono lo stesso insieme. Vedremo, comunque,
che il primo insieme è incluso nel secondo.
Esempio 6.5. La grammatica dell’Esempio 6.1 genera tutte le espressioni
booleane (si dimostri questo fatto per per induzione sulla struttura dell’espressione).
3. Alberi di derivazione
Le derivazioni generate da una grammatica vengono ben visualizzate mediante
l’ausilio di strutture dati ad albero.
Sia G = hV, T, P, Si una grammatica CF. Un albero è un albero di derivazione
(parse tree) per G se:
(1) ogni vertice ha una etichetta, presa tra V ∪ T ∪ {ε};
(2) l’etichetta della radice appartiene a V;
(3) ogni vertice interno (ovvero, non una foglia) ha etichetta appartenente a
V;
(4) se un vertice n è etichettato con A e n1 , . . . , nk sono (ordinatamente, da
sinistra a destra) i vertici figli di A etichettati con X1 , . . . , Xk , allora
A → X1 · · · Xk ∈ P ;
(5) se un vertice n ha etichetta ε, allora n è una foglia ed è l’unico figlio di
suo padre.
Si noti che la definizione di albero non impone che tutte le foglie siano etichettate con simboli terminali. L’albero avente un solo nodo etichettato da S è un
caso particolare di albero di derivazione. Gli alberi verrano utilizzati per dare
una controparte grafica delle derivazioni. Se vi sono foglie etichettate con simboli
non terminali, allora l’albero rappresenta una derivazione parziale. Diremo che un
albero descrive una stringa α ∈ (V ∪ T )∗ se α è proprio la stringa che possiamo
leggere dalle etichette delle foglie da sinistra a destra.
Esempio 6.6. La grammatica dell’esempio 6.1 genera, in particolare, la stringa
w = ((0 or 1) and (not 0)). La derivazione per w è rappresentata dall’albero
seguente (che descrive w):
jjjtEJTJTJTJTTTT
jjjtjttt
JJ TTTTT
j
j
j
JJ
TTTT
jj ttt
j
j
j
J
TT
j
t
(jj
)
?
/
E
and
E
/
/
/??
/
/
?
//
// ??
?
(not E )
( E or E )
0
1
0
60
6. GRAMMATICHE LIBERE DAL CONTESTO
Dato un albero ed un suo nodo n, il sottoalbero identificato da quel nodo è
costituito da quel nodo più tutti i suoi discendenti (figli, nipoti, etc.), nonchè le
varie etichette associate ad essi.
G
Teorema 6.7. Sia G = hV, T, P, Si una grammatica CF. Allora S ⇒∗ α se e
solo se esiste un albero di derivazione con radice etichettata S per G che descrive
α.
Proof. Si proverà un enunciato più forte (e più comodo per la dimostrazione
G
induttiva), ovvero: dato un qualunque simbolo non terminale A ∈ V, A ⇒∗ α
se e solo se esiste un albero di derivazione per G che descrive α la cui radice è
etichettata A.
(→) Proviamo l’enunciato per induzione sul numero i di passi di derivazione per
G
A ⇒i α.
G
Base: i = 0: A ⇒0 α implica, per definizione, che α = A. Ma l’albero con unico
nodo (radice) etichettato con A è un albero di derivazione (parziale) che descrive
A stesso.
G
G
Passo: Supponiamo A ⇒i β1 Bβ3 ⇒1 β1 β2 β3 . Per ipotesi induttiva esiste un
| {z }
α
albero di derivazione T con radice etichettata A che descrive β1 Bβ3 . Ma per
G
definizione, β1 Bβ3 ⇒1 β1 β2 β3 se e solo se esiste la produzione B → β2 in P.
Ma allora, applicando la regola (4) di definizione di albero, dall’albero T si ottiene
l’albero che soddisa i requisiti.
(←) Proviamo l’enunciato per induzione sul numero di nodi interni dell’albero di
derivazione etichettato in A che descrive α. [Completare per esercizio]
4. Ambiguità delle derivazioni
Se ad ogni passo di una derivazione la produzione è applicata al simbolo non
terminale più a sinistra, allora la derivazione è detta sinistra (leftmost). Similmente, se viene applicata sempre a quello a destra, allora è detta destra (rightmost). Se w ∈ L(G), dal Teorema 6.7 sappiamo che per essa esiste almeno un
albero di derivazione con radice etichettata S. Si può dimostrare che, fissato un
albero di derivazione con radice etichettata S, esattamente una derivazione sinistra
(suggerimento: si visiti l’albero in preordine) ed una derivazione destra (simmetricamente) possono essere desunte.
Un albero di derivazione rappresenta dunque un certo insieme di possibili
derivazioni distinte di una stessa stringa. Ci possono tuttavia essere più alberi di
derivazione per la stessa parola:
Esempio 6.8. Si consideri la grammatica
Una derivazione sinistra è:
G
E → E + E|E ∗ E|0|1|2 .
G
G
E ⇒ E + E ⇒ E ∗ E + E ⇒3 2 ∗ 0 + 1 .
5. SEMPLIFICAZIONE
61
Un’altra derivazione sinistra per la stessa stringa è:
G
G
G
G
E ⇒ E ∗ E ⇒ 2 ∗ E ⇒ 2 ∗ E + E ⇒2 2 ∗ 0 + 1 .
Tuttavia gli alberi associati alle due derivazioni (che potrebbero essere visti come
alberi per la computazione dell’espressione associata alla stringa generata) sono
diversi:
oEOOOO
OOO
ooo
o
o
OOO
oo
o
OO
o
o
o
+
E??
E
???
??
∗
1
E
E
2
oEOOOO
OOO
ooo
o
o
OOO
oo
o
OO
o
o
o
∗
E
E??
???
??
+
2
E
E
0
0
1
Il primo è intuitivamente associato a (2∗0)+1 = 1 il secondo, invece, 2∗(0+1) =
2.
Una grammatica CF tale per cui esiste una parola con più di un albero di
derivazione con radice etichettata S è detta ambigua. Un linguaggio CF per cui
ogni grammatica che lo genera è ambigua è detto essere inerentemente ambiguo.
Un esempio di tali linguaggi è:
L = {an bn cm dm : n ≥ 1, m ≥ 1} ∪ {an bm cm dn : n ≥ 1, m ≥ 1} .
La dimostrazione di questa proprietà (3 pagine!) si può trovare in [13].
5. Semplificazione
Vi sono molti modi per restringere la forma delle produzioni permesse alle
grammatiche CF senza alterare la classe dei linguaggi riconosciuti dall’insieme delle
possibili grammatiche. In questo paragrafo si presenteranno due di queste trasformazioni, che conducono alla forma normale di Chomsky e a quella di Greibach.
Mostreremo che ogni linguaggio CF può essere generato da una grammatica
G dalle seguenti due proprietà:
(1) ogni variabile e ogni simbolo terminale di G compaiono in almeno una
derivazione di una qualche parola di L;
(2) non ci sono produzioni della forma A → B con A e B variabili.
Se, inoltre, ε ∈
/ L, non avremo bisogno di produzioni A → ε; sempre sotto tale
ipotesi potremo richiedere che ogni produzione sia della forma A → BC o A → a
(Chomsky) oppure, alternativamente, che ogni produzione sia della forma A → aα
ove α è una stringa di variabili (Greibach).
Nel prosieguo del capitolo alcune dimostrazioni tecniche ma dimostrabili immediatamente per induzione saranno lasciate per esercizio.
62
6. GRAMMATICHE LIBERE DAL CONTESTO
5.1. Eliminazione di simboli inutili. Sia G = hV, T, P, Si una grammatica
CF. Un simbolo X ∈ V ∪ T è utile se esiste una derivazione
G
G
S ⇒∗ αXβ ⇒∗ w ;
altrimenti è detto inutile. Mostreremo come simboli inutili si possano eliminare in
due passi.
Innanzitutto si eliminano le variabili che non conducono in nessun modo ad una
stringa di terminali.
Lemma 6.9. Data una grammatica CF G = hV, T, P, Si tale che L(G) 6= ∅, si
può calcolare in modo effettivo una grammatica equivalente G ′ = hV ′ , T, P ′ , Si tale
G
che per ogni A ∈ V ′ esiste w ∈ T ∗ tale che A ⇒∗ w.
Proof. Per calcolare V ′ usiamo il seguente operatore Γ : ℘(V) −→ ℘(V):
Γ (W) = {A ∈ V : (∃α ∈ (T ∪ W)∗ )(A → α ∈ P)}
Γ è monotono—ovvero X ⊆ Y → Γ (X) ⊆ Γ (Y)—per definizione. Definiamo inoltre
l’applicazione iterata di un operatore:
Γ 0 (W) = W
Γ i+1 (W) = Γ (Γ i (W))
Per induzione su i ≥ 1, si mostra simultaneamente su tutti gli A ∈ T che:
esiste un albero di derivazione che descrive w ∈ T ∗
sse A ∈ Γ i (∅)
con radice etichettata con A e di altezza ≤ i
(completare per esercizio).
G
Dunque, per il Teorema 6.7, si ha che A ∈ Γ i (∅) per qualche i sse A ⇒∗ w.
Poiché V è finito e Γ monotono, si ha che esiste i ≤ |V| tale per cui
Γ i (∅) = Γ (Γ i (∅)) = Γ i+1 (∅)(= Γ i+2 (∅) = · · · )
Pertanto si riesce a calcolare finitamente l’insieme V ′ = Γ |V| (∅), e P ′ = {A → α ∈
P : A ∈ V ′ ∧ α ∈ (V ′ ∪ T )∗ }.
L’ipotesi che il linguaggio sia non vuoto è indispensabile. Se infatti fosse
L = ∅, allora l’applicazione del Lemma 6.9 fornirebbe una grammatica con S ∈
/ V′
e dunque priva di significato in quanto S sarebbe il simbolo iniziale ma non apparterrebbe alla grammatica. In ogni caso, per definizione, ogni simbolo di una
grammatica che genera un linguaggio vuoto è inutile. Si osservi che l’applicazione
della procedura del lemma permette anche di determinare se il linguaggio riconosciuto da un automa sia vuoto o meno.
Fatto ciò eliminiamo le variabili che non sono mai raggiunte da S:
5. SEMPLIFICAZIONE
63
Lemma 6.10. Data una grammatica CF G = hV, T, P, Si, si può calcolare in
modo effettivo una grammatica equivalente G ′ = hV ′ , T ′ , P ′ , S ′ i tale che per ogni
G
X ∈ (V ′ ∪ T ′ ) esistono α e β in (V ′ ∪ T ′ )∗ per cui S ⇒∗ αXβ.
Proof. Definiamo un operatore Γ : ℘(V ∪ T ) −→ ℘(V ∪ T ):
Γ (W) =
{X ∈ V ∪ T : (∃A ∈ W)(A → αXβ ∈ P) } ∪ {S}
Per induzione su i ≥ 0, si mostra (esercizio) che:
esiste un albero di derivazione (parziale)
con radice etichettata con S di altezza ≤ i
in cui X compare come foglia
sse
X ∈ Γ i ({S})
Pertanto, per il Teorema 6.7 si ha che X ∈ Γ i ({S}) per qualche i se e solo se esistono
G
α e β in (V ∪ T )∗ per cui S ⇒∗ αXβ.
Γ è monotono, V e T sono finiti, dunque esiste i ≤ |V| + |T | tale che
Γ i ({S}) = Γ i+1 ({S})(= Γ i+2 ({S}) = · · · )
Siano dunque:
• V ′ = Γ |V|+|T | ({S}) ∩ V;
• T ′ = Γ |V|+|T | ({S}) ∩ T ;
• P ′ = {A → α ∈ P : A ∈ V ′ ∧ α ∈ (V ′ ∪ T ′ )∗ }.
Si osservi come, introducendo anche ε in Γ (W) qualora A −→ ε ∈ P e A ∈ W,
G
saremmo anche in grado di capire se S ⇒∗ ε.
Teorema 6.11. Ogni linguaggio CF non vuoto è generato da una grammatica
CF priva di simboli inutili.
Proof. Immediato, applicando prima il Lemma 6.9 e poi il Lemma 6.10.
Esercizio 6.12. Si cerchi una grammatica con linguaggio non vuoto tale per
cui applicando prima la procedura del Lemma 6.10 e poi quella del Lemma 6.9
non si giunge all’eliminazione dei simboli inutili.
5.2. Eliminazione di ε-produzioni. Data una grammatica CF che genera
un linguaggio L, si desidera eliminare le produzioni della forma A → ε. Qualora
ε ∈ L, ammetteremo la presenza della produzione S → ε.
G
Il metodo è quello di determinare quali variabili A ∈ V sono tali che A ⇒∗ ε.
Tali variabili sono dette annullabili. Ciò che poi si farà sarà quello di eliminare,
tra le Xi di ogni produzione A → X1 · · · Xn zero, una, . . . , o tutte le variabili
annullabili. Se togliendo tutte le variabili annullabili da X1 · · · Xn la stringa diventasse vuota, allora anche A sarà annullabile (ma non aggiungeremo la produzione
A → ε, a meno che A non sia S).
64
6. GRAMMATICHE LIBERE DAL CONTESTO
Teorema 6.13. Se L = L(G) per qualche grammatica CF G = hV, T, P, Si,
allora L \ {ε} è un linguaggio CF generato da una grammatica G ′ = hV ′ , T ′ , P ′ , S ′ i
senza simboli inutili e senza ε-produzioni.
Proof. Definiamo un operatore Γ : ℘(V) −→ ℘(V):
Γ (W) =
{A ∈ V :
(∃A → α ∈ P)
α “privata” delle variabili di W è ε}
Anche qui, Γ è monotono, V è finito, dunque esiste i ≤ |V| tale che Γ i (∅) =
Γ i+1 (∅). Siano dunque N = Γ |V| (∅) e
P ′′
=
{A → α1 · · · αn :
A → X1 · · · Xn ∈ P,
se Xi ∈
/ N, allora αi = Xi ,
se Xi ∈ N, allora αi = Xi o αi = ε,
α1 · · · αn 6= ε }
E’ immediato mostrare che L(G) = L(hV, T, P ′′ , Si). Applichiamo dunque il Teorema 6.11 alla grammatica hV, T, P ′′ , Si.
Ovviamente, se ε ∈ L, aggiungeremo la produzione S → ε. Si osservi come
questa procedura permetta di capire se ε ∈ L. E’ infatti sufficiente vedere se
esiste in P una produzione S → A1 · · · Ak con le Ai ∈ N per i = 1, . . . , k, oppure
direttamente la produzione S → ε.
5.3. Eliminazione di produzioni unitarie. Una produzione della forma
A → B, con A e B variabili, si dice unitaria.
Teorema 6.14. Ogni grammatica CF senza ε è definita da una grammatica
senza simboli inutili, senza ε-produzioni e senza produzioni unitarie.
Proof. Grazie al Teorema 6.13, possiamo partire da una grammatica senza
simboli inutili e senza ε-produzioni. Innanzitutto identifichiamo tutte le coppie
G
di variabili A, B che permettono derivazioni della forma A ⇒∗ B (immediato,
seguendo i cammini del grafo ottenuto prendendo come nodi le variabili e come
archi le produzioni unitarie). Sia, per ogni variabile A, seguenti(A) l’insieme delle
variabili B soddisfacenti la richiesta sopra.
A questo punto togliamo da P tutte le produzioni unitarie e, per ogni A ∈ V e per
ogni produzione non unitaria B → β ∈ P con B ∈ seguenti(A), B 6= A, aggiungo la
produzione A → β.
Dimostrare l’equivalenza dei linguaggi è immediato (induzione sul numero di produzioni unitarie presenti in una derivazione). Tuttavia, la procedura potrebbe aver
generato simboli inutili (si pensi alla grammatica S −→ A, A −→ a): applichiamo
il Teorema 6.11 (si noti che produzioni unitarie non possono essere introdotte dalla
procedura di eliminazione di simboli inutili) e otteniamo il risultato cercato.
7. FORMA NORMALE DI GREIBACH
65
6. Forma normale di Chomsky
Teorema 6.15 (Chomsky, 1959). Ogni linguaggio CF senza ε è generato da
una grammatica in cui tutte le produzioni sono della forma A → BC e A → a.
Proof. Per il Teorema 6.14 possiamo partire da una grammatica G = hV, T, P, Si
senza ε-produzioni, simboli inutili e produzioni unitarie.
Sia A → X1 · · · Xm ∈ P, m ≥ 1 (non vi sono ε-produzioni):
• se m = 1, allora, poiché non vi sono produzioni unitarie, X1 ∈ T : è già
nella forma cercata;
• se m = 2 e X1 , X2 ∈ V, siamo nella forma cercata;
• se m ≥ 2 e qualcuno degli Xi ∈ T , allora per ogni Xi ∈ T introduco in
V una nuova variabile, diciamo Bi , aggiungo la produzione Bi → Xi e
rimpiazzo la produzione A → X1 · · · Xm con A → Y1 · · · Ym , ove Yi = Xi
se Xi ∈ V, Yi = Bi altrimenti;
• se m > 2 e tutti gli Xi sono variabili, allora rimpiazzo la produzione
A → X1 · · · Xm con le produzioni A → BX3 · · · Xm , B → X1 X2 , ove B è
una nuova variabile da aggiungere a V.
E’ immediato verificare che il procedimento termina e che l’insieme finale di produzioni (P ′ ) è nella forma normale di Chomsky. Si tratta di verificare che le
grammatica G ′ = hV ′ , T, P ′ , Si (ove V ′ si ottiene da V con l’aggiunta delle nuove
variabili) ottenuta è equivalente a quella di partenza. Ovvero, per A ∈ V:
G
G′
A ⇒∗ w sse A →∗ w
G
Dimostriamo, per induzione su i ≥ 1 che A ⇒i w implica che esiste k tale per cui
G′
A →i+k w. [Esercizio]
G′
G
Viceversa, dimostriamo, per induzione su i ≥ 1 che A →i w implica che A ⇒j w
per qualche j ≤ i. [Esercizio]
7. Forma normale di Greibach
Per raggiungere questa forma normale abbiamo bisogno di due lemmi preliminari.
Lemma 6.16 (Unfolding). Sia G = hV, T, P, Si una grammatica CF, sia A →
αBγ una produzione di P e B → β1 | · · · |βn l’insieme delle B-produzioni. Sia G ′ =
hV, T, P ′ , Si ottenuta da G eliminando la produzione A → αBγ da P e aggiungendo
le produzioni A → αβ1 γ| · · · |αβn γ. Allora L(G) = L(G ′ ).
Proof. (Esercizio)
Lemma 6.17 (Eliminazione ricorsione sinistra). Sia G = hV, T, P, Si una grammatica CF e sia A → Aα1 | · · · |Aαm l’insieme delle A-produzioni di G per cui A
è anche il simbolo più a sinistra della stringa di destra delle produzioni. Siano
66
6. GRAMMATICHE LIBERE DAL CONTESTO
A → β1 | · · · |βn le rimanenti A-produzioni. Sia G ′ = hV ∪ {B}, T, P ′ , Si la grammatica CF ottenuta aggiungendo una nuova variabile B a V e rimpiazzando tutte
le A-produzioni con:
(1) A → βi |βi B per i ∈ {1, . . . , n};
(2) B → αi |αi B per i ∈ {1, . . . , m}.
Allora L(G ′ ) = L(G).
G
Proof. Mostriamo prima che se x ∈ L(G) allora x ∈ L(G ′ ). Se S ⇒∗ x e
non vi sono in essa produzioni della forma A → Aαi non c’è nulla da dimostrare.
Altrimenti, sia
G
G
G
G
G
G
A ⇒ Aαi1 ⇒∗ Aγ1 ⇒ Aαi2 γ1 ⇒∗ Aγ2 ⇒ Aαi3 γ2 ⇒∗ · · · · · ·
una sottoderivazione in cui tali produzioni si usano (e vengono esplicitate: nei
G
tratti ⇒∗ assumiamo che tali derivazioni non ci sono). Poiché alla fine si deve
ottenere una stringa di terminali, ad un certo punto la A sempre presente in testa
sarà rimpiazzata da una βj usando la produzione A → βj . La derivazione può
essere mimata con G ′ nel modo seguente:
G
Aαi1
⇒
G
Aαjk γk−1
G
βj B
⇒∗ · · ·
βj Bγk−1
⇒
A
Aγk−1
A
βj Bαj2 γ1
G
⇒
⇒∗
G
Aγi1
G
Aγk
G
βj Bαi1
⇒∗
⇒∗
G
⇒
βj αik−1 γk−1
L’implicazione inversa è lasciata per esercizio.
G
⇒∗
Aαi2 γ1
G
βj Bγ1
G
βj γk
G
⇒
⇒∗
⇒∗
βj γk
G
⇒∗ · · ·
G
⇒∗
Teorema 6.18 (Greibach, 1965). Ogni linguaggio CF senza ε è generato da
una grammatica in cui tutte le produzioni sono della forma A → aα, ove α è una
stringa (eventualmente vuota) di simboli non terminali.
Proof. Sia G una grammatica in forma normale di Chomsky. Sia V =
{A1 , . . . , Am }. Si costruirà in tre passi una grammatica G ′ in forma normale di
Greibach equivalente a G.
(1) Si applichi il seguente algoritmo:
for k := 1 to m do
begin
for j := 1 to k − 1 do
elimina le produzioni Ak → Aj α mediante unfolding;
end;
elimina le produzioni Ak → Ak α mediante elim. ricors. sinistra
7. FORMA NORMALE DI GREIBACH
67
Nuove variabili B1 , . . . , Bh sono state introdotte dalla fase di eliminazione
della ricorsione sinistra. L’equivalenza tra G e la grammatica ottenuta
deriva dai lemmi appena dimostrati.
A questo punto tutte le produzioni sono della forma:
Ai
Ai
Bi
→ Aj α j > i
→ aβ
a∈T
→ γ
Inoltre, poiché siamo partiti da una grammatica in forma normale di
Chomsky, si osserva che α ha un prefisso della forma Ai1 · · · Aih seguito
da un simbolo non terminale, seguito ancora da una sequenza eventualmente vuota di variabili. Per lo stesso motivo, β è una stringa di variabili.
Per quanto riguarda le stringhe γ, esse non iniziano mai con un Bi .
(2) Pertanto, ogni produzione Am → α ha la parte destra iniziante con
un simbolo terminale. Posso effettuare l’unfolding delle Ai -produzioni
rimpiazzando Am con ciascuna delle sue possibili parti destre. Posso iterare questo procedimento all’indietro, ottenendo produzioni della forma:
Ai
→ aβ a ∈ T
per ogni i = 1, . . . , m, con β stringa di variabili.
(3) Si tratta ora di semplificare le produzioni Bi → γ. Applicando la sostituzione sul primo (eventuale) simbolo Ai della stringa, si giunge al
risultato.
Esercizio 6.19. Si applichi la procedura descritta per condurre in forma normale di Greibach la grammatica dalle produzioni:
A1
−→ A3 A3 |a
A2
−→ A1 A2 |b
A3
−→ A3 A1 |c
Esercizio 6.20. Si riconduca alle forme normali di Chomsky e di Greibach la
grammatica:
S −→ ε|aSb
Un importante caso particolare di grammatiche in forma normale di Greibach
sono le grammatiche CF in cui le produzioni hanno tutte la forma A → a oppure
A → aB. Tali grammatiche, studiate nel Capitolo 9, sono grammatiche lineari
destre ed hanno l’importante caratteristica di generare esattamente i linguaggi
regolari.
CHAPTER 7
Automi a pila
n questo capitolo descriviamo una macchina astratta per il riconoscimento dei
I linguaggi
CF. Questa macchina corrisponde ad un arricchimento degli automi
già visti per il caso dei linguaggi regolari, mediante una struttura dati opportuna
per memorizzare informazione: la pila. Abbiamo visto che, grazie al pumpinglemma per i linguaggi regolari, il linguaggio
n n
a b : n≥0
non è regolare. Intuitivamente l’impossibilità di riconoscere questo linguaggio
da parte di un automa a stati finiti è legata all’impossibilità da parte di questi
automi di memorizare una quantità arbitrariamente grande (anche se finita) di
simboli e ricordarne il numero. Solo in questo modo infatti si può pensare di
poter confrontare il numero di occorrenze di “a” e “b” nella stringa da leggere.
Una struttura dati adeguata per risolvere questo problema è la pila, ovvero una
struttura dati LIFO (Last In First Out). Da un punto di vista architetturale, un
automa a pila è una macchina costituita da un controllo che è un automa a stati
finiti, un nastro di input che, come negli NFA e DFA, è di sola lettura, ed una
pila sulla quale sono possibili le operazioni di: push(X), pop(X), empty (si veda
la Figura 1):
• L’istruzione push(X) sposta una stringa X in testa alla pila,
• l’istruzione pop(X) preleva il simbolo in testa alla pila, e
• l’istruzione empty verifica, restituendo valore booleano, se la pila è vuota
o meno.
s1
s2
s3
· · · nastro · · ·
Z2
Z3
· · · pila · · ·
⇑
q
m
Z1
Figure 1. Automa a pila
69
70
7. AUTOMI A PILA
Le azioni possibili di un automa a pila sono:
• Leggere dal nastro e dalla pila (pop) contemporaneamente
– avanzare la testina a destra sul nastro
– cambiare stato nel controllo
– inserire una stringa (anche vuota) sulla pila (push)
• Leggere solo dalla pila (pop)
– cambiare stato nel controllo
– inserire una stringa (anche vuota) sulla pila (push)
Definizione 7.1. Un automa a pila non-deterministico (APND) M è una
7-upla:
M = hQ, Σ, R, δ, q0 , Z0 , Fi
dove:
•
•
•
•
•
•
•
Q è un insieme finito di stati
Σ è un alfabeto finito
R è l’alfabeto finito della pila
q0 ∈ Q è lo stato iniziale
Z0 ∈ R è il simbolo iniziale sulla pila
F ⊆ Q è l’insieme degli stati finali
δ : Q × (Σ ∪ {ε}) × R → ℘f (Q × R∗ ) è la funzione di transizione
L’evoluzione di un automa a pila è determinata dall’evoluzione delle sue descrizioni istantanee. Una descrizione istantanea rappresenta la “fotografia” dello
stato (stato + nastro + pila) della macchina ad un certo istante.
Definizione 7.2. Una descrizione istantanea per un APND è una tripla
(q, x, γ)
dove q ∈ Q è lo stato corrente, x ∈ Σ∗ è la stringa ancora da leggere sul nastro e
γ ∈ R∗ è la sequenza di simboli contenuti sulla pila. Se (p, γ) ∈ δ(q, a, Z) allora è
definito un passo di derivazione dell’APND:
(q, aw, Zα) 7→M (p, w, γα)
Il linguaggio L(M) riconosciuto da un APND M si può definire in due modi
diversi: per pila vuota Lp (M) o per stato finale LF (M):
Lp (M) =
x ∈ Σ∗ : (q0 , x, Z0 ) 7→∗M (q, ε, ε), q ∈ Q
LF (M) =
x ∈ Σ∗ : (q0 , x, Z0 ) 7→∗M (q, ε, γ), γ ∈ R∗ , q ∈ F
Proposizione 7.3. Per ogni APND M, esiste un APND M ′ tale che LF (M) =
Lp (M ′ ).
Nel seguito, quando l’APND è previsto per riconoscere le stringhe per pila vuota,
assumeremo F = ∅.
7. AUTOMI A PILA
71
Esempio 7.4. Consideriamo l’APND che riconosce, per pila vuota, il linguaggio xcxr , dove c ∈ {a, b}∗ e xr è la stringa inversa della stringa x (ad esempio, se
x = aab allora xr = baa). L’APND è definito nel modo seguente:
h{q0 , q1 }, {a, b, c}, {Z, A, B}, δ, q0, Z, ∅i
Dove δ risulta definita dalla seguente matrice (le celle vuote possono essere definite
in qualunque modo che conduca sicuramente ad una refutazione):
ε
q0
a
Z
b
c
q1
q0 , ZA q0 , ZB q1 , ε
ε
a
b
c
Z
q1 , Z q1 , Z
A
A
q1 , ε
q1 , Z q1 , Z
B
B
q1 , Z
q1 , ε
q1 , Z
Ad esempio, il riconoscimento per pila vuota della stringa abcba avviene come
segue:
a
bcba
↑
a
l
pila
⇒
l
pila
Z
b
↑
a
nastro
q1
l
B
abcb
pila
A
⇒
ab
⇒
l
pila
⇒
BA
abcba
nastro
q1
A
nastro
Z
a
l
ba
q0
A
↑
c
↑
nastro
q0
Z
abc
cba
↑
nastro
q0
b
pila
⇒
↑
nastro
q1
l
pila
Si osserva che il precedente APND è in realtà deterministico. Al contrario di
quanto accade per gli automi a stati finiti gli automi a pila non-deterministici
sono strettamente più potenti di quelli deterministici. Nel caso del linguaggio
dell’esempio precedente, questo è facilmente riconoscibile da un automa a pila deterministico in quanto esso è costituito da tutte e sole le stringhe del tipo xcxr .
La presenza del simbolo ausiliario c a delimitare le due parti simmetiche della
stringa, permette di avere sulla stringa stessa, un elemento di demarcazione tra le
fasi di caricamento della pila (la lettura della stringa x) ed il suo scaricamento (la
lettura della stringa xr ). In questo modo, il riconoscimento della stringa avviene
72
7. AUTOMI A PILA
in modo deterministico: non è necessario attivare nessun processo parallelo di verifica alla ricerca del punto mediano della stringa. Al contrario, ciò non è possibile
nel riconoscimento del linguaggio CF delle stringe palindrome. In questo caso,
per determinare se la lettura del simbolo corrente sul nastro corrisponde alla lettura del simbolo mediano della stringa, da cui inizia lo scaricamento della pila, ci
appoggiamo ad una computazione non-deterministica.
È importante notare che gli automi a pila deterministici sono solitamente
utilizzati per il riconoscimento sintattico dei linguaggi di programmazione. In
particolare, essi costituiscono la base per la realizzazione di compilatori (fase di
parsing). A tal proposito, si noti come la sintassi degli usuali linguaggi di programmazione sia strutturata in modo tale da interporre tra categorie sintattiche
dei simboli o sequenze di simboli marcatori, ad esempio if, while, then, etc.,
come nel caso dell’Esempio 7.4. Questo permette di realizzare la fase di parsing
del linguaggio attraverso un APD.
Esempio 7.5. Costruiamo un APND
che riconosca il linguaggio delle stringhe
palindrome sull’alfabeto {a, b}: L = wwr : w ∈ {a, b}∗ . L’automa è definito
come segue:
• Q = {q0 , q1 }
• Σ = {a, b}
• R = {Z, A, B}
e relazione di transizione:
q0
ε
a
b
Z
q0 , AZ
q0 , BZ
q1
ε
A
q0 , AA q0 , BA
Z
q1 , ε
q1 , ε
B
q0 , AB
A
q0 , BB
B
a
b
q1 , ε
q1 , ε
q1 , ε
Esercizio 7.6. Scrivere le derivazioni necessarie per riconoscere la stringa:
abbba.
Lemma 7.7. Se L = Lp (M) con M APND, allora L = Lp (M ′ ) con M ′ APND
con 1 solo stato.
Proof. (Idea). Sia M = hQ, Σ, R, δ, q0 , Z0 , Fi. L’idea è quella di aumentare
i simboli nella pila per descrivere ogni coppia (qi , Zi ) con qi ∈ Q e Zi ∈ R. Per
ogni coppia di questo tipo, si aggiunge un nuovo simbolo Zi′ in R e si modifica δ in
modo tale che se Z ′ è il simbolo nuovo in corrispondenza della coppia (q, Z), allora:
δ(q, a, Z) = δ ′ (q0 , a, Z ′ ). Il lemma segue quindi banalmente per costruzione.
I risultati seguenti dimostrano l’equivalenza tra i linguaggi riconosciuti dagli
APND e i linguaggi CF.
7. AUTOMI A PILA
73
Teorema 7.8. Se L = Lp (M) con M APND con 1 stato, allora L è CF.
Proof. Sia M = h{q}, Σ, R, δ, q, Z0 , ∅i un APND con 1 solo stato che riconosce il linguaggio Lp (M). Definiamo la grammatica libera da contesto: G =
hR, Σ, Z0 , Pi, avente R come alfabeto di simboli non terminali, Z0 ∈ R come simbolo
iniziale e le seguenti produzioni:
Z → aZ1 Z2 · · · Zn se (q, Z1 Z2 · · · Zn ) ∈ t(q, a, Z)
È ovvio che se α ∈ R∗ , allora:
G
(q, a, Zα) 7→M (q, ε, Z1 Z2 · · · Zn α) sse Zα ⇒ aZ1 Z2 · · · Zn α
Per induzione si dimostra che per ogni x ∈ Σ∗ :
da cui segue la tesi.
G
(q, x, Z0 ) 7→∗M (q, ε, ε) sse Z0 ⇒ x
Il seguente teorema stabilisce l’equivalenza tra i linguaggi riconosciuti dagli
APND ed il linguaggi CF. Prima di vedere questo risultato, osserviamo l’analogia
tra la derivazione da una grammatica CF ed i passi di caricamento/svuotamento
della pila durante le varie fasi di riconoscimento delle sottostringhe di una data
stringa da parte di un APND.
Esempio 7.9. Consideriamo la seguente grammatica G in forma normale di
Greibach.
S → aB
A → a
S
→ bA
B
A → aS
B
A → bAA
B
→ bS
→ aBB
→ b
Sia M l’APND con 1 solo stato q definito dalla seguente relazione di transizione:
a
b
S
q, B
q, A
A
q, ε
q, AA
q
ε
q, S
B
q, BB
q, ε
q, S
Si noti come le seguenti derivazioni siano del tutto simmetriche:
Teorema 7.10. Sia L CF. Allora esiste un APND M tale che L = Lp (M).
Proof. Sia L = L(G) con G = hV, T, P, Si grammatica CF in forma normale
di Greibach. Definiamo:
74
7. AUTOMI A PILA
S
(q, aababb, S)
aB
(q, ababb, B)
aaBB
??
??
??
aabSB
aabB
(q, babb,
??BB)
??
??
(q, abb, B) (q, abb, SB)
aabaBB
aabaBB
??
??
??
aababB
aababSB
o
o
o
ooo
wwooo
aababb aababbAB
aababbS
(q, bb, BB) (q, bb,?BB)
???
??
(q,
b, B)
(q, b, SB)
o
o
o
ooo
wwooo
(q, ε, ε)
(q, ε, S)
(q, ε, AB)
Figure 2. Simmetria tra albero di derivazione e APND.
e dove:
•
•
•
•
•
Q := {q}
Σ := T
R := V
Z0 := S
F := ∅
(q, α) ∈ t(q, a, A) sse A → aα ∈ P
Si osserva che:
G
xAβ ⇒1 xaαβ dove α, β ∈ V ∗ e x ∈ T ∗
Ma questo avviene se e solo se l’APND cosı̀ definito fa una mossa del tipo:
(q, a, Aβ) 7→1M (q, ε, αβ)
G
Per induzione [completare per esercizio] sul numero di passi della derivazione ⇒
si dimostra che:
G
xAβ ⇒n xyαβ sse (q, y, Aβ) 7→n
M (q, ε, αβ)
G
Ne consegue che: S ⇒∗ y sse (q, y, S) 7→∗M (q, ε, ε).
CHAPTER 8
Proprietà dei linguaggi liberi dal contesto
i linguaggi CF valgono delle proprietà simili a quelle studiate per i linguaggi
P erregolari
nel Capitolo 5.
1. Il pumping lemma per i linguaggi CF
Lemma 8.1 (Bar-Hillel, Perles, Shamir, 1961). Sia L un linguaggio CF. Allora
c’è una costante n ∈ N (dipendente dal linguaggio L) tale che per ogni z ∈ L, |z| ≥
n, esistono stringhe uvwxy tali che
(1) z = uvwxy,
(2) |vx| ≥ 1,
(3) |vwx| ≤ n, e
(4) per ogni i ≥ 0 vale che uvi wxi y ∈ L.
Proof. Sia G = hV, T, P, Si la grammatica che genera L \ {ε}. Senza perdita di
generalità, possiamo assumere che G sia in forma normale di Chomsky (se ε ∈ L,
potremmo costruire una nuova grammatica G ′ che estende G con un nuovo simbolo
iniziale S ′ e le produzioni S ′ → ε e S ′ → S e modificare appena la dimostrazione—
si noti che G ′ non è in forma normale. Poiché il significato del lemma è per stringhe
“lunghe”, non complicheremo la dimostrazione in questo modo).
Per induzione su i ≥ 1, si mostra (esercizio) che se un albero di derivazione
per una stringa z ∈ T ∗ ha tutti i cammini di lunghezza minore o uguale a i, allora
|z| ≤ 2i−1 .
Sia |V| = k (k > 0) e sia n = 2k (n > 1). Se z ∈ L e |z| ≥ n, allora ogni
albero di derivazione per z deve avere un cammino di lunghezza almeno k + 1. Ma
tale cammino ha almeno k + 2 nodi, tutti, eccetto l’ultimo, etichettati da variabili.
Pertanto vi deve essere una variabile ripetuta nel cammino.
In particolare, dunque, vi devono essere due vertici v1 , v2 tali che:
(1) entrambi sono etichettati dalla stessa variabile, diciamo A;
(2) v1 è più vicino alla radice di v2 ;
(3) poiché n ≥ 2, a v1 è associata una produzione del tipo: A → BC;
(4) da v1 alla foglia al più la lunghezza è k + 1.
Si prenda ora il sottoalbero T1 con radice in v1 ; esso rappresenta una derivazione
di una parola z1 di lunghezza al più 2k . Sia z2 invece la parola relativa al sottoalbero T2 di T1 con radice in v2 . Possiamo scrivere z1 = vz2 x. Inoltre almeno una
75
76
8. PROPRIETÀ DEI LINGUAGGI LIBERI DAL CONTESTO
tra v e x deve essere non vuota, in quanto a v1 è associata una produzione del
tipo: A → BC ed il sottoalbero T2 è un sottoalbero di esattamente uno dei due
sottoalberi individuati da B e C.
Allora abbiamo che:
G
A ⇒∗ vAx
e
G
A ⇒∗ z2
ove |vz2 x| ≤ 2k = n. Ma allora ne segue anche che
G
A ⇒∗ vi z2 xi
per ogni i ≥ 0. Si ponga w = z2 e u e y il prefisso e il suffisso di z necessari
affinchè z = uvwxy.
Esempio 8.2. Il linguaggio {ai bi ci : i ≥ 1} non è CF. Supponiamo per assurdo
che lo sia. Come fatto nel caso dei linguaggi regolari (Lemma 5.1), dobbiamo
mostrare la veridicità del complemento della formula dimostrata nel lemma, ovvero
che per ogni n ≥ 1 sappiamo trovare una stringa z ∈ L, |z| ≥ n che soddisfa
una certa condizione. Scegliamo z = an bn cn . Dobbiamo mostrare che per ogni
quintupla di stringhe u, v, w, x, y tali che an bn cn = uvwxy, |vx| ≥ 1 e |vwx| ≤ n
esiste un i ≥ 0 tale per cui uvi wxi y ∈
/ L.
Vi sono molti modi di partizionare z in uvwxy che soddisfano i requisiti.
Tuttavia questi si possono raggruppare nelle seguenti casistiche:
(1) uvwx = ah con h ≤ n
(2) u = ah , vwx = ak bm con k > 0, k + m ≤ n
(3) u = an bh , vwx = bm con m ≤ n
(4) u = an bh , vwx = bk cm con k + m ≤ n
(5) u = an bn ch , vwx = cm con m ≤ n
In ciascuna delle casistiche, tuttavia, si ha che uv0 wx0 y = uwy ∈
/ L, in quanto
vengono alterati al massimo due tra i numeri di ripetizioni dei tre tipi di carattere
a, b e c.
Esercizio 8.3. Si dimostri che i seguenti linguaggi non sono liberi dal contesto:
(1) {0a 1b 0ab : a, b ∈ N, a, b > 0}
b
(2) {0a 1b 0a : a, b ∈ N, a, b > 0}
(3) {0a 1b 0a 1b : a, b ∈ N, a, b > 0}
2. Proprietà di chiusura
Teorema 8.4. I linguaggi CF sono chiusi rispetto all’unione, alla concatenazione, ed alla chiusura di Kleene.
Proof. La dimostrazione è costruttiva. Siano G1 = hV1 , T1 , P1 , S1 i e G2 =
hV2 , T2 , P2 , S2 i le grammatiche generanti i linguaggi L1 e L2 . Per semplicità si assuma che V1 e V2 siano disgiunti (altrimenti si proceda a ridenominare le variabili).
Sia S una nuova variabile.
3. ALGORITMI DI DECISIONE
77
Allora
• G∪ = hV1 ∪ V2 ∪ {S}, T1 ∪ T2 , P1 ∪ P2 ∪ P, Si, ove P consta delle produzioni:
S → S1 |S2
è la grammatica che genera L1 ∪ L2 .
• G◦ = hV1 ∪ V2 ∪ {S}, T1 ∪ T2 , P1 ∪ P2 ∪ P, Si, ove P consta delle produzioni:
S → S1 S2
è la grammatica che genera L1 L2 .
• G∗ = hV1 ∪ {S}, T1 , P1 ∪ P, Si, ove P consta delle produzioni:
S → ε|S1 S
è la grammatica che genera L∗1 .
Teorema 8.5. I linguaggi CF non sono chiusi rispetto all’intersezione.
Proof. Consideriamo L1 = {ai bi cj : i ≥ 1, j ≥ 1} generato da:
i j j
S → RC, R → ab | aRb, C → c | cC
e L2 = {a b c : i ≥ 1, j ≥ 1} generato da:
S → AR, R → bc | bRc, C → c | cC
i i i
La loro intersezione è {a b c : i ≥ 1}, mostrato essere non CF nell’esempio 8.2.
Corollario 8.6. I linguaggi CF non sono chiusi rispetto alla complementazione.
Proof. Se lo fossero, allora lo sarebbero anche rispetto all’intersezione, in
quanto A ∩ B = A ∪ B, contraddicendo il Teorema 8.5.
3. Algoritmi di decisione
Teorema 8.7 (Vuoto, Finito, Infinito). Data una grammatica CF G = hV, T, P, Si,
i problemi:
(1) L(G) = ∅,
(2) L(G) è finito,
(3) L(G) è infinito,
sono decidibili.
Proof. 1) Come osservato nel paragrafo 5.1, se la grammatica ottenuta dall’applicazione
dell’algoritmo presente nella dimostrazione del Lemma 6.9 è tale che S ∈
/ V ′ , allora
il linguaggio è ∅. Altrimenti no.
2,3) Se L(G) = ∅ oppure L(G) = {ε} (entrambe proprietà che si dimostrano
durante la normalizzazione) il linguaggio è chiaramente finito. Altrimenti, G ′ =
hV ′ , T, P ′ , Si una grammatica t.c. L(G ′ ) = L(G) \ ε in forma normale di Chomsky
e priva di simboli inutili. Creiamo un grafo G avente:
78
8. PROPRIETÀ DEI LINGUAGGI LIBERI DAL CONTESTO
• un nodo per ogni variabile A ∈ V ′ ;
• un arco hA, Bi ed un arco hA, Ci per ogni produzione A → BC ∈ P ′ .
Allora L(G) (= L(G ′ )) è finito se e solo se G non ha cicli.
Se G non ha cicli, la finitezza è immediata: solo un numero finito di alberi di
derivazione con radice etichettata da S possono essere costruiti.
Viceversa, supponiamo G abbia (almeno) un ciclo passante per un nodo associato ad una variabile A. Allora riusciamo a costruire un albero con un cammino dalla radice verso le foglie (qui è importante l’ipotesi che la grammatica non
abbia simboli inutili) che attraversa due nodi etichettati con A. Ripetendo la dimostrazione del pumping lemma, si riescono a generare infiniti alberi di derivazione
diversi.
Teorema 8.8 (Appartenenza). Data una grammatica CF G = hV, T, P, Si, e
una stringa z, il problema z ∈ L(G) è decidibile.
Proof. Per il caso z = ε, dal Teorema 6.13 si ha che ε ∈ L sse esiste S →
A1 · · · An (n ≥ 0) in P tale per cui Ai ∈ N per i = 1, . . . , n.
Sia z 6= ε e G ′ = hV ′ , T, P ′ , Si la grammatica equivalente a G in forma normale
di Greibach. Allora, se z ha una derivazione, ne ha una di esattamente |z| passi.
Generiamo esaustivamente tutte le derivazioni di |z| passi e verifichiamo se esiste
una di queste che deriva z.
L’algoritmo impiegato nella dimostrazione del Teorema 8.8 ha complessità
esponenziale. Fortunatamente per chi necessita di riconoscere o refutare stringhe
(ad esempio, per capire se un programma Pascal o C contiene degli errori sintattici) può avvalersi di metodi più efficienti. In particolare, l’algoritmo di CockeYounger-Kasami (1965–67) che è O(|z|3 ), oppure l’algoritmo di Valiant (1975)
evente complessità O(|z|2.8 ).
Si osservi che nel caso di linguaggi regolari invece tale problema viene deciso
in tempo O(n). Infatti, dato il DFA che accetta il linguaggio, la computazione
avviene in tempo proporzionale alla lunghezza della stringa.
Per quanto riguarda il problema dell’equivalenza di due linguaggi liberi dal
contesto, il problema è mostrato essere indecidibile in [13], Teorema 8.12.
Il problema corrispondente nel caso dei linguaggi regolari è invece decidibile
(si confronti il Teorema 5.13) ed è dimostrato essere NL-completo in [14].
CHAPTER 9
Le grammatiche regolari e la gerarchia di
Chomsky
n questo capitolo si studieranno le famiglie di grammatiche CF che generano
I esattamente
i linguaggi regolari. Saranno dunque collocate in un contesto più
ampio: riassumeremo brevemente dei risultati (alcuni dei quali presenti in questo
testo) che permettono di classificare le grammatiche in base all’espressività dei
linguaggi generabili e presenteremo la classificazione insiemistica completa delle
grammatiche.
1. Grammatiche Regolari
Una grammatica CF si dice lineare destra se ogni produzione è della forma:
A → wB
w ∈ T + , oppure
A → w
w ∈ T+
A → Bw
w ∈ T + , oppure
più eventualmente S → ε. Se invece tutte le produzioni sono della forma:
A → w
w ∈ T+
più eventualmente S → ε, si dice lineare sinistra.
Lemma 9.1. Data una grammatica lineare destra G esiste una grammatica G ′
equivalente (in forma normale di Greibach) tale che tutte le produzioni sono della
forma:
A → aB a ∈ T, oppure
più eventualmente S → ε.
A → a
a∈T
Proof. Ogni produzione della forma A → a1 a2 · · · an B viene sostituita dalle
produzioni: A → a1 C1 , C1 → a2 C2 , . . . , Cn−1 → an B, ove C1 , . . . , Cn sono nuovi
simboli non terminali. Similmente, A → a1 a2 · · · an viene sostituita dalle produzioni: A → a1 C1 , C1 → a2 C2 , . . . , Cn−1 → an .
79
80
9. LE GRAMMATICHE REGOLARI E LA GERARCHIA DI CHOMSKY
Teorema 9.2. Se L è generato da una grammatica lineare destra, allora L è
un linguaggio regolare.
Proof. Grazie al Lemma 9.1 possiamo supporre che G = hV, T, P, Si tale
che L(G) = L sia nella forma semplificata descritta nell’enunciato di tale lemma.
Costruiremo un NFA M = hQ, Σ, δ, q0 , Fi che riconosce L.
• Q = V ∪ {⊥};
• Σ = T;
• B ∈ δ(A, a) sse A → aB ∈ P, ⊥ ∈ δ(A, a) sse A → a ∈ P; δ(⊥, a) è
indefinito per ogni simbolo a ∈ Σ.
• q0 = S;
•
{⊥, S} se S → ε ∈ P
F=
{⊥}
altrimenti
G
Si deve mostrare che ^
δ(S, w) ∩ F 6= ∅ se e solo se S ⇒∗ w, per ogni stringa w.
Iniziamo mostrando, per induzione su |w| ≥ 0, che per ogni A ∈ V (dunque
A 6= ⊥)
A ∈ ^δ(S, w)
sse
G
S ⇒∗ wA
Base: Sia w = ε. Per definizione, ^δ(S, ε) = {S}. Per la forma della grammatica
G
S ⇒∗ εA sse S = A.
Passo: Sia w = va.
A∈^
δ(S, va)
sse A ∈
sse A ∈
G
S
def di ^δ nei NFA
δ(B, a)
^
B∈δ(S,v)
S
G
B : S⇒ ∗ vB
δ(B, a) ip. induttiva
sse S ⇒∗ vaA
def di δ (B −→ aA ∈ P)
Da questo risultato, per definizione di F, si ha che:
G
(1) S ⇒∗ ε se e solo se ^
δ(S, ε) ∩ F 6= ∅;
G
G
(2) S ⇒∗ va se e solo se esiste A tale che S ⇒∗ vA e A → a ∈ P. Ciò accade
se e solo se A ∈ ^
δ(S, v) e ⊥ ∈ δ(A, a), ovvero ⊥ ∈ ^δ(S, va).
Teorema 9.3. Se L è un linguaggio regolare, allora L è generato da una
grammatica lineare destra.
Proof. Sia M = hQ, Σ, δ, q0 , Fi il DFA che riconosce L. Costruiamo una
grammatica lineare destra G = hV, T, P, Si tale che L(G) = L nel modo seguente:
• V = Q;
• T = Σ;
3. GRAMMATICHE DI TIPO 1
•
• S = q0 .
A → aB ∈
A→ε ∈
P
sse δ(A, a) = B
P
sse A ∈ F
81
G
Si mostra per induzione su |w| che ^δ(q0 , w) = p se e solo se q0 ⇒|w| wp
[Esercizio]. A questo punto si ha immediatamente che ^δ(q0 , w) ∈ F se e solo se
G
q0 ⇒|w| w.
Per costruzione, la grammatica ottenuta può avere ε-produzioni e dunque non
è della forma desiderata. Si applichi dunque l’eliminazione di tali ε-produzioni per
ottenere la grammatica nella forma voluta.
Il Teorema 9.3 implica che ogni linguaggio regolare è anche CF.
Si possono mostrare risultati analoghi ai teoremi 9.2 e 9.3 per le grammatiche
lineari sinistre (si veda [13]).
2. Grammatiche di tipo 0
Le grammatiche di tipo 0 (a struttura di frase) sono le grammatiche della
forma G = hV, T, P, Si in cui P contiene produzioni
∗
+
ove α ∈ (V ∪ T ) , β ∈ (V ∪ T ) .
α→β
Teorema 9.4. L = L(G) per G grammatica a struttura di frase se e solo se L
è un insieme ricorsivamente enumerabile.
Proof. (Si veda [13])
3. Grammatiche di tipo 1
Le grammatiche di tipo 1 (dipendenti dal contesto) sono le grammatiche della
forma G = hV, T, P, Si in cui P contiene produzioni
+
+
α→β
ove α ∈ (V ∪T ) , β ∈ (V ∪T ) , e |α| ≤ |β|, con l’eccezione dell’eventuale produzione
S −→ ε (in tal caso richiediamo che S non occorra mai in β).
Per queste grammatiche esiste una forma normale per le produzioni, che devono essere del tipo:
α1 Aα2 → α1 βα2
con β 6= ε.
Teorema 9.5. Ogni linguaggio L generato da una grammatica dipendente dal
contesto è ricorsivo.
Proof. (Si veda [13])
82
9. LE GRAMMATICHE REGOLARI E LA GERARCHIA DI CHOMSKY
Tuttavia:
Teorema 9.6. Ci sono insiemi ricorsivi che non sono generati da nessuna
grammatica dipendente dal contesto.
Proof. (Si veda [13])
i i i
Nell’Esempio 8.2 abbiamo mostrato che {a b c : i ≥ 1} non è un linguaggio
CF. Mostreremo ora che tale linguaggio è un linguaggio dipendente dal contesto.
Esempio 9.7. La grammatica di tipo 1:
S → aSBC | aBC
CB → BC
bB → bb
i i i
genera esattamente il linguaggio {a b c : i ≥ 1}.
bC
cC
→ bc
→ cc
aB → ab
4. Gerarchia
Ricordiamo:
• dagli esempi 5.3 e 6.4 sappiamo che il linguaggio {ai bi : i ≥ 0} è un
linguaggio CF ma non regolare. Sappiamo inoltre che le grammatiche
(CF) lineari destre e sinistre generano esattamente i linguaggi regolari.
• Dagli esempi 8.2 e 9.7 sappiamo che il linguaggio {ai bi ci : i ≥ 1} è un
linguaggio dipendente dal contesto ma non CF.
• Dai Teoremi 9.6 e 9.4 sappiamo che la classe di linguaggi generati da
grammatiche dipendenti dal contesto è inclusa strettamente nella classe
di linguaggi generati da grammatiche a struttura di frase.
Pertanto viene indotta una gerarchia di grammatiche, nota come gerarchia di
Chomsky, fatta di inclusioni proprie, che si può trovare in Figura 1.
4. GERARCHIA
✬
✬
83
✩
Struttura di frase
✬
Dipendenti dal contesto
✬✬
✩
✩
✩✩
Libere dal contesto
Lineari destre
Lineari sinistre
✫✫
✫
✫
✫
✪✪
✪
✪
✪
Figure 1. La gerarchia di Chomsky
Part 2
Teoria della calcolabilità
CHAPTER 10
Nozione intuitiva di algoritmo
In questa parte del testo presenteremo alcuni sistemi formali introdotti per
studiare in modo rigoroso i concetti di algoritmo, programma e funzione calcolabile.
In generale, un sistema formale S è un sistema di formule e regole per combinare
formule, descritto mediante un insieme finito o numerabile S di simboli, e tale
che per ogni regola R ⊆ Sk per un qualche k ∈ N, sia possibile stabilire in modo
finito (decidibile) se una formula f è conseguenza di f1 , . . . , fk−1 secondo R, ovvero
se hf1 , . . . , fk−1 , fi ∈ R. Il senso dei sistemi formali è quello di definire in modo
rigoroso, mediante simboli e regole, l’impalcatura entro cui derivare asserzioni su
un dato modello della realtà. Il calcolo proposizionale e la logica del prim’ordine
sono esempi noti di sistemi formali.
Studieremo dunque alcuni sistemi formali per descrivere in modo rigoroso la
nozione intuitiva di calcolabilità. Ciò permetterà di stabilire al tempo stesso i limiti
intrinseci dell’informatica moderna e studiare alcuni formalismi per rappresentare
il “calcolo”, apparentemente lontani tra loro, ma equivalenti nel potere espressivo
riguardante ciò che è calcolabile.
La corrente Parte 2 del testo, che riguarda questi argomenti, è strutturata nel
modo seguente: nel par. 1 del presente capitolo sarà introdotta la nozione informale
ed intuitiva di algoritmo, o di funzione calcolabile. In seguito, nel Capitolo 11 sarà
presentata la nozione di Macchina di Turing (MdT in breve), come primo sistema
formale per definire il concetto di funzione calcolabile. L’importanza della MdT è,
oltre che culturale, essendo una delle prime formalizzazioni del concetto di calcolabilità, anche tecnica: ad esempio, mediante MdT sono attualmente formalizzate le
nozioni più importanti nella teoria moderna della complessità degli algoritmi che
studieremo nella parte 4 del testo. Nel Capitolo 12 introdurremo le funzioni parziali
ricorsive, come modello matematico per le funzioni calcolabili. L’equivalenza tra
questi due modelli di calcolo ci porterà, nel Capitolo 13, all’analisi della Tesi di
Church-Turing e successivamente, nei Capitoli 14 e 15 ad enunciare alcuni risultati
generali sulla calcolabilità ed alcuni problemi algoritmicamente insolubili.
1. Requisiti di un algoritmo
Discuteremo ora le caratteristiche che deve avere un algoritmo partendo da
quella che è l’esperienza e l’idea intuitiva che tutti ne abbiamo. Un algoritmo viene
descritto in un certo linguaggio, che può anche essere semplicemente l’italiano,
87
88
10. NOZIONE INTUITIVA DI ALGORITMO
cosı̀ come usando i linguaggi nati appositamente per descrivere algoritmi quali i
linguaggi di programmazione e i sistemi formali che vedremo nei prossimi capitoli.
Facciamo qui riferimento all’esperienza personale e immaginiamo di guardare un
algoritmo. Possiamo concordare che, indipendentemente dal problema che intende
risolvere, ha delle caratteristiche comuni.
a: Un algoritmo è di lunghezza finita.
b: Esiste un agente di calcolo che porta avanti il calcolo eseguendo le
istruzioni dell’algoritmo.
c: L’agente di calcolo ha a disposizione una memoria dove vengono immagazzinati i risultati intermedi del calcolo.
d: Il calcolo avviene per passi discreti.
e: Il calcolo non è probabilistico.
I punti a–c hanno una ovvia interpretazione. Il punto d afferma che il calcolo non
avviene mediante dispositivi analogici. Il punto e afferma che il calcolo non obbedisce a nessuna legge di probabilità. Associato al punto a parleremo di espressioni
simboliche ovvero espressioni in un linguaggio di simboli che costituisce il linguaggio per decrivere un algoritmo.
Altre caratteristiche degli algoritmi sono:
f: Non deve esserci alcun limite finito alla lunghezza dei dati di ingresso.
g: Non deve esserci alcun limite alla quantità di memoria disponibile.
Mentre il punto f è ragionevole: ad esempio un algoritmo di somma deve poter
funzionare per ogni possibile addendo, ovvero numero naturale, per il punto g è
necessario un chiarimento. Per evidenziare la necessità di assumere una memoria
illimitata facciano osservare che, limitandola, alcuni algoritmi noti per calcolare
semplici funzioni non potrebbero funzionare. Ad esempio la funzione λx. x2 non
sarebbe calcolabile poiché lo spazio di memoria necessario per calcolare il quadrato
di x dipende da x, e per il punto f, esso deve essere illimitato.
Le seguenti osservazioni sono essenziali per comprendere la natura del calcolo
e la sua complessità.
h: Deve esserci un limite finito alla complessità delle istruzioni eseguibili
dal dispositivo.
i: Sono ammesse esecuzioni con un numero di passi finito ma illimitato.
Il punto h, in relazione al punto a, stabilisce la intrinseca finitezza del dispositivo di calcolo. Ad esempio, pensando ad un calcolatore, esso sarà in grado di
calcolare solo indirizzando direttamente una parte finita della sua memoria (registri o memoria RAM) stabilendo quindi un limite nella complessità delle istruzioni
eseguibili. Questo è un punto chiave nell’analisi che vedremo della nozione di effettiva calcolabilità. Un dispositivo di calcolo può effettivamente tenere traccia solo
di un numero finito di simboli, ovvero esso può reagire (eseguendo un istruzione)
tenendo conto di un numero finito di simboli ricordati. Questo venne intuitivamente giustificato da Turing che per primo analizzò formalmente il concetto di
effettiva calcolabilità, con l’intrinseca limitazione della memoria umana. Tuttavia,
2. FUNZIONI CALCOLABILI
89
non c’è limite alla memoria ausiliaria, come visto nel punto g. Per quanto riguarda
il punto i, osserviamo che non essendo limitabile il numero di passi richiesti per
eseguire un generico algoritmo (si pensi alla moltiplicazione o alla funzione λx. x2
discussa nel punto g), non è possibile stabilire a priori un limite massimo sul numero di passi nell’esecuzione di un algoritmo. Uno studio approfondito su come
legare il numero di passi alla lunghezza dei dati è argomento trattato nella teoria
della complessità degli algoritmi (si veda la Parte 4 del testo).
2. Funzioni calcolabili
Secondo quanto appena illustrato, un algoritmo definisce una funzione, ovvero
una associazione {input} ❀ {output}. Questa può essere rappresentata matematicamente come una funzione sui dati manipolati dall’algoritmo. Possiamo pertanto
asserire che un algoritmo “calcola” funzioni. Diremo quindi che una funzione f è
calcolabile se esiste un algoritmo ed un agente di calcolo tale che ad ogni input x
restituisce come risultato del calcolo f(x).
Tuttavia, una definizione intuitiva di questo tipo riguardante ciò che e’ intuitivamente calcolabile non è soddisfacente. Supponiamo di trattare solo funzioni sui
numeri naturali.1 Supponiamo di poter descrivere in questo modo tutti i processi
di calcolo sui numeri naturali, ovvero che una funzione f : N −→ N sia calcolabile
se esistono un algoritmo ed un agente di calcolo come descritti nei punti a–i sopra,
tale che per ogni input x ∈ N, l’algoritmo resituisce f(x) ∈ N. Ovvero, secondo
queste ipotesi, una funzione è calcolabile se esiste un algoritmo ed un agente di
calcolo che terminano restituendo comunque un risultato, per ogni input. Pur non
assumendo limiti nel tempo e spazio impiegati dall’agente di calcolo per eseguire
i suoi conti, le precedenti ipotesi stabiliscono che ciò che è calcolabile lo sia comunque, per ogni input, a seguito di un insieme potenzialmente illimitato (ma
finito) di passi elementari di calcolo eseguiti dall’agente. Ovvero le funzioni considerate come “calcolabili” secondo le ipotesi a–i sono tutte totali, ovvero sempre
definite per ogni input.
Essendo gli algoritmi di lunghezza finita ed essendo possibile descrivere l’agente
di calcolo mediante un insieme finito di simboli e regole che ne disciplinano il calcolo, è possibile enumerare gli algoritmi e gli agenti di calcolo. Sia dunque Px l’xesimo programma-agente che calcola una data funzione gx . Per le ipotesi a–i la funzione gx è totale, ovvero sempre definita. Definiamo la funzione h(x) = gx (x) + 1.
È chiaro che la funzione h è calcolabile: tutti noi conosciamo un algoritmo per
incrementare un naturale, ed inoltre per ipotesi gx è calcolabile, essendo calcolata
dall’algoritmo-agente Px . Quindi, essendo h calcolabile, deve esistere un algoritmoagente che la calcola e che avrà un dato indice nella numerazione. Sia questo indice x0 , ovvero h sia calcolata dal programma-agente Px0 (h = gx0 ). Si ottiene in
1 Questa non è una limitazione, poiché, come vedremo in seguito, ogni struttura dati, anche
la più complessa, può essere messa in corrispondenza biunivoca con un sottoinsieme dei numeri
naturali, modulo una opportuna codifica dei dati in numeri.
90
10. NOZIONE INTUITIVA DI ALGORITMO
questo modo una evidente contraddizione:
gx0 = h(x0 ) = gx0 + 1
Non è possibile che una funzione sempre definita sui naturali sia uguale al successore di se stessa. La funzione h è quindi non calcolabile nel nostro formalismo
scelto, che pertanto, essendo h intuitivamente calcolabile, non esprime tutto ciò
che è intuitivamente calcolabile.
3. Algoritmi e Programmi
Con le suddette ipotesi si è dimostrato che non si è mai in grado di descrivere
tutto ciò che è “intuitivamente calcolabile”. Ovvero, sarà sempre possibile costruire funzioni, intuitivamente calcolabili e totali, non esprimibili nel sistema formale
adottato. Questo chiaramente rappresenta un limite insormontabile alla nostra
teoria, rendendola inadeguata a trattare la nozione di calcolabilità. Abbiamo
bisogno di estendere i precedenti punti a–i con una nuova ipotesi fondamentale:
si assumerà che un processo di calcolo possa non terminare. Questo corrisponde
all’intuizione che, un programma chiaramente non terminante del tipo:
read(x); while true do · · · endw
corrisponde ad una funzione non definita su tutti gli argomenti, per la quale sappiamo pensare ad un programma (quello sopra) ed un agente di calcolo che la calcola
(un computer). Si noti ora la necessità di introdurre degli oggetti sintatticamente
del tutto simili agli algoritmi, ma senza la proprietà di sicura terminazione. Sono
questi i programmi.
È quindi necessario, per superare la precedente limitazione dei punti a–i, assumere che, oltre ai punti a–i vale la seguente ipotesi:
l: Sono ammesse esecuzioni con un numero di passi infinito (cioè non terminanti).
Il punto l rappresenta una necessità intrinseca, e come abbiamo visto irrinunciabile,
nella trattazione matematica di ciò che è effettivamente calcolabile. Secondo questa
ipotesi, la funzione h descritta in precedenza può risultare indefinita su alcuni
argomenti (ad esempio x0 ), ovvero indicando con ↑ il simbolo di indefinito, la
precedente uguaglianza può risultare valida (non contraddittoria):
↑ = gx0 = h(x0 ) = gx0 + 1 = ↑ .
CHAPTER 11
Macchine di Turing
In questo capitolo introduciamo le Macchine di Turing (MdT), ideate da Alan
Turing negli anni ’30 per rappresentare in modo formale una macchina in grado
di eseguire algoritmi costituiti da passi elementari e discreti di calcolo, secondo la
definizione informale data precedentemente. Turing, nella sua analisi del concetto
di calcolabilità, introduce per primo la parola “computer”, con il significato di
una persona che fa un calcolo composto da passi elementari e discreti utilizzando
un foglio di carta (memoria). Questa nozione di computer include tutte le caratteristiche di base dei calcolatori moderni (memoria, input/output, stato) e, come
vedremo, modella in modo soddisfacente il concetto di effettiva calcolabilità.
1. Descrizione modellistica e matematica
Nell’eseguire un calcolo (manuale), dato un input, calcoliamo un risultato con
l’ausilio della scrittura su carta. L’idea di Turing è quella di pensare ad una semplice macchina in grado di scrivere simboli su un nastro potenzialmente infinito,
rappresentando il fatto (anche espresso nel punto g) che nei calcoli abbiamo solitamente a disposizione una quantità illimitata di carta dove registrare i risultati
parziali del calcolo. L’organizzazione del nastro è fatta mediante celle che rappresentano la quantità di memoria unitaria. La macchina cosı̀ pensata utilizzerà
un alfabeto finito, col quale possiamo rappresentare una infinità di dati. Il corpo
pensante della macchina (detto controllo) sarà realizzato mediante un automa a
stati finiti deterministico (DFA). Questa restrizione modella perfettamente il punto
h precedente, dove è stata assunta una complessità limitata nell’esecuzione delle
istruzioni da parte della macchina. Tutto questo viene rappresentato matematicamente con una macchina detta Macchina di Turing (MdT in breve).
Una MdT è un dispositivo di calcolo rappresentabile con un nastro di lunghezza
infinita nel quale vengono immagazzinati i dati o sequenze di simboli del calcolo.
Uno di questi simboli è $ rappresentante l’assenza di simboli (spazio bianco). Il
controllo della MdT ha accesso al nastro attraverso una testina di lettura e scrittura che permette di leggere o scrivere un simbolo alla volta. Una MdT è pertanto
costituita da due parti: il programma finito secondo cui verrà eseguito il calcolo
e gli organi meccanici per lo scorrimento del nastro e il comando della testina.
La struttura a stati finiti della parte di controllo modella una macchina con una
memoria che potremmo dire a breve termine, finita, mentre il nastro modella una
91
92
11. MACCHINE DI TURING
memoria a lungo termine, potenzialmente infinita. Questa distinzione tra memoria a breve e lungo termine è riscontrabile anche nelle architetture dei moderni
calcolatori.
···
ℓ3
ℓ2
ℓ1
s
r1
r2
r3
···
⇑
q
Figure 1. Il modello meccanico della Macchina di Turing
Ad ogni istante nel calcolo, il simbolo presente nella casella esaminata dalla
testina rappresenta l’input alla macchina (in Figura 1, il simbolo s). In risposta
la macchina può decidere di modificare il simbolo e/o spostare il nastro a sinistra
o a destra della casella esaminata. Questo permette di avere in input un simbolo
diverso per eseguire il passo successivo della computazione (il simbolo ℓ1 se lo
spostamento sarà a sinistra, r1 se a destra). Inoltre la testina (che si trova nello
stato q—memoria finita) può cambiare il suo stato scegliendolo da un insieme
finito di stati possibili.
Il comportamento di una MdT è descrivibile mediante una tabella detta matrice funzionale della macchina in cui le righe rappresentano gli stati del controllo
mentre le colonne rappresentano i simboli di ingresso. In una generica cella è descritta l’azione eseguita dalla macchina nello stato corrispondente in riga e leggendo
dal nastro il simbolo corrispondente in colonna.
sj
qi
qr sk L
L’azione compiuta, essendo il controllo nello stato qi e leggendo il simbolo sj ,
è in questo caso:
• portarsi in uno stato (interno) qr
• scrivere il simbolo sk
• spostare il nastro a destra R o a sinistra L.
Nel caso sj = sk , la macchina lascia inalterato il simbolo sul nastro. Se la
casella corrispondente alla coppia hsj , qi i è vuota, la macchina si ferma. Una computazione di una MdT sarà dunque una sequenza di passi compiuti dalla macchina.
Più formalmente, definiamo una MdT come segue.
1. DESCRIZIONE MODELLISTICA E MATEMATICA
93
Definizione 11.1. Una Macchina di Turing M consiste di:
(1) un alfabeto finito Σ = {s0 , . . . , sn } con almeno due simboli distinti s0 = $
(blank) e s1 = 0 (tally);
(2) un insieme finito di stati Q = {q0 , . . . , qm } tra i quali vi è lo stato iniziale
q0 (dunque Q è non vuoto);
(3) un insieme finito non vuoto di istruzioni (o quintuple) P = {I1 , . . . , Ip }
ognuna delle quali di uno dei seguenti 2 tipi base:
• q s q′ s′ R
• q s q′ s′ L
tale che non esistono due istruzioni che iniziano con la medesima coppia
q, s.
Si può dunque immaginare P come la descrizione “estensionale” (ovvero per
casi) di una funzione (in generale) parziale δ : Q × Σ −→ Q × Σ × {R, L}. Tale
funzione δ è detta funzione di transizione.
Si osservi la caratteristica locale delle istruzioni: il loro significato è univocamente determinato dalla parte di nastro immediatamente adiacente alla testina
e dallo stato in cui la macchina si trova. Lo stato in cui si trova l’intera MdT
in un certo istante del calcolo è dunque esprimibile mediante la nozione di descrizione istantanea di una MdT, che rappresenta lo stato interno del controllo, il
posizionamento della testina e il nastro.
Definizione 11.2. Una descrizione istantanea (ID) della macchina è una
quadrupla
hq, v, s, wi
⋆
dove v, w ∈ Σ rappresentano i caratteri significativi (cioè escludendo la sequenza
illimitata di $ sempre presenti a destra e a sinistra) presenti sul nastro a sinistra
ed a destra della testina, s è il simbolo letto dalla testina, essendo la macchina
nello stato q.
Ad esempio, se la situazione è
. . . $ $ $ s1 . . . si−1 si si+1 . . . sj $ $ $ . . .
⇑
q
allora v = s1 . . . si−1 , s = si e w = si+1 . . . sj .
Vediamo ora come definire formalmente il generico passo di calcolo di una
MdT. Definiamo una relazione tra descrizioni istantanee, ovvero una relazione
⊢ ⊆ ID × ID, che rappresenti l’esecuzione di un singolo passo di calcolo in una
data MdT.
Definizione 11.3. Il successore ⊢ è una “mappa” tra descrizioni istantanee
definita come:
• hq, v, r, s wi ⊢ hq ′ , v r ′ , s, wi se q r q ′ r ′ R è una istruzione di P;
94
11. MACCHINE DI TURING
• hq, v s, r, wi ⊢ hq ′ , v, s, r ′ wi se q r q ′ r ′ L è una istruzione di P.
Una computazione sarà dunque definita come segue, ovvero come una sequenza
finita di passi.
Definizione 11.4. Una computazione è una sequenza finita di ID α0 , . . . , αn
tale che
• α0 = v s q0 w e,
• αi ⊢ αi+1 per ogni i ∈ {0, . . . , n − 1}.
Una computazione è detta terminante se esiste un n ≥ 0 per cui αn = v s q w e
non vi è nessuna istruzione di P iniziante con q ed s (cioè δ(q, s) non è definita),
i.e. αn 6⊢.
In accordo con il punto l della precedente sezione, esistono MdT che non
terminano, ovvero che non calcolano nulla a partire da un dato input.
Osserviamo come una MdT cosı̀ descritta soddisfa i requisiti per la definizione
ragionevole di algoritmo.
a: Le istruzioni della MdT sono finite.
b: La MdT è l’agente di calcolo che esegue l’algoritmo.
c: Il nastro rappresenta la memoria della macchina.
d: La MdT opera in modo discreto.
e: Ad ogni ID corrisponde una sola azione (determinismo della MdT).
f: Non esiste alcun limite all’input, essendo il nastro illimitato.
g: La capacità della memoria (nastro) è illimitata.
h: Le operazioni eseguibili sono semplici e quindi di complessità limitata.
i: Non esiste alcun limite al numero di istruzioni eseguite in quanto la
medesima quintupla può essere usata più volte.
l: Possono esistere MdT che non calcolano nulla generando una sequenza
infinita di ID.
Teorema 11.5 (Determinismo). Se α ⊢ β e α ⊢ γ con α, β, γ ∈ ID, allora
β ≡ γ.
Proof. Ovvio, poiché per definizione non esistono due quintuple q s q ′ s ′ X
aventi la medesima coppia q, s.
Esempio 11.6. Incremento di uno in binario.
Sia Σ = {$, 0, 1}. Si vuole passare da una configurazione iniziale
ad una finale
. . . $ $ $ sn . . . s0 $ $ $ $ . . .
⇑
q0
′
. . . $ $ $ $ sm
. . . s0′ $ $ $ . . .
⇑
q2
1. DESCRIZIONE MODELLISTICA E MATEMATICA
95
ove
m
X
i=0
n
X
2i · si′ = 1 +
i=0
2i · si .
Una possibile definizione della funzione δ sarà la seguente:
$
δ
0
q0
q1 $ L
q1
q2 1 L
1
q2 1 L q1 0 L
q2
q2 0 L q2 1 L
Esempio 11.7. Copia di una stringa unaria.
Sia Σ = {$, 0}. Si vuole passare da una configurazione iniziale
. . . $ $ $ 0| .{z
. . 0} $ $ $ $ . . .
n
⇑
ad una finale
q0
. . . $ $ $ 0| .{z
. . 0}
$
⇑
n
qf
0| .{z
. . 0} $ $ $ . . .
n
Una possibile definizione della funzione δ sarà la seguente:
δ
q0
$
0
q1 $ L
q1
q2 $ R
q2
q3 $ R q2 0 R
q3
q4 0 L
q3 0 R
q4
q5 $ L
q4 0 L
q5
q6 0 L
q5 0 L
q6
q7 $ R q2 $ R
q7
q7 0 R
Si provi, per esercizio, a definire δ supponendo di disporre di Σ = {$, 0, 1}.
Esempio 11.8. Definiamo una MdT che calcola il successore in base 10 di un
numero. S = {0, . . . , 9}, Q = {q0 , q1 } essendo q1 lo stato di terminazione. La
96
11. MACCHINE DI TURING
δ
q0
q1
0
1
2
···
7
8
9
$
q1 1 R
q1 2 R
q1 3 R
···
q1 8 R
q1 9 R
q0 0 L
q1 1 R
···
Figure 2. MdT per l’incremento in base 10
matrice funzionale in figura 2 definisce la MdT desiderata che calcola il successore
di un numero scritto sul nastro supponendo che la MdT inizi il calcolo essendo la
testina posizionata sulla cifra meno significativa del numero da incrementare.
Esercizio 11.9. Si scriva la funzione di transizione per le macchine di Turing
soddisfacenti i seguenti requisiti:
(1) si calcoli il successore di un numero decimale
(2) decisione di quale sia la più lunga tra due stringhe di 0 poste una a
sinistra ed una a destra della testina. Si scelga una opportuna forma di
risposta (che tenga conto anche della possibilità che le due stringhe siano
uguali);
(3) si effettui la somma (in binario) di due stringhe non nulle di 0 e 1 poste
entrambe a sinistra della testina ed inframmezzate da un $;
(4) si sposti la testina di n posizioni a destra rispetto a quella di partenza,
ove n è letto da un input binario;
(5) si calcoli il numero di occorrenze di 1 in una data sequenza binaria;
(6) si ordini, con ripetizioni, una stringa in input di 0 e 1.
2. Funzioni calcolabili da MdT
Ad ogni MdT può essere facilmente associata una funzione che diremo calcolata
dalla MdT. Poiché in questa parte del corso trattiamo solo funzioni sui naturali, è
necessario fissare una opportuna codifica dei naturali come stringhe nell’alfabeto
della MdT. Ad esempio è possibile utilizzare una codifica binaria nel caso Σ =
{$, 0, 1} o una codifica decimale. Nella seguente definizione, assumeremo invece
una codifica unaria, ovvero un generico numero n ∈ N sarà rappresentato come
una sequenza di n + 1-simboli 1 consecutivi. Questa codifica è particolarmente
conveniente, anche se del tutto inessenziale, per la seguente definizione di funzione
calcolabile da una MdT.
Definizione 11.10. Una funzione f : Nn −→ N è Turing–calcolabile se esiste
una MdT tale che, partendo dalla configurazione iniziale
. . . $ $ $ x1 $ . . . $ xn $ $ $ $ . . .
⇑
q0
2. FUNZIONI CALCOLABILI DA MDT
97
termina nella configurazione
. . . $ $ $ . . . $ f(x1 , . . . , xn ) $ $ $ $ . . .
⇑
qf
se f(x1 , . . . , xn ) è definita, non termina altrimenti; dove per ogni (x1 , . . . , xn ) ∈
Nn , x1 , . . . , xn , f(x1 , . . . , xn ) sono le rappresentazioni in unario rispettivamente di
x1 , . . . , xn , f(x1 , . . . , xn ) (mentre qf ∈ Q è tale per cui δ(qF , $) è indefinito).
Chiaramente, una funzione sui naturali Turing-calcolabile è parziale, poiché
può non essere definita su alcuni (o tutti) gli argomenti. Vedremo in seguito il
significato più profondo di funzione parziale nella teoria della calcolabilità.
Esempio 11.11. Le seguenti funzioni sono Turing-calcolabili:
• λ x.x: Q = {q0 }; δ sempre indefinito.
• λ x.0 :
δ
$
q0
q1 $ R
0
q1
• λ x.x + 1 :
δ
$
q0
q1 0 R
0
q1
• λ x1 . . . xn .xi :
for j = 0, . . . , i − 1
δ(qj , 0) = (qj , 0, L);
δ(qj , $) = (qj+1 , $, L).
Esercizio 11.12. Si supponga di disporre delle definizioni per le macchine di
Turing computanti le funzioni:
f : Nk −→ N,
g1 , . . . , gk : Nn −→ N.
Come esercizio si mostri che la funzione
h : Nn −→ N,
definita come
def
h(x1 , . . . , xn ) = f(g1 (x1 , . . . , xn ), . . . , gk (x1 , . . . , xn )),
è Turing calcolabile.
98
11. MACCHINE DI TURING
Nota 11.13. Si osservi che, se esiste una MdT computante la funzione f, allora
ne esistono infinite calcolanti la stessa funzione (si aggiungano arbitrariamente
stati o simboli ‘inutili’).
3. MdT generalizzate
Il modello di MdT che abbiamo visto è solo uno dei possibili modelli matematici per definire l’effettiva calcolabilità di funzioni. È infatti possibile definire
una vasta gamma di MdT differenti ma equivalenti rispetto alla classe di funzioni Turing-calcolabili che esse possono indurre. Questo perchè qualunque MdT
ottenuta anche con modifiche strutturali (per esempio aumentando il numero dei
nastri o delle testine) se soddisfa le 10 condizioni che abbiamo definito per caratterizzare la nozione intuitiva di algoritmo, può essere simulata da una MdT definita
come nella Definizione 11.1. I seguenti risultati, dei quali non riportiamo dimostrazione, forniscono un esempio di come si possa modificare il modello di MdT
senza modificare l’insieme delle corrispondenti funzioni calcolabili. La potenza del
formalismo delle MdT non viene dunque ridotta/aumentata modificando l’insieme
dei simboli/stati o modificando la struttura stessa della macchina (numero di nastri
e/o testine).
• Una MdT con n nastri ed m testine (m ≥ n) può essere simulata da una
MdT con 1 nastro ed una testina;
• Una MdT con n simboli ed m stati può essere simulata da una MdT con
2 stati, aumentando opportunamente il numero dei suoi simboli;
• Una MdT con n simboli ed m stati può essere simulata da una MdT con
2 simboli, aumentando opportunamente il numero dei suoi stati;
• Ogni MdT può essere simulata da una MdT che può solo scrivere e non
rimpiazzare simboli sul nastro.
Per approfondimenti su queste tematiche, si consulti, ad esempio [13].
CHAPTER 12
Funzioni parziali ricorsive di Kleene & Robinson
Lo scopo primario di questa parte del corso è di studiare le funzioni sui numeri
naturali, esprimibili in modo effettivo o algoritmico. La definizione intuitiva del
concetto di funzione effettivamente calcolabile o funzione calcolabile mediante un
algoritmo, ovvero mediante una sequenza discreta di passi elementari di calcolo di
una MdT, fornita nel capitolo precedente, può sembrare limitativa rispetto a ciò
che intuitivamente ci sembra effettivamente calcolabile. Per valutare la robustezza
di questa formalizzazione del concetto di funzione calcolabile, nei seguenti due
paragrafi studieremo diverse nozioni di calcolabilità che, come vedremo, si equivalgono e sono a loro volta tutte equivalenti alla calcolabilità mediante MdT.
Nel Paragrafo 1 studieremo la caratterizzazione di effettiva calcolabilità sui
naturali nota con il termine di funzione ricorsiva. Arriveremo alla trattazione
delle funzioni parziali ricorsive, dette anche funzioni ricorsive generali, di Kleene
& Robinson, arricchendo via via con nuovi costrutti, o metodi di composizione,
alcune funzioni elementari sui naturali, dette funzioni ricorsive di base.1 Questi
costrutti, come vedremo nel Cap. 16, corrispondono a costrutti noti ed ampiamente
utilizzati nei moderni linguaggi di programmazione.
1. Funzioni primitive ricorsive
L’insieme dei numeri naturali è facilmente caratterizzabile mediante induzione,
ovvero affermando che ogni numero naturale n è esprimibile come una iterazione
della operazione elementare di successore applicata al valore costante 0: n = Sn (0).
Pertanto
N = {0, S(0), S(S(0)), S(S(S(0))), . . .}
Nel seguito indicheremo spesso S(x) con x + 1. Questo procedimento induttivo per
definire i numeri naturali si basa sull’idea che ogni numero naturale è definibile a
partire da un numero più semplice (in questo caso minore nell’ordinamento usuale
sui naturali) applicando l’operazione di successore. Questo modo di procedere
è comune in matematica e informatica (si veda il paragrafo 2.6), e trova svariate applicazione nelle cosı̀ dette definizioni ricorsive di funzioni. Una definizione
ricorsiva di funzione è una definizione dove il valore della funzione per un dato argomento è direttamente correlato al valore della medesima funzione su argomenti
1 Nel
seguito per funzione ricorsiva intenderemo una funzione ricorsiva totale. Nel caso di
funzioni parziali, parleremo di funzioni parziali ricorsive.
99
100
12. FUNZIONI PARZIALI RICORSIVE DI KLEENE & ROBINSON
“più semplici” o al valore di funzioni “più semplici”. Ad esempio, la seguente
funzione definisce ricorsivamente la successione di Fibonacci: 1, 1, 2, 3, 5, 8, 13,
...
f(0) = 1
f(1) =
f(x + 2) =
1
f(x + 1) + f(x).
Al fine di comprendere meglio queste definizioni è pertanto dare un significato
preciso al concetto informale di “più semplice”. Identifichiamo le funzioni “più
semplici” in assoluto con le funzioni costanti. Trattando i naturali, consideriamo
dunque la costante 0 come funzione costante elementare. Supponiamo inoltre di
avere a disposizione come funzione elementare l’operazione di successore, che ci
permette di definire qualsiasi numero naturale, e la funzione identica. Queste
assunzioni ci portano a definire il concetto di funzione ricorsiva di base.
Definizione 12.1. Si dicono funzioni ricorsive di base le seguenti funzioni:
• la funzione costante 0: λx. 0;
• La funzione successore S: λx. x + 1;
• La funzione identità, o i-esima proiezione, πi : λx1 · · · xn . xi con 1 ≤ i ≤
n.
Formalizziamo ora il meccanismo di definizione di funzioni per composizione
e ricorsione primitiva. Questo meccanismo generalizza i precedenti esempi.
Definizione 12.2. Una funzione f : Nn −→ N si dice:
• definita per composizione da g1 , . . . , gk : Nn −→ N e h : Nk −→ N se
f(x1 , . . . , xn )=h(g1 (x1 , . . . , xn ), . . . , gk (x1 , . . . , xn ))
•
definita per ricorsione primitiva da g : Nn−1 −→ N e h : Nn+1 −→ N se
f(x , . . . , x
1
n−1 , 0) = g(x1 , . . . , xn−1 )
f(x , . . . , x
, y + 1) = h(x , . . . , x
, y, f(x , . . . , x
, y))
1
n−1
1
n−1
1
n−1
Tutte e sole le funzioni definibili a partire dalle precedenti funzioni ricorsive
di base mediante composizione e ricorsione primitiva definiscono l’insieme delle
funzioni primitive ricorsive. L’idea è quella di costruire via via funzioni effettivamente calcolabili a partire dalle funzioni (banalmente) effettivamente calcolabili di
base, ovvero 0 e S, costruendo da queste, per composizione e ricorsione primitiva,
funzioni via via più complesse
Definizione 12.3. La classe P delle funzioni primitive ricorsive è la più piccola classe (ovvero l’intersezione di tutte le classi) di funzioni contenenti le funzioni
ricorsive di base e chiuse per composizione e ricorsione primitiva.
Nota 12.4. Segue direttamente dalla definizione che, per ogni funzione f,
f ∈ P sse esiste una sequenza finita di funzioni f1 , . . . , fn , tale che: fn = f e per
ogni funzione fj , con j ≤ n, o fj è una funzione ricorsiva di base, oppure è ottenuta
1. FUNZIONI PRIMITIVE RICORSIVE
101
mediante composizione o ricorsione primitiva a partire da funzioni fi1 , . . . , fik con
i1 , . . . , ik < j e per le quali vale la stessa proprietà. Queste non sono altro che le
condizioni per fissare un sistema formale.
Esempio 12.5. Consideriamo la sequenza di funzioni ottenute come visto in
precedenza:
f5
f1
=
λx. x
f2
=
λx. x + 1
f3
=
λx1 x2 x3 . x2
f4
=
f2 ◦f3 = λx1 x2 x3 . x2 + 1
= Tale che:
f5 (0, x2 )
=
f5 (y + 1, x2 ) =
f6
f1 (x2 )
f4 (y, f5 (y, x2 ), x2 )
= f5 (f1 , f1 )
È facile vedere che f6 = λx. 2x e che f5 = λxy. x + y. Dimostrare per esercizio.
Le funzioni primitive ricorsive sono molto frequenti in matematica ed informatica. Il seguente esempio elenca alcune tra le più note funzioni primitive ricorsive.
Esempio 12.6. Le seguenti funzioni sono ricorsive primitive (per semplicità si
denoteranno numerali con numeri):
Zero: 0n = λx1 · · · xn . 0: definibile come 0(π1 (x1 , . . . , xn ));
Costante: cn = λx1 · · · xn .c: definibile come S(· · · S(0n (x1 , . . . , xn ) ) · · · ) ;
| {z }
| {z }
c
Somma:
+(x, 0) = x
+(x, y + 1) = S(+(x, y))
c
Rispetto alla notazione utilizzata:
• h è +, di arità 2,
• g è la funzione identica, di arità 1,
• f(x, y, z), di arità 3, è la funzione ottenuta come S(π3 (x, y, z)).
Un esempio di ‘computazione’ nel formalismo che si sta definendo è il
seguente:
+(S(S(0)), S(S(S(0))))
= S(+(S(S(0)), S(S(0))))
=
S(S(+(S(S(0)), S(0)))
=
S(S(S(+(S(S(0)), 0)))))
=
S(S(S(S(S(0))))))
102
12. FUNZIONI PARZIALI RICORSIVE DI KLEENE & ROBINSON
Moltiplicazione:
·(x, 0) = 0
·(x, y + 1) = +(·(x, y), y))
Potenza:
x0 = S(0)
xy+1 = ·(xy , x))
Iper-potenza:
iper(x, 0) = π (x)
1
iper(x, y + 1) = iper(x, y)x
... x
y
Si osservi che iper(x, y) = x x|{z} = x(x
)
y
Predecessore:
pred(0) = 0
pred(y + 1) = π (y)
1
Differenza:
−(x, 0) = π (x)
1
−(x, y + 1) = pred(−(x, y))
Fattoriale:
0! = S(0)
(y + 1)! = ·(S(y), y!)
Segno:
sg(0) = 0
sg(y + 1) = S(0)
Definiamo inoltre sg(x) = −(1, sg(x)).
Valore assoluto della differenza: | − (x, y)| = +(−(x, y), −(y, x));
Minimo
e massimo:
min(x, y) = −(x, −(x, y))
max(x, y) = +(x, −(y, x))
Divisione
intera: Assumiamo che div(x, 0) = mod(x, 0) = 0.
mod(0, x) = 0
mod(y + 1, x) = S(mod(y, x)) · sg(| − (x, S(mod(y, x)))|)
div(0, x) = 0
div(y + 1, x) = div(y, x) + sg(| − (x, S(mod(y, x)))|)
1. FUNZIONI PRIMITIVE RICORSIVE
103
Produttoria:
Sia f una funzione ricorsiva primitiva binaria, allora definiamo la funzione
y−1
Π
z<y f(x, z) = Πz=0 f(x, z):
Π
f(x, z) = S(0)
z<0
Π
z<y+1 f(x, z) = ·(f(x, y), Πz<y f(x, z))
Sommatoria:
Sia f una funzione ricorsiva primitiva binaria, allora definiamo la funzione
y−1
Σ
z<y f(x, z) = Σz=0 f(x, z):
Σ
f(x, z) = 0
z<0
Σ
z<y+1 f(x, z) = +(f(x, z), Σz<y f(x, z))
µ-operatore limitato di minimizzazione:
Sia f una funzione primitiva ricorsiva n + 1 aria.
g(x1 , . . . , xn , y) =
=
µz < y. (f(x1 , . . . , xn , z) = 0)
il più piccolo z minore di y
tale che f(x1 , . . . , xn , z) = 0 se tale z esiste
y
altrimenti
Tale funzione è primitiva ricorsiva:
g(x1 , . . . , xn , y) = Σv<y (Πu≤v sg(f(x1 , . . . , xn , u))).
Esercizio 12.7. Si definiscano le seguenti funzioni ricorsive primitive:
(1) la funzione che, dato x, fornisce il numero dei suoi divisori;
(2) la funzione che, dato x, restituisce 1 se x è primo, 0 altrimenti;
(3) la funzione p che, dato x, restituisce il p-esimo numero primo (si assuma
p(0) = 0).
(4) la funzione che restituisce l’esponente di p(y) nella fattorizzazione di x;
(5) la funzione che restituisce 1 se e solo se x è un cubo perfetto;
(6) la funzione che restituisce 1 se e solo se x è ottenibile come somma di
due cubi.
Teorema 12.8. Le funzioni primitive ricorsive sono totali.
Proof. Per induzione strutturale sulla complessità (nel senso di numero di
operazioni di composizione e ricorsione primitiva) delle funzioni definite in P osserviamo che le funzioni ricorsive di base sono totali, e la composizione di funzioni
totali è totale. Resta da dimostrare che la composizione mediante ricorsione primitiva di funzioni totali è totale. Questo passo è lasciato per esercizio.
I precedenti esempi ci lascerebbero pensare che la nozione di funzione primitiva
ricorsiva catturi esattamente il concetto intuitivo di funzione calcolabile mediante
un algoritmo. Questo è falso! Innanzitutto, il Teorema 12.8 afferma che le funzioni
104
12. FUNZIONI PARZIALI RICORSIVE DI KLEENE & ROBINSON
primitive ricorsive sono totali e, come vedremo, questa è una limitazione, ovvero
una funzione calcolabile può essere indefinita su alcuni (o tutti) i valori di input. Le
funzioni primitive ricorsive hanno inoltre un’ulteriore limitazione anche all’interno
delle funzioni totali sui naturali. Per dimostrare questo fatto, è sufficiente dare
un testimone, ovvero una funzione evidentemente calcolabile e totale, ma non
appartenente a P. Una tale funzione è la funzione di Ackermann.
La funzione di Ackermann è una funzione in 3 argomenti, ack : N3 −→ N,
definita come segue:
ack(0, 0, y) = y
ack(0, x + 1, y) = ack(0, x, y) + 1
ack(1, 0, y) = 0
ack(z + 2, 0, y) = 1
ack(z + 1, x + 1, y) = ack(z, ack(z + 1, x, y), y).
Per esercizio si verifichi che la funzione è totale (ed è ben definita, cioè non vi sono
chiamate ricorsive ad oggetti ‘più grandi’).
Vediamo di capire il significato della definizione di ack. Dalle prime due
equazioni segue che ack(0, x, y) = y+x. La terza e quarta definzione costituiscono
le condizioni iniziali per la quinta che è la vera e propria chiamata ricorsiva di ack.
Questa afferma che λxy. ack(z + 1, x, y) è ottenuta calcolando
0 se z + 1 = 1
y0 = ack(z + 1, 0, y) =
1 se z + 1 > 1
e quindi applicando la funzione λwy. ack(z, w, y) x volte, ovvero la sequenza:
y1
=
ack(z + 1, 1, y)
=
ack(z, y0 , y)
y2
=
ack(z + 1, 2, y)
=
ack(z, y1 , y)
y3
=
ack(z + 1, 3, y)
=
ack(z, y2 , y)
...
yx
=
...
...
ack(z + 1, x, y) =
ack(z, yx−1 , y)
2. DIAGONALIZZAZIONE
105
Quindi abbiamo:
f(0, x, y)
=
y+x
f(1, x, y)
=
y·x
f(2, x, y)
=
yx
f(3, x, y)
=
yy
...
y
··
x-volte
...
Ad esempio, ack(3, 3, 3) = 327 > 1014 .
Teorema 12.9. ack non è ricorsiva primitiva [25].
La dimostrazione del Teorema 12.9 è piuttosto complessa ed è basata sul fatto
che ack cresce più velocemente di qualunque funzione ricorsiva primitiva. Questa
funzione, universalmente accettata come funzione calcolabile non è primitiva ricorsiva pur essendo totale. Inoltre esistono infinite funzioni totali e chiaramente
calcolabili che non sono esprimibili mediante ricorsione primitiva: basta aggiungere
alla funzione ack una generica costante.
2. Diagonalizzazione
In questa sezione studieremo uno degli strumenti fondamentali della teoria
delle funzioni calcolabili: la diagonalizzazione. Il suo uso, già evidenziato nel
Capitolo 10, sarà frequente nel seguito. La tecnica di diagonalizzazione fu per la
prima volta utilizzata da Cantor nel 1874 per dimostrare uno dei risultati fondamentali nella teoria classica degli insiemi, ovvero la non enumerabilità dei numeri
reali (si veda anche il Capitolo 2).
L’idea della diagonalizzazione è la seguente: Dato un insieme S numerabile,
costruiamo una matrice nel modo seguente: in ogni riga i è presente una possibile
enumerazione di S: si,0 , si,1 , si,2 , . . . che identifica univcocamente una funzione
totale f : N −→ S. Supponiamo che l’insieme di tali enumerazioni sia numerabile
e tale enumerazione la leggiamo scandendo la matrice riga per riga.
s0,0
s0,1
s0,2
...
s1,0
s1,1
s1,2
...
s2,0
s2,1
s2,2
...
...
...
...
...
Sia d : S −→ S una funzione totale che non sia mai l’identità su S, ovvero tale che
∀s ∈ S. d(s) 6= s (una tale funzione ovviamente esiste). Da questa costruzione,
consideriamo la diagonale s0,0 , s1,1 , s2,2 , . . . che viene trasformata da d nella
sequenza:
d(s0,0 ), d(s1,1 ), d(s2,2 ) . . . .
106
12. FUNZIONI PARZIALI RICORSIVE DI KLEENE & ROBINSON
La caratteristica essenziale di questa sequenza è che essa differisce da ogni altra riga
della matrice. Pertanto esiste una enumerazione di S non presente nella matrice.
Ciò contraddice con l’ipotesi che l’insieme delle enumerazioni n(e dunque delle
funzioni totali da N a S è numerabile. Pertanto, data una qualsiasi enumerazione
degli oggetti di un sistema formale dato, in questo modo sarà sempre possibile
“costruire” un oggetto che non appartiene alla numerazione, ovvero esterno al
sistema formale dato.
Come esempio di applicazione di questa tecnica, dimostreremo l’inadeguatezza
della classe delle funzioni primitive ricorsive nell’esprimere ciò che intuitivamente
viene ritenuto effettivamente calcolabile. Ovvero mediante un “argomento diagonale” dimostreremo che è possibile definire effettivamente una classe di funzioni
calcolabili non esprimibili mediante ricorsione primitiva. In realtà la diagonalizzazione ha un ben più ampio campo di utilizzo in quanto è applicabile ad ogni
sistema formale le cui istruzioni definite su un alfabeto dato, sono effettivamente
enumerabili. L’inadeguatezza della classe delle funzioni primitive ricorsive per esprimere la calcolabilità era comunque già chiara dal fatto che la funzione calcolabile
di Ackermann non è primitiva ricorsiva.
Consideriamo l’insieme delle funzioni primitive ricorsive P ed un formalismo,
ovvero un insieme finito di simboli con il quale sia possibile descrivere tutte le
possibili derivazioni associate alle funzioni in P. Un siffatto alfabeto può essere
ad esempio
Σ = {a, b, c, . . . , z, 0, 1, 2, . . . 9, (, )}
Ogni derivazione associata ad una funzione in P è quindi descrivibile con una
opportuna sequenza finita di simboli in Σ∗ . È inoltre facile definire una procedura
che verifichi se una data stringa σ ∈ Σ∗ corrisponde ad una derivazione legittima
per una funzione in P (si veda anche la Nota 12.4). È pertanto possibile enumerare
tutte le derivazioni legittime per funzioni primitive ricorsive esaminando le stringhe
in Σ di lunghezza 1, quindi quelle di lunghezza 2 etc. Sia quindi Qx l’(x + 1)-esima
derivazione in questa lista e sia gx la funzione di cui Qx è derivazione. Definiamo
la funzione h come segue:
h(x) = gx (x) + 1
Evidentemente abbiamo un algoritmo per calcolare h: dato x, generiamo la lista
delle derivazioni fino a Qx , quindi utilizziamo Qx per calcolare gx (x) e sommiamo
1. h cosı̀ costruita non è primitiva ricorsiva altrimenti, se lo fosse, avremmo
h = gx0 per un qualche x0 , e quindi l’assurdo:
gx0 (x0 ) = h(x0 ) = gx0 (x0 ) + 1
Si noti l’analogia di questa dimostrazione (informale) con la dimostrazione di Cantor sulla non enumerabilità di ℘(N). È pertanto possibile “costruire”, mediante
diagonalizzazione, una funzione a tutti gli effetti calcolabile che non è primitiva
ricorsiva.
3. FUNZIONI PARZIALI RICORSIVE
107
Il problema posto dalla diagonalizzazione sembra limitare in modo intrinseco e
insuperabile ogni definizione matematica di funzione calcolabile codificabile con un
alfabeto finito di simboli. Infatti, per diagonalizzazione sembra possibile costruire
funzioni calcolabili che sfuggono ad ogni definizione formale, basata ad esempio su
un alfabeto finito, di funzione calcolabile.
L’idea fondamentale che permette di superare la difficoltà indotta dalla diagonalizzazione su un dato sistema formale è quella di ammettere algoritmi, o insiemi
di istruzioni per calcolare sia funzioni totali che parziali. Per comprendere meglio
questo aspetto fondamentale da cui è iniziato lo studio della teoria della calcolabilità, cerchiamo di applicare un argomento diagonale al caso di sistemi formali per
esprimere funzioni parziali. Osserveremo che un sistema formale che caratterizzi
un sottoinsieme opportuno di funzioni parziali, non è soggetto a limitazioni dovute
ad argomenti diagonali.
Sia ψx la funzione parziale corrispondente all’(x+1)-esimo insieme di istruzioni
Px in un dato sistema formale per funzioni parziali, e sia x0 tale che ψx0 è la
funzione parziale ϕ definita dalle seguenti istruzioni: trova Px , calcola ψx (x) e, se
e quando si ottiene un risultato, restituisci come output di ϕ il valore ψx (x) + 1.
Trattandosi di funzioni parziali, l’equazione
ψx0 (x0 ) = ϕ(x0 ) = ψx0 (x0 ) + 1
non genera contraddizioni, poiché ϕ(x0 ) può non essere definita.
Questo metodo di procedere, ovvero considerare la teoria della effettiva calcolabilità come una teoria di funzioni parziali è alla base degli approcci di Kleene,
Church e Turing sviluppati negli anni ‘30. Tutti i sistemi formali che vedremo
infatti hanno in comune una descrizione in un opportuno sistema formale, ovvero
mediante un opportuno insieme di simboli e regole, di ciò che sono gli algoritmi
ed una conseguente caratterizzazione del sottoinsieme delle funzioni parziali calcolabili in quel sistema formale, ovvero calcolabile dagli algoritmi cosı̀ descritti.
Abbiamo già visto un esempio di questo modo di procedere nella descrizione delle
MdT come sistema formale per esprimere algoritmi e nella funzioni calcolabili da
una MdT come descrizione di opportune funzioni parziali. Queste considerazioni
generali sulla natura parziale delle funzioni calcolabili giustificano l’assunzione l
nella descrizione intuitiva di ciò che è effettivamente calcolabile. Indicheremo nel
seguito con f(x) ↓ il fatto che f(x) è definita mentre con f(x) ↑ il fatto che f(x)
non è definita.
3. Funzioni parziali ricorsive
Introduciamo ora un metodo generale per costruire funzioni parziali a partire
da funzioni totali (ad esempio primitive ricorsive).
108
12. FUNZIONI PARZIALI RICORSIVE DI KLEENE & ROBINSON
Definizione 12.10. Sia f una funzione totale n + 1 aria, allora definiamo la
funzione
ϕ(x1 , . . . , xn )
= µz. (f(x1 , . . . , xn , z) = 0)
il più piccolo z t.c. f(x , . . . , x , z) = 0 se z esiste
1
n
=
↑
altrimenti
Tale operatore tra funzioni è detto µ-operatore ed una funzione cosı̀ definita è
detta definita per minimizzazione o µ-ricorsione da f.
Chiaramente una funzione definita per minimizzazione è in generale, per definizione,
parziale.
Nota 12.11. Analizziamo l’ipotesi che f sia una funzione totale. Questa
ipotesi può essere rilasciata definendo la minimizzazione, o µ-ricorsione nel modo
seguente: Sia ψ una funzione parziale; ϕ è definita per µ-ricorsione da ψ se
ϕ(x1 , . . . , xn ) = µz. (∀y ≤ z. ψ(x1 , . . . , xn , y) ↓ ∧ ψ(x1 , . . . , xn , z) = 0)
La rimozione dell’ipotesi di totalità nella Definizione 12.10 provocherebbe un chiaro
non senso. Infatti, per determinare il minimo z tale che f(x1 , . . . , xn , z) = 0 si
ricorre al seguente algoritmo (facilmente descrivibile con una MdT) calcolando la
sequenza
f(x1 , . . . , xn , 0), f(x1 , . . . , xn , 1), . . .
fino a che non si trova il primo valore z tale che f(x1 , . . . , xn , z) = 0. Se f fosse
parziale, ovvero il suo calcolo potesse non convergere, la procedura cosı̀ descritta
non sarebbe effettiva. Inoltre, come dimostrato da Kleene nel ‘52, le funzioni
parziali ricorsive non sono chiuse rispetto allo schema2
ϕ(x1 , . . . , xn ) = µz. (ψ(x1 , . . . , xn , z) = 0).
Definizione 12.12. La classe delle funzioni parziali ricorsive è la minima
classe PR di funzioni contenente P e chiusa per µ-ricorsione.
Esempio 12.13. Si osservi come si possa utilizzare il µ-operatore per la definizione
delle seguenti funzioni:
• logaritmo (intero):
⌊loga x⌋ = µy. (leq(x, a(y+1) ) = 0),
ove leq è una funzione ricorsiva primitiva tale che leq(x, y) = 0 se e solo
se x < y;
2 Tale risultato si può mostrare facilmente ma utilizzando risultati e nozioni che vedremo in
seguito nel Cap. 17. Anticipando alcune nozioni, se A è un insieme ricorsivamente enumerabile
ma non ricorsivo, allora definendo:
ψ(x, y) = 0 ⇔ (y = 0 ∧ x ∈ A) ∨ y = 1
si ha che ψ è parziale ricorsiva; ma definendo f(x) = µy. (ψ(x,y) = 0), abbiamo che f non può
essere parziale ricorsiva altrimenti, se lo fosse, essendo chiaramente totale, sarebbe ricorsiva e
poiché f(x) = 0 ⇔ x ∈ A anche A sarebbe ricorsivo.
4. EQUIVALENZA TRA MDT E FUNZIONI PARZIALI RICORSIVE
109
• radice√n-esima (intera):
⌊ n x⌋ = µy. (leq(x, (y + 1)n ) = 0);
4. Equivalenza tra MdT e funzioni parziali ricorsive
Il seguente risultato dimostra l’equivalenza delle funzioni calcolabili da MdT
e le funzioni parziali ricorsive di Kleene & Robinson.
Teorema 12.14. f : N −→ N è parziale ricorsiva se e solo se è Turingcalcolabile.
Proof. Per dimostrare il verso (→), conviene dimostrare un teorema più
forte: se ϕ è parziale ricorsiva, allora esiste una MdT che la calcola che:
(1) Funziona anche se il nastro a sinistra dell’input e a destra della posizione
iniziale della testina non è una sequenza infinita di $.
(2) Quando termina, sappiamo che deve terminare a destra dell’output. Richiediamo inoltre che subito a sinistra dell’output ci sia l’input (o una copia di
esso).
(3) La parte del nastro che sta a sinistra della posizione iniziale della testina
(in particolare l’input) non viene modificata.
Queste 3 assunzioni ci permetteranno di applicare l’ipotesi induttiva.
Si tratta ora di realizzare (o descrivere a parole) le MdT che calcolano le
funzioni di base soddisfacendo alle restrizioni suddette e poi mostrare che disponendo delle MdT che soddisfano le condizioni sopra e che calcolano delle funzioni,
siamo in grado di definire quelle necessarie per calcolare la funzione che fa la
loro composizione (qui le proprietà sopra sono essenziali), ricorsione primitiva e
minimizzazione [Completare per esercizio].
Mostriamo invece l’implicazione inversa e cioé che se ϕ è Turing-calcolabile
allora ϕ è parziale ricorsiva.
Sia ϕ : N −→ N una funzione calcolabile da una MdT Z. L’obiettivo è quello
di codificare la MdT Z come una funzione sui naturali che sia parziale ricorsiva.
Per semplicità assumiamo, senza incorrere in limitazioni, che la MdT Z sia definita
con 2 simboli, che corrispondono ai numeri 0 e 1 rispettivamente:
Σ
=
Q =
{0, 1}
{q0 , . . . , qk }
Con queste ipotesi possiamo rappresentare una generica ID come una tupla di
numeri, associando un numero nell’intervallo [0, k] a ogni simbolo di stato in Q.
Definiamo ar : ID −→ N4 tale che, per ogni α ∈ ID della forma:
α = · · · b2 b1 b0 s qh c0 c1 c2 · · ·
110
12. FUNZIONI PARZIALI RICORSIVE DI KLEENE & ROBINSON
con s ∈ {0, 1}, h ∈ [0, k] e {bi , ci }i∈N ⊆ Σ, allora: ar(α) = (h, m, s, n) dove
m =
n
=
P∞
i=0
P∞
i=0
bi 2i =
ci 2i =
Pkm
i=0
Pkn
bi 2i
i
i=0 ci 2
ove km (kn ) è il massimo intero i per cui bi 6= 0 (rispettivamente ci 6= 0). In questo
modo, poiché il numero di 1 sul nastro non può essere infinito (siamo partiti con
un numero finito di 1 ed abbiamo effettuato un numero finito di passi), per ogni
ID in una computazione di Z, segue che ar associa in modo univoco ad ogni ID
una quadrupla di naturali (aritmetizzazione di ID).
Una computazione di una MdT è definita come una sequenza (anche infinita)
di ID: α1 ⊢ α2 ⊢ . . .. Definiamo dunque una funzione che esprime un passo di
transizione tra ID, ovvero una funzione che manipola quadruple di naturali. A tal
proposito, definiamo le seguenti funzioni: ∆ : Q × Σ −→ Q, ∇ : Q × Σ −→ Σ e
X : Q × Σ −→ {0, 1} tali che:
• ∆(q, s) è lo stato che la MdT Z assume trovandosi nello stato q con
simbolo in lettura s;
• ∇(q, s) è il simbolo che la MdT Z produce trovandosi nello stato q con
simbolo in lettura s;
• X (q, s) è lo spostamento a destra (= 1) o sinistra (= 0) compiuto dalla
MdT Z trovandosi nello stato q con simbolo in lettura s.
Per i valori non definiti si assegni il valore k + 1 per renderle totali. Si noti che
∆, ∇ e X sono funzioni definite dalla matrice funzionale di Z, e sono chiaramente
funzioni primitive ricorsive (lo si dimostri per esercizio). Possiamo quindi definire
le trasformazioni compiute eseguendo un singolo passo della MdT Z sulle quadruple
rappresentanti una generica ID di Z:
q′
s′
m
n′
′
=
∆(q, s)
=
(m mod 2)(1 − X (q, s)) + (n mod 2)X (q, s)
=
(2m + ∇(q, s))X (q, s) + (m div 2)(1 − X (q, s))
=
(2n + ∇(q, s))(1 − X (q, s)) + (n div 2)X (q, s).
Queste funzioni sono chiaramente primitive ricorsive, essendo la composizione di
funzioni primitive ricorsive. Possiamo dunque rappresentare una transizione α ⊢ β
nel modo seguente: Se ar(α) = (q, s, m, n) allora ar(β) = (q ′ , s ′ , m ′ , n ′ ).
Per rappresentare l’effetto di t-transizioni o passi di calcolo della MdT Z,
definiamo le seguenti funzioni di 5 argomenti:
• P∆ (t, q, s, m, n) è lo stato (∈ [0, k]) ottenuto dopo t-passi partendo da
una ID α tale che ar(α) = (q, m, s, n);
• P∇ (t, q, s, m, n) è il simbolo (∈ {0, 1}) ottenuto dopo t-passi partendo da
una ID α tale che ar(α) = (q, m, s, n);
4. EQUIVALENZA TRA MDT E FUNZIONI PARZIALI RICORSIVE
111
• PM (t, q, s, m, n) è il valore (∈ N) rappresentante il nastro a sinistra della
testina ottenuto dopo t-passi partendo da una ID α tale che ar(α) =
(q, m, s, n);
• PN (t, q, s, m, n) è il valore (∈ N) rappresentante il nastro a destra della
testina ottenuto dopo t-passi partendo da una ID α tale che ar(α) =
(q, m, s, n).
È facile vedere che, ad esempio, P∆ può essere definita nel modo seguente:
P∆ (0, q, s, m, n)
=
P∆ (t + 1, q, s, m, n) =
q
P∆ (t, q ′ , s ′ , m ′ , n ′ )
Supponiamo che la MdT Z abbia uno stato q1 ∈ Q di terminazione, ovvero tale
che le uniche caselle vuote della matrice funzionale di z siano in corrispondenza
della riga q1 . È chiaro che ogni MdT può essere trasformata in modo tale da
avere uno stato di terminazione di questo tipo. Pertanto, la MdT Z termina il
suo calcolo nel primo (minimo) t tale che: P∆ (t, q0 , s0 , m0 , n0 ) = 1, essendo α0
la configurazione iniziale tale che ar(α0 ) = (q0 , m0 , s0 , n0 ). Ovvero, il numero di
passi necessario per terminare, è definibile mediante µ-ricorsione come:
µt. (P∆ (t, q0 , s0 , m0 , n0 ) − 1 = 0).
Sia x ∈ N un numero naturale di input. Assumiamo che la MdT inizi il suo
calcolo avendo il numero x sul nastro alla destra della sua testina codificato come
una sequenza di x-zeri seguita da 1 (rappresentata matematicamente dal numero
2x ). Assumiamo anche che il risultato, se calcolato, sia rappresentato nel medesimo
modo sul nastro della macchina. È dunque chiaro che, avendo un modo algoritmico
primitivo ricorsivo dato da una funzione C per rappresentare come potenza di 2 il
numero alla destra della testina nella ID terminale, la funzione ϕ calcolata da Z è
esprimibile dalla seguente funzione parziale ricorsiva:
ϕ(x) = C(PN (µt. (P∆ (t, 0, 0, 0, 2x ) − 1 = 0), 0, 0, 0, 2x )).
Per esercizio si trovi la funzione C che chiude la dimostrazione e si dimostri che è
primitiva ricorsiva.
Corollario 12.15 (Forma normale di Kleene). Per ogni funzione ϕ ∈ PR
(o equivalentemente Turing calcolabile) esistono f, g ∈ P tali che
ϕ = λx. f((µt. g(t, x) = 0), x) .
CHAPTER 13
Tesi di Church-Turing
Negli stessi anni in cui venivano proposti i vari formalismi per definire la
effettiva calcolabilità, ad esempio le MdT, le funzioni ricorsive, il λ-calcolo etc.,
veniva dimostrata anche la loro l’equivalenza. Si dimostrava cioè che i diversi
sistemi permettono di calcolare esattamente la stessa classe di funzioni. Abbiamo
visto degli esempi nelle sezioni precedenti riguardanti le MdT e le funzioni parziali
ricorsive di Kleene & Robinson.
Questi risultati (in particolare l’equivalenza del λ-calcolo con le funzioni parziali
ricorsive) portarono Church e Turing nel ‘36 a formulare la seguente Tesi fondamentale:
Tesi di Church-Turing: La classe delle funzioni “intuitivamente calcolabili” coincide con la classe delle funzioni Turing
calcolabili.
Si tratta chiaramente di una tesi indimostrabile, vista l’impossibilità di maneggiare
formalmente (= matematicamente) la nozione di “intuitivamente calcolabile”. Accettando la Tesi di Church-Turing si osserva immediatamente la notevole importanza che assumono le funzioni calcolabili da MdT, o equivalentemente, le funzioni
parziali ricorsive o λ-definibili. Queste classi di funzioni vengono a rappresentare,
in virtù della Tesi di Church-Turing, la classe delle funzioni calcolabili effettivamente mediante un algoritmo. La Tesi di Church-Turing ha pertanto notevole
interesse metamatematico. Lo stesso Gödel scrisse nel ‘46 le seguenti osservazioni
riguardanti la Tesi di Church-Turing, in riferimento ad una presentazione di Tarski
sul medesimo argomento:
Tarski has stressed in his lecture (and I think justly) the great
importance of the concept of general recursiveness1 (or Turing’s computability). It seems to me that this importance is
largely due to the fact that with this concept one has for the
first time succeeded in giving an absolute definition of an interesting epistemological notion, i.e. one not depending on the
formalism chosen. [ . . . ] By a kind of miracle it is not necessary to distinguish orders and the diagonal procedure does not
lead outside the defined notion.
1 . . . che
noi chiamiamo ricorsività a la Kleene & Robinson.
113
114
13. TESI DI CHURCH-TURING
[K. Gödel, Princeton 1946]
La Tesi di Church-Turing permette di limitare l’estensione di ciò che è effettivamente calcolabile: se dimostriamo che una funzione non è parziale ricorsiva,
allora, in virtù della Tesi di Church-Turing, essa non è calcolabile in nessun modo
effettivo. Analogamente, dare un algoritmo per una funzione corrisponde a dimostrare, per la Tesi di Church-Turing, che essa è una funzione parziale ricorsiva.
Questo utilizzo della Tesi di Church-Turing è fondamentale in questo corso. Infatti,
sarà sufficiente dimostrare la realizzabilità di un algoritmo per affermare l’esistenza
di una MdT o di una funzione parziale ricorsiva corrispondente, senza dover esibire una tale macchina o una funzione per calcolarlo. Questo ci permetterà di
sviluppare un certo numero di strumenti a supporto della teoria delle calcolabilità,
senza dover esibire esplicitamente complesse MdT o funzioni parziali ricorsive.
Questa caratteristica della Tesi di Church-Turing darà un aspetto apparentemente
informale alle dimostrazioni di effettiva calcolabilità di funzioni. Dimostrazioni di
questo tipo, che si basano su una non banale applicazione della Tesi di ChurchTuring, saranno dette dimostrazioni mediante Tesi di Church-Turing. Pertanto,
ogni volta che una funzione risulterà “palesemente” calcolabile, la si accetterà come
effettivamente calcolabile da uno dei formalismi prescelti, senza dover costruire la
MdT o altra funzione corrispondente. Nel seguito dunque considereremo equivalenti i concetti di calcolabilità: “effettiva” (ovvero associata all’esistenza di una
MdT), “algoritmica” (ovvero associata all’esistenza di un algoritmo che soddisfi i
requisiti a–l) o “ricorsiva” (ovvero associata ad una funzione ricorsiva parziale che
la calcola).
A questo punto della trattazione, è bene distinguere, per chiarezza, tra due
nozioni diverse di calcolabilità. Infatti è possibile definire una funzione g per cui
sappiamo esistere un algoritmo ma per la quale non sappiamo dare l’algoritmo che
la calcola. Consideriamo le funzioni definite nel modo seguente:
1 se esattamente x ‘5’ consecutivi appaiono nella
f(x) =
espansione decimale di π
0 altrimenti
g(x) =
1
0
se almeno x ‘5’ consecutivi appaiono nella espansione
decimale di π
altrimenti
È chiaro che g è calcolabile: dato un algoritmo che genera l’espansione decimale
di π, g sarà o la funzione costante g(x) = 1, nel caso vi sia un numero arbitrariamente grande di occorrenze successive di ‘5’ in π, che è chiaramente una funzione
calcolabile; oppure sarà la funzione calcolata da un qualche algoritmo che, fissato
13. TESI DI CHURCH-TURING
un k (ed esempio il massimo numero di ‘5’ consecutivi in π), calcola:
1 se x ≤ k
g(x) =
0 se x > k
115
In questo secondo caso esiste sicuramente un tale k che permette di individuare
l’algoritmo cercato, ma ci è impossibile sapere quale sia il k giusto. In entrambi i
casi la funzione g è calcolabile (ed é in particolare primitiva ricorsiva), ma in generale non sappiamo costruire effettivamente un algoritmo per calcolarla. Al contrario, per f non siamo in grado a tutt’oggi di affermare nulla sulla sua calcolabilità
effettiva. Pertanto, mentre per g è possibile invocare la Tesi di Church-Turing, ed
affermare che esiste una MdT che la calcola, anche se non si sa quale, per f questo
non è possibile. Accettando quindi di considerare calcolabile ogni funzione per la
quale esiste un algoritmo che la calcoli, possiamo nel seguito utilizzare la Tesi di
Church-Turing ogni qual volta sia chiara l’esistenza di un algoritmo per calcolare
una data funzione.
CHAPTER 14
Aritmetizzazione e universalità
Aritmetizzazione significa semplicemente traduzione nel linguaggio dell’aritmetica.
Il primo utilizzo dell’aritmetizzazione è dovuto a Gödel nel 1931 nella dimostrazione
del risultato fondamentale di incompletezza dell’aritmetica; da qui anche il termine
equivalente di Gödelizzazione. Nel seguito considereremo le MdT come esempio
ed applicheremo alle MdT il concetto di aritmetizzazione. Per la Tesi di ChurchTuring, sappiamo che questa non è una restrizione, ed analoghe aritmetizzazioni
possono essere definite per ogni sistema formale equivalente alle MdT.
1. Enumerazione delle MdT
Sia Σ = {$, 0}. Quante sono le macchine di Turing ‘distinte’ descrivibili? Si
supponga Q = {q0 }. Si tratterà di riempire la tabella
$ 0
q0
in tutti i modi (significativi) possibili, ossia ogni singola casella potrà essere
(1) vuota (si immagini di scrivere $ $ $);
(2) q0 $ R;
(3) q0 $ L;
(4) q0 0 R;
(5) qo 0 L.
Dunque ci sono esattamente 25 = 5 × 5 macchine di Turing distinte ad un solo
stato.
Si può pensare a questo punto di definire un ordinamento ✁ sulle macchine di
Turing ad uno stato definibili; fissato un ordinamento su Σ (poniamo $ <Σ 0), sia
inoltre L <M R e si consideri la cella a sinistra più importante di quella a destra,
essendo M insieme dei possibili movimenti della testina, cioé M = {L, R}.
Si assuma che $ $ $ sia minore di qualunque terna q0 s m, allora si avrà (in
una estensione lessicografica i cui parametri sono, nell’ordine cella e contenuto):
h$ $ $, $ $ $i ✁ h$ $ $, q0 $ Li ✁ · · · ✁ h$ $ $, q0 0 Ri ✁ hq0 $ L, $ $ $i ✁ · · ·✁ hq0 0 R, q0 0 Ri.
Più in generale, sia Q = {q0 , . . . , qn−1 }, Σ = {s0 = $, s1 = 0}, m0 = L,m1 = R;
si definisca inoltre un ordinamento tra le 2 · n celle presenti (per righe, colonne,
117
118
14. ARITMETIZZAZIONE E UNIVERSALITÀ
a zig zag etc.). Definendo esplicitamente l’ordinamento per ogni cella nel modo
seguente:
• $ $ $ ≺ qi s m per ogni qi ∈ Q, s ∈ Σ, m ∈ {L, R};
• qi1 sj1 mk1 ≺ qi2 sj2 mk2 se i1 < i2 oppure (i1 = i2 e j1 < j2 ) oppure
(i1 = i2 e j1 = j2 e k1 < k2 ).
si ha un buon ordine sull’insieme delle macchine di Turing aventi n stati, che sono
in tutto
(n · 2 · 2 + 1)2n = (4 · n + 1)2n ,
dunque ci saranno 25 MdT ad uno stato, 94 MdT a due stati, 136 MdT a tre
stati,. . . . Si può dunque pensare di associare un numero naturale (un indice) ad
ogni macchina di Turing in maniera biunivoca:
1 ÷ 25
MdT ad uno stato
4
26 ÷ 25 + 9
MdT a due stati
25 + 94 + 1 ÷ 25 + 94 + 136
MdT a tre stati
...
...
all’interno di ogni gruppo, poi ci si basa sulla relazione di ordinamento. In generale,
con un semplice algoritmo, dato x ∈ N si può risalire alla MdT x-esima.
Esempio 14.1. Assumendo un ordinamento delle celle per cui la più importante è quella in alto a sinistra, la macchina di Turing n. 4399 ha due stati
(Q = {q0 , q1 }), e la sua funzione di transizione è la seguente:
δ
$
q0
q1 $ R
0
q1
Si osservi che tale MdT computa la funzione λx.0.
Una differente aritmetizzazione delle MdT si basa sulle proprietà della decomposizione in fattori primi dei numeri naturali. Analogamente al caso precedente, è possibile, in modo algoritmico, passare da una MdT ad un numero naturale che la aritmetizza, e viceversa è possibile determinare se un numero è o no
l’artimetizzazione di una MdT. Consideriamo ancora una generica MdT definita
su un alfabeto finito:
Σ
= {s0 , . . . , sn }
Q
= {q0 , . . . , qm }
X
= {L, R}
1. ENUMERAZIONE DELLE MDT
119
ed associamo, ad ogni simbolo x ∈ Σ ∪ Q ∪ X un numero dispari maggiore di 1:
a(x) = 2k + 1 per qualche k ≥ 1, tale che se x 6= y allora a(x) 6= a(y). Ad esempio:
R
L s0
q1
s1
q2
...
↓
↓
↓
↓
↓
...
3
5
↓
7
9
11
13
...
Definiamo numero di Gödel di una espressione E ∈ (Σ ∪ Q ∪ X)∗ il numero:
gn(E) =
k
Y
a(xi )
pi
i=1
essendo k il numero dei simboli in E, pi l’i-esimo numero primo e xi l’i-esimo simbolo di E. Per l’unicità della decomposizione in fattori primi, per ogni espressione
esiste uno ed un solo numero di Gödel corrispondente; inoltre tale numero è pari
nell’ipotesi E sia una stringa di almeno un simbolo.
Poiché le quintuple di una MdT sono a tutti gli effetti espressioni in (Σ∪Q∪X)∗ ,
è possibile in modo algoritmico associare ad ogni quintupla un corrispondente
numero di Gödel, e viceversa verificare se un dato numero è numero di Gödel di
una quintupla legittima.
Definiamo numero di Gödel di una MdT Z, ovvero di una sequenza di quintuple
Z = {E1 , . . . , Eh } il numero:
h
Y
gn(Ei )
pi
i=1
Questo numero è anch’esso unico, per l’unicità della decomosizione in fattori primi,
e non risulta confondibile con gn(E) per una qualche espressione E ∈ (Σ ∪ Q ∪ X)∗ ,
poiché, al contrario di gn, è ottenuto mediante esponenziazione pari. Analogamente, due sequenze con lo stesso numero di Gödel sono identiche (verificare per
esercizio).
In generale è possibile definire infinite aritmetizzazioni diverse per MdT, e
quindi funzioni parziali ricorsive. L’artimetizzazione scelta non è quindi sostanziale
nello sviluppo della teoria della calcolabilità. Essenziale è che, come dimostrato
in precedenza, esistano metodi algoritmici per codificare MdT all’interno dei numeri naturali (suoi argomenti di calcolo). La possibilità di vedere i numeri come
al tempo stesso rappresentanti gli argomenti del calcolo e le MdT che eseguono il
calcolo, permette di applicare ai sistemi formali analizzati fino ad ora, uno dei principi alla base dell’informatica moderna, ovvero quello di poter passare programmi
come argomenti ad altri programmi. In tutto ciò la scelta della particolare aritmetizzazione è inessenziale.1
1 La libertà di poter applicare MdT a MdT (autoapplicazione o referenziazione) condurrebbe
immediatamente a paradossi tipo paradosso di Russell nella teoria che stiamo sviluppando. Ancora una volta, l’assunzione fondamentale che le funzioni calcolabili siano essenzialmente parziali,
120
14. ARITMETIZZAZIONE E UNIVERSALITÀ
Nel seguito dunque indicheremo con:
• Px l’insieme di istruzioni di una MdT di indice x, ovex il corrispondente
indice o numero di Gödel della macchina, in una data aritmetizzazione.
• ϕx la funzione calcolata dalla macchina Px .
Il seguente risultato è banale in seguito ad una qualsiasi aritmetizzazione delle
MdT.
Teorema 14.2. Ci sono ℵ0 macchine di Turing distinte o funzioni parziali
ricorsive, e ℵ0 funzioni ricorsive.
Proof. Segue banalmente dal fatto che tutte le funzioni costanti sono ricorsive. Pertanto ci sono almeno ℵ0 funzioni ricorsive.
Segue dunque per la Tesi di Church-Turing che:
Teorema 14.3. Ci sono esattamente ℵ0 funzioni calcolabili.
Indicheremo nel seguito con ϕx la funzione parziale ricorsiva di indice o numero di
Gödel x. Chiaramente sappiamo che esiste un algoritmo per passare da x a Px e/o
ϕx e viceversa. Pertanto, con ϕx indicheremo indifferentemente l’x-esima MdT o
funzione parziale ricorsiva in una data aritmetizzazione.
Il seguente risultato dimostra che la cardinalità delle funzioni calcolabili è
strettamente minore della cardinalità di tutte le possibili funzioni sui naturali.
Si tratta di una rivisitazione del Teorema di Cantor, nel contesto delle funzioni
calcolabili. Ne consegue che l’insieme delle funzioni calcolabili è strettamente
contenuto (ne rappresenta una piccolissima parte) nell’insieme di tutte le funzioni,
ovvero nell’insieme di tutti i possibili problemi esprimibili mediante funzioni sui
naturali. In particolare, potendo selezionare una funzione in N −→ N, è facile
dimostrare che la probabilità di selezionarne una calcolabile è 0.
Teorema 14.4. Esistono funzioni (totali) f : N −→ N non Turing calcolabili.
Proof. Sappiamo che, dal Teorema di Cantor (vedi Cap. 2) che:
|{f : N −→ N}| ≥
|{f : N −→ {0, 1}}|
=
|℘(N)|
>
|N|
| f : N −→ N : f è Turing calcolabile |
≥
Rispetto a quanto visto nelle sezioni precedenti, abbiamo pertanto dimostrato
le inclusioni tra classi di funzioni come evidenziate nella Figura 1.
permette di superare tali paradossi, rendendo la teoria della calcolabilità una teoria consistente.
Si veda in proposito la Nota 15.4.
2. MACCHINA DI TURING UNIVERSALE
✬
N −→ N
✛
calcolabili
✫
✚
121
✩
✘
✙
✪
Figure 1. Diagramma di inclusione tra insiemi di funzioni
2. Macchina di Turing Universale
Per quanto si è illustrato nella sezione 1, dato un numero naturale x si può
facilmente risalire ad una tabella definente la funzione di transizione della x-esima
macchina di Turing. D’altro canto ogni singola macchina di Turing è un oggetto
provvisto di un compito fisso. Una volta definita la funzione di transizione non vi
è modo di modificarla.
Il passo immediatamente successivo (che fornisce la base teorica al concetto di
calcolatore programmabile) è il seguente: si supponga di disporre della macchina
di Turing con un unico input che, interpretiamo come una coppia hx1 , x2 i, con il
seguente comportamento:
• x1 è l’indice di una macchina di Turing Px1 ;
• x2 è la rappresentazione di un input che va immaginato come input per
la macchina Px1 .
La macchina in questione dapprima calcola la funzione di transizione della
macchina Px1 , memorizzando questa funzione. Suppone poi (cioè lo memorizza
opportunamente) di essere nello stato q0 . Si posiziona dunque in corrispondenza del primo carattere di input (chiamiamolo s) e va a vedere—sulla tabella
memorizzata—cosa farebbe Px1 , ossia computa δPx1 (q0 , s). Procede dunque nella
simulazione andando a modificare il carattere sulla zona di nastro dedicata all’input,
lo stato nella opportuna zona in cui va memorizzato, e sposta idealmente la testina,
in base al risultato di δPx1 (q0 , s).
Una tale MdT si riesce ovviamente a costruire (anche se non può essere considerato un facile esercizio) e dunque avrà un suo indice nella enumerazione delle
MdT; sia esso u. Si avrà pertanto:
∀x1 x2 . Pu (hx1 , x2 i) = Px1 (x2 )
ove il simbolo = in questo contesto sta a significare che, se una macchina termina,
allora terminano entrambe fornendo lo stesso risultato, altrimenti entrambe ciclano. Come interessante ed immediata conseguenza avremo, ad esempio, Pu (hu, ui) =
Pu (u).
Più in generale possiamo dimostrare il seguente risultato, che rappresenta una
prima dimostrazione mediante Tesi di Church.
122
14. ARITMETIZZAZIONE E UNIVERSALITÀ
Teorema 14.5. Esiste un indice z tale che per ogni x e y,
ϕ (y) se ϕ (y) è definita
x
x
ϕz (x, y) =
↑
altrimenti
Proof. Sia Px l’insieme di istruzioni di una MdT di indice x. Sappiamo che
esiste un metodo effettivo per ottenere Px da x. Applichiamo Px all’input y e,
quando questa termina, prendiamo il risultato come output di una funzione di 2
argomenti ψ(x, y). Pertanto abbiamo che:
ϕ (y) se ϕ (y) converge
x
x
ψ(x, y) =
↑
se ϕ (y) diverge
x
Applicando la Tesi di Church-Turing, possiamo concludere che ψ è parziale ricorsiva e pertanto esiste un indice z tale che ψ = ϕz .
La funzione ϕz ottenuta nella dimostrazione del precedente teorema è detta
funzione parziale universale e corrisponde alla MdT universale vista in precedenza.
Chiaramente, il precedente teorema può essere generalizzato a funzioni di k ≥ 1
variabili, in modo tale che, per ogni funzione di k ≥ 1 variabili, esiste una funzione
di k + 1 variabili che gioca il ruolo di funzione universale parziale.
Esercizio 14.6. Formulare e dimostrare il precedente teorema per funzioni
di k ≥ 1 variabili.
3. Il Teorema s-m-n
Il seguente risultato, noto con il nome Teorema s-m-n e dovuto a Kleene,
sarà fondamentale nel seguito. Anch’esso rappresnta un esempio di dimostrazione
basata sulla Tesi di Church-Turing.
Teorema 14.7 (s-m-n). Per ogni coppia di interi m, n ≥ 1 esiste una funzione
ricorsiva sm
n di m + 1 variabili tale che per ogni x, y1 , . . . , ym abbiamo che:
λz1 · · · zn . ϕx (y1 , . . . , ym , z1 , . . . , zn ) = ϕsm
n (x,y1 ,...,ym )
Proof. Consideriamo per semplicità il caso m = n = 1. La dimostrazione per
il caso più generale per m, n ≥ 1 è simile. Fissati x0 e y0 , la funzione λz. ϕx0 (y0 , z)
è chiaramente una funzione calcolabile in una sola variabile. Per ogni x0 e y0 ,
la funzione λz. ϕx0 (y0 , z) dipenderà costruttivamente da x0 e y0 , pertanto esisterà per la Tesi di Church-Turing, una funzione ricorsiva (totale!) s tale che:
λz. ϕx0 (y0 , z) = ϕs(x0 ,y0 ) .
Il Teorema s-m-n gioca un ruolo essenziale nello sviluppo della teoria della ricorsività, e spesso verrà utilizzato, come la Tesi di Church-Turing, tacitamente. Riportato alla programmazione ‘tradizionale’, il significato è più o meno il seguente.
Se avete scritto un programma che implementa un certo algoritmo su m + n dati
3. IL TEOREMA S-M-N
123
in imput e da un certo momento in poi m di questi sono fissati, allora sapete ottenere un programma ‘specializzato’ che accetta solo n parametri in input. Questa
è la giustificazione al lavoro delle software house che ‘installano’ del software gestionale generale alle varie ditte (che permettono di fissare, con i loro dati, gli m
parametri).
Il seguente risultato è una semplice applicazione del Teorema s-m-n.
Teorema 14.8. Esiste una funzione ricorsiva g in due argomenti tale che per
ogni x e y: ϕg(x,y) = ϕx ◦ϕy .
Traccia. Si definisca ψ(x, y, z) = ϕx (ϕy (z)) se ϕy (z) ↓ e ϕx (ϕy (z)) ↓,
indefinita altrimenti. Concludere per esercizio.
CHAPTER 15
Problemi insolubili
In questo Capitolo analizzeremo alcuni problemi classici non risolvibili nella
teoria della effettiva calcolabilità sviluppata fino ad ora. Il più classico problema
insolubile è il problema della terminazione posto da Turing.
Esiste una procedura effettiva tale che dati x e y determina se
ϕx (y) è definita o no?
Il seguente risultato preliminare dimostra l’insolubilità di un problema correlato.1
Lemma 15.1. Non esiste una funzione ricorsiva g tale che per ogni x:
1 se ϕx (x) ↓
g(x) =
0 se ϕ (x) ↑
x
Proof. Supponiamo per assurdo che esista una MdT di indice i0 tale che
g = ϕi0 . Allora possiamo definire la funzione:
↑ se g(x) = 1 = ϕ (x)
i0
g ′ (x) =
0 se g(x) = 0 = ϕ (x)
i0
′
Dunque anche g sarà calcolabile e dunque esiste i1 tale che ϕi1 = g ′ . Ma allora
abbiamo che:
ϕi1 (i1 ) ↓ ⇔ g ′ (i1 ) = 0
ϕi1 (i1 ) ↑ ⇔
g ′ (i1 ) ↑
⇔ g(i1 ) = 0 ⇔ ϕi1 (i1 ) ↑
⇔ g(i1 ) = 1 ⇔ ϕi1 (i1 ) ↓
Che è assurdo. Dunque non esiste un tale indice i1 , ovvero non esiste l’indice i0
che calcoli g, ovvero g non è ricorsiva.
In base al Lemma 15.1, segue il teorema:
Teorema 15.2. Non esiste una funzione ricorsiva ψ tale che per ogni x e y:
1 se ϕ (y) ↓
x
ψ(x, y) =
0 se ϕ (y) ↑
x
1 Si ricorda che con ϕ (y) ↓ si intende che la macchina di Turing x-esima sull’input y
x
termina o equivalentemente, che la funzione ricorsiva parziale x-esima è definita per l’input x.
Con ϕx (y) ↑ si intende l’opposto di tale affermazione.
125
126
15. PROBLEMI INSOLUBILI
Proof. Se esistesse allora esisterebbe un indice x0 tale che ϕx0 (x) = ψ(x, x).
ψ(x, x) non è altro che la funzione g(x) dimostrata non esistere nel Lemma 15.1.
Il problema della terminazione è stato, storicamente, uno dei primi problemi
matematici dimostrati come insolubili, ovvero per i quali non esiste una procedura
in grado di “decidere” il problema per ogni possibile input, e rappresenta, per la
sua semplicità ed importanza pratica e teorica, uno dei risultati più importanti
ottenuti in matematica nel ventesimo secolo.
Il seguente risultato, dovuto a Kleene, dimostra la non risolubilità di un altro
semplice problema correlato alle funzioni ricorsive, ovvero non è possibile decidere
se una funzione parziale ricorsiva data è totale. Il problema è chiaramente legato
alla non decidibilità della terminazione.
Teorema 15.3. Non esiste una funzione ricorsiva f tale che per ogni x:
1 se ϕ è totale
x
f(x) =
0 se ϕ non è totale
x
Proof. La dimostrazione utilizza un argomento diagonale. Supponiamo che
esista una tale funzione f. Allora definiamo una funzione che associa ad ogni
naturale una corrispondente funzione ricorsiva.
g(0)
=
g(x + 1) =
µy. (f(y) = 1)
µy (y > g(x) ∧ f(y) = 1).
Poiché sappiamo che esistono ω funzioni ricorsive totali, anche g sarà totale. Per
la tesi di Church e questa osservazione è quindi ricorsiva. Definiamo una funzione
h tale che h = λx. ϕg(x) (x) + 1. Per la definizione di f segue che h è totale,
e quindi, per la Tesi di Church-Turing, ricorsiva. Sia dunque z0 l’indice per cui
h = ϕz0 e sia y0 tale che g(y0 ) = z0 . Questo indice esiste per definizione di g.
Allora h(y0 ) = ϕg(y0 ) (y0 ) + 1. Ma ϕg(y0 ) (y0 ) = h(y0 ) per definizione di y0 .
Poiché h è totale, questa è una contraddizione.
Nota 15.4. Argomenti diagonali e insolubilità sono nozioni strettamente legate
tra loro. Ritorniamo brevemente sulla questione delle funzioni parziali. Abbiamo
visto che, ricorrendo alle funzioni parziali, si possono superare i limiti imposti da
argomenti di tipo diagonale ai sistemi formali introdotti per definire il concetto
di effettiva calcolabilità. La necessità di utilizzare funzioni parziali in luogo di
funzioni totali segue anche dalla possibilità, dimostrata in precedenza, di definire
macchine universali o funzioni ricorsive universali. Questa possibilità, associata
alla ipotesi di totalità delle funzioni calcolabili, conduce infatti direttamente a
15. PROBLEMI INSOLUBILI
127
paradossi simili al paradosso di Russell.2 L’aritmetizzazione infatti non impone
nessuna distinzione tra funzioni (MdT) e argomenti, come nella teoria degli insiemi classica non vi è distinzione tra insiemi e loro argomenti. Questa mancanza
di struttura permette una indisciplinata possibilità di definizioni di insiemi o funzioni, che conduce ai ben noti paradossi.
È noto che è possibile codificare coppie, terne etc. con numeri naturali. Ad
esempio la seguente funzione g : N × N −→ N è biiettiva (verificare per esercizio)
e chiaramente calcolabile:
1
g(x, y) = (x2 + 2xy + y2 + 3x + y).
2
Essa permette di associare in modo univoco, ad ogni coppia di naturali, un numero
naturale corrispondente. Quindi per la Tesi di Church-Turing esisterà un indice
x0 tale che, se U(x, y) è la funzione parziale universale vista in precedenza, allora:
U(x, y) = ϕx0 (g(x, y))
Inoltre, essendo sia g che ϕx0 calcolabili, esisterà un indice y0 tale che ϕy0 (x) =
ϕx0 (g(x, x)). Sarà quindi lecito definire la funzione:
1 se ϕ (x) = ϕ (x) = 0
y0
x
f(x) =
0 se ϕ (x) = ϕ (x) 6= 0
y0
x
Anche questa funzione avrà un opportuno indice z0 . Segue quindi che ϕz0 (z0 ) = 0
se e solo se ϕz0 (z0 ) 6= 0, che è chiaramente una contraddizione. Per superare
questo paradosso, del tutto analogo al paradosso di Russell, analogamente a quanto
visto per superare i limiti imposti dalla diagonalizzazione, basterà ancora assumere
che la funzione calcolabile f, calcolata dalla z0 -esima MdT, diverga in z0 , essendo
quindi parziale ricorsiva. La funzione
1 se ϕx (y) ↓
U(x, y) =
↑ se ϕ (y) ↑
x
è dunque necessariamente parziale ricorsiva, ed infatti, l’algoritmo che la calcola è
facilmente identificabile con la MdT universale vista in precedenza.
Esercizio 15.5. Dimostrare che i seguenti problemi non ammettono soluzione
ricorsiva:
(1) Decidere se, per ogni x: ϕx = λx.0. (Traccia: Sia f la funzione ricorsiva
parziale definita come f(x, y) = 0 se ϕx (y) ↓, indefinita altrimenti. Per
s-m-n si ha che f(x, y) = ϕr(x) (y) per r totale ricorsiva. Se, per assurdo,
esistesse g t.c. g(x) = 1 se ϕx = λx.0, g(x) = 0 altrimenti, si definisca
2 Il paradosso di Russell nella teoria classica degli insiemi è ottenuto considerando come
definibile nella teoria stessa l’insieme A = {x | x 6∈ x}. Quindi x ∈ A ⇔ x 6∈ x, da cui segue che
A ∈ A ⇔ A 6∈ A.
128
15. PROBLEMI INSOLUBILI
h(x) = g(r(x)) e si giunga ad un assurdo rispetto all’enunciato del Lemma
15.1).
(2) Decidere se, per ogni x: ϕx è costante;
(3) Decidere se per ogni x e y: ϕx = ϕy . (Traccia: si mostri che allora si
saprebbe decidere il problema 1).
(4) Decidere se per ogni x, y e z: ϕx (y) = z.
CHAPTER 16
Calcolabilità e Linguaggi di Programmazione
In questo capitolo vedremo alcune applicazioni tra le più importanti della teoria elementare della calcolabilità vista nel precedente capitolo al progetto ed alla
implementazione dei moderni linguaggi di programmazione. In particolare metteremo in evidenza come l’espressività (in senso lato) di un linguaggio dipenda dalla
possibilità offerta da quest’ultimo di codificare uno dei formalismi affrontati per
definire il concetto di effettiva calcolabilità. Questi problemi hanno dato origine,
negli anni ‘60, alla ricerca di linguaggi di programmazione ad alto livello, in grado
cioè di esprimere tutte le funzioni effettivamente calcolabili. Tale ricerca ha dato
origine a una vasta famiglia di linguaggi come ALGOL e PASCAL, progenitori dei
più moderni linguaggi C, C++, e Java. Inoltre vedremo come l’esistenza (almeno
teorica) di strumenti ampiamente utilizzati nella moderna pratica informatica per
manipolare programmi, come i compilatori, gli interpreti, e i programmi di specializzazione, dipenda dai risultati di universalità visti nella caratterizzazione delle
funzioni calcolabili.
1. Il linguaggio While
Abbiamo fino ad ora definito il concetto di effettiva calcolabilità su una semplicissima struttura dati: i numeri naturali N. Come vedremo, questa apparente
restrizione in realtà si dimostra essere perfettamente equivalente ad ogni definizione
di calcolabilità data su strutture dati più evolute, quali stringhe, alberi etc. I numeri naturali sono pertanto sufficienti per rappresentare ogni possibile struttura
dati su cui si vogliono definire algoritmi.
Al fine di evitare l’aritmetizzare dei programmi, in questo capitolo studieremo
un semplice linguaggio di programmazione imperativo chiamato While in grado
di manipolare strutture dati più complesse. While è un linguaggio ad alto livello
adeguato per rappresentare tutte le funzioni calcolabili che permette di manipolare
semplici strutture dati. Le strutture dati impiegate in While sono molto simili a
quelle impiegate nei linguaggi Scheme e LISP. Questa estensione ci permetterà di
evitare l’aritmetizzazione (operazione di interesse preminentemente matematico)
permettendo quindi di considerare direttamente i programmi come dati. Questo,
come abbiamo visto, è il passo fondamentale per definire il concetto di funzione
universale.
129
130
16. CALCOLABILITÀ E LINGUAGGI DI PROGRAMMAZIONE
2. Strutture dati
Le strutture dati del linguaggio While sono alberi binari. Questi permettono
di rappresentare la sintassi concreta dei programmi come dati. Sia A un insieme
finito di atomi, o espressioni elementari, e nil l’albero vuoto. L’insieme degli alberi
DA è definito ricorsivamente come il più piccolo insieme tale che:
nil ∈ DA
A ⊆ DA
∀d1 , d2 ∈ DA . (d1 .d2 ) ∈ DA
In altri termini, se A = {a1 , . . . , an }, DA è il linguaggio generato dalla grammatica
CF: DA −→ nil | a1 | · · · | an | (DA .DA ).
Ad esempio, l’albero in Figura 1 è rappresentato come: ((a.((b.b).c)).d), dove
A = {a, b, c, d} è il corrispondente insieme di atomi.
((a.((b.b).c)).d) •??
???
??
(a.((b.b).c)) •??
d
?
??
??
a
•??((b.b).c)
??
??
?
?(b.b)
•
c
?
??
??
?
b
b
Figure 1. L’albero ((a.((b.b).c)).d)
Teorema 16.1. DA è isomorfo a N
Traccia. È semplice definire una biiezione tra N e DA . Ad esempio si possono
prima contare gli alberi di altezza 0, poi quelli di altezza 1 e cosı̀ via. Ad esempio,
se A = {a1 , . . . , an }:
0
1
2
...
n
nil a1
a2
...
an
n+1
n+2
...
(nil.nil) (nil.a1 ) . . .
n2 + 3n + 2 n2 + 3n + 3 . . .
(an .an )
(nil.(nil.nil))
...
4. SEMANTICA
131
3. Sintassi
Nel seguito assumeremo di avere a disposizione un insieme infinito e numerabile
di variabili Var. La sintassi di While è definita da una grammatica CF nel modo
seguente dove x, y ∈ Var, e d ∈ DA (per essere precisi, dovremmo scrivere DA
in luogo di d; tuttavia ci farà comodo nel seguito identificare con d un arbitrario
elemento di d ∈ DA , pertanto usiamo questa notazione):
Exp
Com
Prog
→ x | d | cons(Exp1 , Exp2 ) | hd(Exp) | tl(Exp) | Exp1 = Exp2
→ x := Exp | Com1 ; Com2 | skip | while Exp do Com endw
→ read(Listavar); Com; write(Listavar)
Listavar → x | x, Listavar
Assumiamo inoltre che in Listavar le variabili siano tutte distinte. Si osservi come
in questo linguaggio non vi sia la dichiarazione di tipo per le variabili. Tutte le
variabili infatti possono assumere solo valori di “tipo” DA .
Esempio 16.2. Il seguente programma While è ben definito [darne l’albero
di derivazione per esercizio].
read(x);
y := nil;
while x do
y := cons(hd(x), y);
x := tl(x)
endw;
write(y)
4. Semantica
La semantica di un programma While è data in termini di un sistema di
transizione [26, 31]. Per definirla, dobbiamo prima definire il concetto di stato.
Questo rappresenta la memoria della macchina preposta all’esecuzione di programmi While. La memoria può essere vista come una nastro in cui ogni cella
corrisponde esattamente ad una variabile in Var e può contenere esclusivamente
valori in DA oppure non contenere alcun valore (essere indefinita o cella vuota)—in
tal caso diremo che contiene ⊥. Pertanto, uno stato σ è rappresentabile come una
funzione parzialmente definita da variabili in valori σ : Var → DA ∪ {⊥}. L’insieme
di tutti gli stati è detto State. Se σ ∈ State, allora il valore dello stato (memoria)
σ in corrispondenza alla variabile x ∈ Var è dato dal valore della funzione σ in x,
ovvero σ(x) ∈ DA . Un comando ha lo scopo di modificare lo stato corrente.
132
16. CALCOLABILITÀ E LINGUAGGI DI PROGRAMMAZIONE
E[[x]]σ = σ(x)
E[[d]]σ = d
E[[cons(E1 , E2 )]]σ = (E[[E1 ]]σ.E[[E2 ]]σ)
c
se E[[E]]σ = (t.c)
E[[tl(E)]]σ =
nil altrimenti
E[[E1 = E2 ]]σ = (E[[E1 ]]σ = E[[E2 ]]σ)
t
se E[[E]]σ = (t.c)
E[[hd(E)]]σ =
nil altrimenti
Table 1. Semantica delle espressioni
La semantica intuitiva dei comandi di While è data nel seguente modo:
I programmi hanno variabili di ingresso (read) e uscita (write).
Possono usare inoltre quante altre variabili si voglia, prese da
Var. I comandi modificano lo stato nel seguente modo:
• skip lascia lo stato invariato;
• x := Exp memorizza il valore di Exp calcolato nello stato
corrente nella cella di memoria rappresentata dalla variabile x;
• C1 ; C2 corrisponde all’esecuzione del comando C2 nello
stato lasciato dall’esecuzione del comando C1 ;
• while E do C endw esegue il comando C fino a che l’albero
rappresentato dalla espressione E non è vuoto.
La semantica di un programma:
read(x1 , . . . , xn ); C; write(y1 , . . . , ym )
è quindi data dalla funzione che associa ad ogni variabile in
ingresso x1 , . . . , xn il valore delle variabili in uscita y1 , . . . , ym
determinato nello stato modificato da C.
Possiamo quindi dare la semantica formale di While induttivamente sulla sintassi
come sistema di transizione:
Semantica delle espressioni: Ogni espressione rappresenta un albero.
Al fine di valutare espressioni contenenti variabili è necessario per la
loro valutazione ricorrere alla memoria. La semantica delle espressioni è
data dunque da una funzione di interpretazione semantica:
E : Exp × State → DA ∪ {⊥}
definita come in Tabella 1. Il risultato dell’espressione E1 = E2 può
essere false oppure true; queste entità sintattiche possono essere usate
liberamente come abbreviazioni degli alberi nil e (nil.nil), rispettivamente.
4. SEMANTICA
133
Semantica dei comandi: La semantica dei comandi è definita induttivamente sulla sintassi dal sistema di transizione in Tabella 2. Le configurazioni del sistema di transizione sono definite da coppie: Com × State.
Ogni configurazione hC, σi rappresenta il comando C da eseguire e lo
stato σ in cui questo viene eseguito. Il sistema di transizione definisce
una relazione −→⊆ (Com × State) × (Com × State) che rappresenta
il generico passo di calcolo. Nel seguito, se il comando è vuoto (stringa
vuota ε), allora la configurazione corrispondente hε, σi è rappresentata
semplicemente come σ.
E[[E]]σ=d
hx:=E,σi−→ σ[d/x] hskip,σi−→ σ
hC1 ,σi−→ σ ′
hC1 ;C2 ,σi−→ hC2 ,σ ′ i
hwhile E
E[[E]]σ=nil
C endw,σi−→ σ
do
E[[E]]σ6=nil
hwhile E
do
C
endw,σi−→ hC;while
E
do
C
endw,σi
Table 2. Semantica dei comandi
Semantica dei Programmi: La semantica di un programma While è
definita a partire dalla semantica dei comandi, o meglio dalla chiusura
transitiva −→∗ della relazione di transizione dei comandi. Essa definisce
una funzione parziale da alberi in alberi, ovvero:
[[·]]W : Prog → (DA → DA ∪ {↑})
La semantica [[P]]W di un programma
P = read(x1 , . . . , xn ); C; write(y1 , . . . , ym )
è definita come segue, per ogni
e1 , . . . , em
W
[[P]] (d1 , . . . , dn ) =
↑
d1 , . . . , dn ∈ DA :
se hC, [d1 /x1 , . . . , dn /xn ]i −→∗ σ ′
e σ ′ (y1 ) = e1 , . . . , σ ′ (ym ) = em
altrimenti
Esempio 16.3. Come esempio di semantica di un semplice programma While,
consideriamo la semantica del programma P dell’Esempio 16.2 che calcola l’inverso
di una lista rappresentata come un albero sbilanciato a sinistra.
134
16. CALCOLABILITÀ E LINGUAGGI DI PROGRAMMAZIONE
Esercizio 16.4. Il caso del ⊥, o meglio della propagazione del valore indefinito, è trattato in modo incompleto nella Tabella 1. Per esercizio, si pensi
a come modificare la semantica delle espressioni in modo da migliorare questo
aspetto.
Esercizio 16.5. Si fornisca una semantica formale per i comandi:
• if Exp then C1 else C2 , dal significato intuitivo seguente: si valuti l’espressione
Exp; se è diverso da nil si esegua l’istruzione C1 , altrimenti si esegua
l’istruzione C2 .
• for x := Exp do C endfor. La semantica intuitiva di questo importante
costrutto, ripreso nella Sezione 6, è la seguente: si valuta Exp sul valore
dello stato iniziale. Essa sarà un albero che necessita di esattamente
n ≥ 0 operazioni di tipo tl per restituire l’espressione nil. L’istruzione C
viene quindi ripetuta per n volte; x all’inizio ha il valore iniziale di Exp,
poi viene via via ridotto fino a raggiungere x = nil. x e le variabili che
occorrono in Exp non possono essere modificate entro C.
Esercizio 16.6. Si mostri, scrivendo frammenti di codice While in cui sono
introdotte (se servono) ulteriori variabili, che i due costrutti suddetti possono essere
definiti all’interno del linguaggio While (in altri termini che il codice scritto ha,
sulle variabili originarie, la stessa semantica).
Esercizio 16.7. Si mostri come il comando if-then-else dell’esercizio precedente possa essere simulato dal comando for definito nello stesso esercizio. Suggerimento: se α è l’espressione dell’if-then-else, si assegnino le variabili u e v nel
seguente modo: u := (α = nil); v := (u = nil); Si eseguano dunque due cicli for:
uno controllato da u che esegue C2 e uno da v che esegue C1 .
Esercizio 16.8. Sia A = {a, . . . , z}. Si scriva un Programma While che
verifica se un elemento x1 di A è presente nell’albero x2 (restituisce y = 1 in caso
affermativo, y = 0 altrimenti).
5. Espressività di While e Turing completezza
È possibile rappresentare i numeri naturali in DA . La rappresentazione in DA
del numero n è denotata n. La seguente definizione illustra uno dei modi a nostra
disposizione di codificare i numeri naturali:
0 = nil
n + 1 = (nil . n)
Utilizzando il cons e la sua semantica, anche cons(nil, cons(nil, . . . , cons(nil, nil) · · · )
{z
}
|
rappresenta il numero n.
n
5. ESPRESSIVITÀ DI WHILE E TURING COMPLETEZZA
135
Definizione 16.9. Una funzione f : Nk → N è While-calcolabile se esiste un
programma While P tale che per ogni x1 , . . . , xk ∈ N:
[[P]]W (x1 , . . . , xk ) = f(x1 , . . . , xk )
Esempio 16.10. Il seguente programma While calcola la funzione f(x, y) =
x + y:
read(x1 , x2 );
while x2 do
x1 := cons(nil, x2 );
x2 := tl(x2 )
endw;
write(x1 )
Esercizio 16.11. Si scrivano i programmi While per il calcolo della differenza
tra numeri naturali, del prodotto, delle operazioni div, mod, e del test x < y.
L’ampiezza della classe delle funzioni While-calcolabili è determinata dal
seguente teorema, che conferma la Tesi di Church-Turing anche per i linguaggi
di programmazione imperativa.
Teorema 16.12. f : N → N è While-calcolabile sse è Turing-calcolabile.
Proof. (→) Per induzione sulla sintassi di While, costruiamo una MdT
che calcola la medesima funzione calcolata dal corrispondente programma While.
Poiché i programmi sono costituiti da sequenze di comandi, è sufficiente indurre
sulla complessità sintattica dei comandi. Similmente a quanto fatto nella dimostrazione del Teorema 12.14, poniamo delle restrizioni alle MdT che costruiamo
in modo tale da semplificare l’applicazione dell’ipotesi induttiva.
(1) Per semplicità, l’alfabeto delle MdT coincide con l’alfabeto della grammatica usata per costruire DA .
(2) Tali macchine hanno, prima dell’esecuzione, a sinistra dell’input la descrizione del contenuto delle variabili x1 , x2 , x3 , . . . (solo un numero finito
di queste è diverso da 0). Alla fine della computazione (se termina)
a sinistra della testina vi sarànno invece i valori finali di tali variabili
(poiché all’inizio solo un numero finito di queste era diverso da 0 e la
computazione ha per ipotesi lunghezza finita, anche alla fine avremo solo
un numero finito di x1 , x2 , x3 , . . . diverse da 0).
(3) Ci serviranno MdT per il calcolo di espressioni. In tal caso assumiamo
che la parte sinistra del nastro sia come sopra, mentre immediatamente
a destra della testina sia memorizzata l’espressione calcolata.
(4) Inoltre le macchine sono definite in modo tale da essere indipendenti dal
contenuto del nastro sulla parte destra rispetto alla posizione iniziale
della testina.
136
16. CALCOLABILITÀ E LINGUAGGI DI PROGRAMMAZIONE
Base: Vi sono 3 MdT di base:
• Calcolo di una espressione E: In E vi saranno dati da DA e variabili. Si
costruisce l’espressione a partire da ciò. [Esercizio], mettendo il risultato
subito a destra della posizione finale della testina.
• istruzione skip: una MdT che non fa nulla soddisfa a tutti i requisiti.
• xi := E. Si parte dal calcolo dell’espressione (vedi sopra). Una volta
pronta, si mette il valore dell’espressione calcolata nella posizione della
variabile xi , dopo avere spostato a destra i contenuti delle variabili x1 , . . . , xi−1 .
Passo: Come caso induttivo, affrontiamo i 3 casi possibili:
(1) Supponiamo che la MdT M1 simuli l’istruzione C1 e la MdT M2 simuli
l’istruzione C2 :
Si attiva la macchina M1 . Se questa termina, si attiva la
macchina M2 .
(2) Analizziamo il caso del comando while. Supponiamo di avere, per ipotesi
induttiva, una MdT MC per il comando C ed una ME per la valutazione
della espressione E. La seguente MdT corrisponde alla esecuzione del
comando while E do C endw:
Si attiva ME . Se sul nastro a destra della testina vi è nil, allora
termina. Altrimenti si attiva MC e, qualora termini, si reitera.
(→) Sia f una funzione Turing calcolabile. Dal Teorema 12.14, sappiamo che
f ∈ PR. La dimostrazione prosegue per induzione sulla struttura delle funzioni
parziali ricorsive. Costruiamo ed utilizziamo solo programmi che inizializzano
tutte le variabili che usano.
Base:
• λx. 0: read(x); x := nil; write(x);
• λx. x + 1: read(x); x := cons(nil, x); write(x);
• λx1 . . . xn . xi : read(x1 , . . . , xn ); skip; write(xi ).
Passo: Analizziamo i tre casi induttivi.
(1) Supponiamo per ipotesi induttiva che esista un programma Ph :
h
h
read(xh
1 , . . . , xk ); Ch ; write(y )
che calcola la funzione h e Pg1 , . . . , Pgk :
read(xg1 i , . . . , xgni ); Cgi ; write(ygi )
che calcolano le funzioni g1 , . . . , gk . Supponiamo inoltre che tali programmi usino tutti variabili diverse (altrimenti le ridenominiamo). Sia µ
il massimo indice di variabile usato in quei programmi. Allora la funzione
f(x1 , . . . , xk ) = h(g1 (x1 , . . . , xn ), . . . , gk (x1 , . . . , xn )) si calcola nel modo
5. ESPRESSIVITÀ DI WHILE E TURING COMPLETEZZA
137
seguente:
read(xµ+1 , . . . , xµ+n );
xg1 1 := xµ+1 ; . . . ; xgn1 := xµ+n ; Cg1 ;
..
.
gk
g1
h
xg1 k := xµ+1 ; . . . ; xgnk := xµ+n ; Cgk ; xh
1 := y ; . . . ; xk := y ; Ch ;
write(yh )
(2) Sia ora f definita per ricorsione primitiva (per semplicità denotazionale
usiamo x invece di x1 , . . . , xn ):
f(x, 0) = g(x)
f(x, y + 1) = h(x, y, f(x, y))
e supponiamo, per ipotesi induttiva, che alle funzioni g ed h corrispondano i programmi:
read(xg1 );
Cg ;
write(yg )
h h
read(xh
1 , x2 , x3 );
Ch ;
write(yh )
Supponiamo ancora che tali programmi usino variabili diverse e sia µ il
massimo indice di variabile usato. Il seguente programma implementa la
funzione f:
read(xµ+1 , xµ+2 );
xg := xµ+1 ; Cg ; xµ+3 := yg ; xµ+4 := nil;
while xµ+2 do
h
3
xh
1 := xµ+1 ; x2 := xµ+4 ; xh := xµ+3 ;
Ch ;
xµ+2 := tl(xµ+2 ); xµ+4 := cons(nil, xµ+4 );
xµ+3 := yh ;
endw;
write(xµ+3 )
(3) Sia infine ϕ definita per minimizzazione a partire da una funzione f :
Nn+1 −→ N, calcolata da:
read(xf1 , . . . , xfn , xfn+1 ); Cf ; write(yf ).
138
16. CALCOLABILITÀ E LINGUAGGI DI PROGRAMMAZIONE
e siano x1 , . . . , xn , y, temp variabili non usate in esso. Il seguente programma calcola la funzione ϕ:
read(x1 , . . . , xn );
y := nil;
x1f := x1 ; . . . ; xfn := xn ; xfn+1 := y;
Cf ;
while yf do
y := cons(nil, y);
x1f := x1 ; . . . ; xfn := xn ; xfn+1 := y;
Cf ;
endw;
write(y)
È possibile generalizzare quanto visto per il linguaggio While ad un arbitrario linguaggio di programmazione. Sia L un linguaggio di programmazione che
definisce funzioni su un insieme di dati D. Nel seguito con L indicheremo la classe
dei programmi sintatticamente corretti scritti nel linguaggio L. Supponiamo che
esista una codifica univoca dei numeri naturali in D, ovvero per ogni n: n ∈ D e
per ogni n 6= m: n 6= m. Sia [[·]]L la semantica del linguaggio L definita in modo
formale:1
[[·]]L : L → (D → D ∪ {↑})
Una funzione f : Nk → N è L-calcolabile se esiste un programma P ∈ L tale che
per ogni x1 , . . . , xk ∈ N:
[[P]](x1 , . . . , xk ) = f(x1 , . . . , xk )
Definizione 16.13. Un linguaggio di programmazione L è detto Turingcompleto se l’insieme delle funzioni L-calcolabili coincide con la classe delle funzioni
Turing-calcolabili.
Il linguaggio While è dunque Turing-completo per il Teorema 16.12. In particolare, ogni linguaggio di programmazione sufficientemente espressivo per codificare i numeri naturali e comprendente i comandi di base di assegnamento, composizione ed iterazione condizionata tipo While è Turing-completo. Infatti, per
dimostrare la Turing-completezza di un linguaggio è sufficiente dimostrare che
questo è in grado di simulare i costrutti di base di While.
1 È
noto che ad ogni linguaggio di programmazione, anche complesso e di alto livello, è
possibile associare una semantica formale [31, 21].
6. FOR-CALCOLABILITÀ E FUNZIONI PRIMITIVE RICORSIVE
139
Esercizio 16.14. Una Macchina di Turing si può rappresentare mediante un
insieme (lista) di quintuple (lista di 5 elementi). Si scriva un programma While in
grado di simulare il comportamento (interpretare) una data macchina di Turing
(questa è una dimostrazione alternativa che ogni funzione Turing-calcolabile è
While-calcolabile).
6. For-calcolabilità e funzioni primitive ricorsive
In questa sezione presenteremo una variante al linguaggio While. Sostituiremo al comando while due comandi, uno di selezione ed uno iterativo molto usati in programmazione: l’if-then-else ed il ciclo for. Nell’esercizio 16.5 tali costrutti
sono stati definiti e la loro simulazione utilizzando il while è stata richiesta come
(semplice) esercizio. Mostreremo ora come un linguaggio che disponga di questi
due costrutti più l’assegnamento permette una espressività pari a quella del formalismo delle funzioni primitive ricorsive.
Nelle stesse ipotesi generali dei programmi While, definiamo un programma
For mediante una grammatica CF nel modo seguente dove x, y ∈ Var, e d ∈ DA
(in particolare, d può essere nil):
Exp
→ x | d | cons(Exp1 , Exp2 ) | hd(Exp) | tl(Exp) | Exp1 = Exp2
Com → x := Exp | Com1 ; Com2 | skip |
if Exp then C1 else C2 | for x := Exp do C endfor
Prog
→ read(x1 , . . . , xn ); Com; write(y1 , . . . , ym )
La semantica intuitiva dei due costrutti è definita nell’esercizio 16.5. Inoltre,
come mostrato nell’esercizio 16.7, il costrutto if-then-else è simulabile dal for e
dunque superfluo (non ne parleremo nelle dimostrazioni). Una funzione f : Nk → N
è For-calcolabile se se esiste un programma For P tale che per ogni x1 , . . . , xk ∈ N:
[[P]]L (x1 , . . . , xk ) = f(x1 , . . . , xk )
Nel teorema seguente si forniranno delle definizioni più tecniche e precise, ma la
sua comprensione e definizione ne può fare a meno.
Teorema 16.15. f : Nm −→ N è For-calcolabile se e solo se f è primitiva
ricorsiva.
Traccia. Per mostrare che una funzione primitiva ricorsiva è For-calcolabile
è sufficiente ripetere la seconda parte della dimostrazione del Teorema 16.12 fino
al passo induttivo 1. Bisogna mostrare anche il passo induttivo 2 con il nuovo
linguaggio. Supponiamo che alla funzione h : N3 −→ N corrisponda il programma:
read(x1h , x2h , x3h ); Ch ; write(yh ). Il seguente programma implementa la funzione f
140
16. CALCOLABILITÀ E LINGUAGGI DI PROGRAMMAZIONE
definita per ricorsione primitiva:
read(n, x);
xg := x;
Cg ;
temp := yg ;
for w := n do
x1h := temp; x2h := w; x3h := x;
Ch ;
temp := yh ;
endfor;
write(temp)
Leggermente più tecnico è il viceversa. Dobbiamo mostrare che ogni funzione
calcolata da un programma For è calcolabile con una funzione primitiva ricorsiva.
Innanzitutto partiamo dalle seguenti assunzioni:
• Assumiamo che ogni espressione usata nel programma sia tale che per
ogni istanziazione delle variabili in essa in numerali si ottiene un numerale. Questa assunzione è eliminabile grazie all’isomorfismo tra DA e
N, ma snellisce la dimostrazione.
• Con le stesse motivazioni, assumiamo che ogni variabile non presente tra
quelle di input sia immediatamente inizializzata a nil (0).
• Assumiamo che un programma usi tutte e sole le variabili v1 , . . . , vp e
che le variabili x1 , . . . , xn di input siano le prime n variabili v1 , . . . , vp .
In generale si avrà che m ≤ p e n ≤ p (n è il numero di variabili di
output).
• Ogni comando C è visto come un insieme di p funzioni da Np −→ N.
• Le m funzioni calcolate da un programma P
read(v1 , . . . , vn ); C; write(vi1 , . . . , vim )
saranno dunque le funzioni fPi1 , . . . , fPim definite nel seguente modo: se
fC
i1 , . . . , fim sono le funzioni calcolate da C sulle variabili di output, allora,
per j = 1, . . . , m
fPij (x1 , . . . , xn ) = fC
, . . . , xn , 0, . . . , 0)
ij (x
{z
}
|1
p
ovvero con le variabili del programma non presenti tra gli input inizializzate a 0. Ovviamente, se le fC sono primitive ricorsive, lo saranno anche
le fP .
7. INTERPRETI E METAPROGRAMMAZIONE
141
• Per brevità, con x̄ si denota la lista x1 , . . . , xp .
Ci si concentra dunque sulle funzioni calcolate dai comandi.
Base:
skip: Per ogni i = 1, . . . , p si avrà che fC
i (x̄) = xi ovvero una proiezione.
vj := E: Per ogni i = 1, . . . , p, i 6= j si avrà che fC
i (x̄) = xi ovvero una
proiezione. Per quanto riguarda fC
(x̄),
per
l’assuzione
sopra, o E è un
j
numerale ℓ ∈ N e dunque fC
(x̄)
=
ℓ
(ovviamente
p.r.)
oppure
E è del tipo
i
cons(nil, · · · , cons(nil, vk ) · · · ) ovvero fC
(x̄)
=
x
+
ℓ
per
qualche
ℓ intero.
k
i
Anche in questo caso la funzione è p.r.
Passo:
1
2
C1 ; C2 : Siano, per ipotesi induttiva, per i = 1, . . . , p le funzioni fC
e fC
i
i .
Allora le funzioni associate alla composizione sono, per i = 1, . . . , p:
C1
C1
2
fC
i (f1 (x̄), . . . , fp (x̄))
for vj := E do C; endfor: Siano, per ipotesi induttiva e per i = 1, . . . , p, le
E
funzioni fC
i (x̄) primitive ricorsive equivalenti al comando C e fi quelle
associate al comando vj := E. Definisco allora per i = 1, . . . , p, le funzioni
hji (l’apice j ricorda che stiamo parlando della variabile vj ) nel seguente
modo:
hji (x̄, 0) = xi
i 6= j
hjj (x̄, 0) = 0
j
j
hji (x̄, n + 1) = fC
i (h1 (x̄, n), . . . , hp (x̄, n)) i 6= j
hj (x̄, n + 1) = hj (x̄, n) + 1
j
j
La funzione da associare al comando for, per ogni i, sarà:
gi (x̄) = hji (x̄, fE
j (x̄))
7. Interpreti e Metaprogrammazione
In questa sezione introduciamo il concetto di interprete e di metaprogrammazione. Per metaprogrammazione si intende la costruzione di programmi che
manipolano programmi. Questa possibilità, sancita dalla universalità dei sistemi
di calcolo Turing-completi, risiede nel fatto che i programmi possono essere codificati in modo univoco nei dati manipolati dai programmi stessi. La MdT universale
U è il prototipo di metaprogramma: essa prende in input l’indice x di una data
MdT M ed un dato y, e restituisce in output il risultato ottenibile attivando la
macchina Mx sul dato y:
ϕ (y) se ϕ (y) ↓
x
x
U(x, y) =
↑
altrimenti
142
16. CALCOLABILITÀ E LINGUAGGI DI PROGRAMMAZIONE
Traslando questo ragionamento sui linguaggi di programmazione, si ottiene il
concetto di interprete. Siano L ed S due linguaggi di programmazione Turingcompleti; assumiamo che operino sul medesimo insieme di dati D.
Definizione 16.16. Un programma int ∈ L tale che, per ogni S-programma
P e per ogni dato d ∈ D:
[[int]]L (P, d) = [[P]]S (d)
è un interprete in L di S-programmi (o semplicemente di S).
Ovviamente, se [[P]]S (d) ↑, allora anche [[int]]L (P, d) ↑.
L’esistenza di un interprete è assicurata dalla Turing-completezza dei linguaggi
in oggetto. In altri termini in L si deve simulare l’esecuzione del S-programma
P sull’input d istruzione per istruzione. Per far ciò ci si baserà sulla semantica
(formalmente definita) del programma P nel linguaggio S. Un interprete in L per
L programmi è detto metainterprete del linguaggio L.
Vedremo nel dettaglio come realizzare un metainterprete del linguaggio While.
Per altri linguaggi Turing-completi, il lavoro da fare sarà analogo. Innanzitutto
è necessario rappresentare in DA i programmi While. A tal fine definiamo un
insieme finito di atomi:
A = {‘‘ := ′′ , ‘‘var ′′ , ‘‘while ′′ , ‘‘cons ′′ , ‘‘tl ′′ , ‘‘hd ′′ , ‘‘nil ′′ , ‘‘; ′′ , ‘‘quote ′′ }
In DA possiamo codificare i numeri naturali, mediante sintassi concreta:
n = (nil.(nil. · · · (nil. nil) · · · ))
{z
}
|
n
Assumendo che l’insieme delle variabili Var = {v0 , v1 , v2 , . . . }, useremo la seguente
codifica per codificare un insieme infinito di variabili: la variabile vi è codificata da
(var.i). Useremo poi una parola chiave per ogni costrutto, espressione o comando,
previsto nella sintassi di While. Useremo quote per dire che l’espressione (un
albero d privo di variabili) non necessita di essere ulteriormente codificata. La
seguente funzione : While −→ DA di traduzione da programmi While ad alberi
DA , è definita induttivamente sulla sintassi in tabella 3.
Se vi sono più variabili di ingresso/uscita si userà una lista del tipo: lista
((var.1).((var.2). · · · (var.n)) · · · ). Se P è un programma While, allora P ∈ DA
è detta sintassi concreta di P. Ad esempio, la sintassi concreta del programma
7. INTERPRETI E METAPROGRAMMAZIONE
read(vi ); C; write(vj ) =
C1 ; C2
=
while E do C endw =
vi := E =
vi
=
d =
cons(E1 , E2 ) =
143
((var.i).(C.(var.j)))
(; .(C1 .C2 ))
(while(E.C))
(:= .((var.i).E))
(var.i)
(quote.d)
(cons.(E1 .E2 ))
hd(E)
=
(hd.E)
tl(E)
=
(tl.E)
E1 = E2
=
(= .(E1 .E2 ))
Table 3.
nell’Esempio 16.3, assumendo x come v1 e y come v2 è data dal seguente albero:
(
(var.1).
( (; .
( (:= .((var.2).(quote nil))).
( (while.((var.1).
(; . ( (:= .((var.2).(cons (hd (var.1)(var.2)))).
(:= .((var.1).(tl (var.1))))
).
(var.2)
))
Teorema 16.17. Esiste un interprete in While per While.
Proof. Scrivere l’interprete per esercizio, utilizzando i costrutti di While,
eventualmente estesi con costrutti condizionali tipo case. Suggerimento: utilizzare
le strutture dati ad albero per rappresentare la sintassi concreta dei programmi ed
una pila implementata con una lista per la valutazione delle espressioni.
La possibilità offerta da un linguaggio Turing-completo della metaprogrammazione è alla base della esistenza di problemi algoritmicamente non risolvibili. I
limiti di ciò che è calcolabile nascono quindi dalle potenzialità del sistema di calcolo
stesso. Cosı̀ come l’indecidibilità della terminazione è dimostrata utilizzando funzioni universali, daremo una dimostrazione della indecidibilità della terminazione
utilizzando il linguaggio While, senza ricorrere all’aritmetizzazione dello stesso.
144
16. CALCOLABILITÀ E LINGUAGGI DI PROGRAMMAZIONE
def
Supponiamo esista un programma halt = read(x1 , x2 ); C; write(y) ∈ While,
tale che:
(nil.nil) [[P]]W (d) ↓
[[halt]]W (P, d) =
nil
[[P]]W (d) ↑
Definiamo il programma R ∈ While nel modo seguente:
read(x);
x1 := x; x2 := x;
C;
while y do y := y endw;
write(y)
Sia dunque R la rappresentazione in D del programma R.
• Se [[R]]W (R) ↓, allora l’output di C, che termina sempre per ipotesi, è
y = (nil.nil). Ma allora il programma R entra in loop nel ciclo while.
Questo implica che [[R]]W (R) ↑.
• Se [[R]]W (R) ↑ allora l’output di C è nil. Ma allora il programma R non entra nel ciclo while e dunque termina restituendo nil. Dunque [[R]]W (R) ↓.
Entrambi i casi portano ad un assurdo.
8. Specializzatori e Proiezioni di Futamura
Dalla Turing-completezza dei linguaggi di programmazione derivano altri strumenti importanti per la moderna programmazione di sistemi complessi. Abbiamo
già visto che la Turing-completezza assicura l’esistenza di programmi universali,
ovvero di interpreti. Vedremo ora che essa permette di implementare programmi
che operano come specializzatori di programmi. L’esistenza di questi programmi
segue dal Teorema s-m-n, la cui dimostrazione segue a sua volta dalla possibilità
di implementare programmi che manipolano programmi.
Supponiamo che L, S e T siano linguaggi di programmazione Turing-completi
operanti sul medesimo insieme di dati D.
Definizione 16.18. Un programma spec scritto in un linguaggio di implementazione L è uno specializzatore spec ∈ L se per ogni programma P scritto
in un linguaggio di programmazione S e per ogni porzione di suo input s ∈ D,
restituisce un T -programma: [[spec]]L (P, s) tale che per ogni dato d ∈ D:
[[[[spec]]L (P, s)]]T (d) = [[P]]S (s, d).
Dunque uno specializzatore non interpreta un programma ma ne restituisce
uno nuovo sulla base di uno esistente (in un dato linguaggio) e di una parte del suo
input. Il suo compito è simile a quello della funzione ricorsiva totale del Teorema
8. SPECIALIZZATORI E PROIEZIONI DI FUTAMURA
145
s-m-n. Per dimostrare l’esistenza di specializzatori, dimostriamo il Teorema s-m-n
nel caso dei programmi While.
Teorema 16.19 (Teorema s-m-n). Esiste un programma While spec tale che
per ogni programma While P e per ogni porzione del suo input s ∈ D, [[spec]](P, s)
è un programma While e per ogni d ∈ D vale che:
[[[[spec]](P, s)]](d) = [[P]](s, d)
Proof. Vediamo cosa dovrebbe fare il programma spec. Dato P:
read(x1 , x2 ); C; write(y)
e s ∈ D deve restituire il programma:
read(x2 ); x1 := s; C; write(y).
E’ immediato scrivere un programma che fa ciò in While (ma anche in ogni altro
linguaggio di programmazione).
Dunque esiste uno specializzatore all’interno della classe dei programmi While.
Cerchiamo di generalizzare la cosa a tre linguaggi generici S, T , e L. Per la
definizione e l’esistenza di un interprete in T per L, so che:
[[P]]S (s, d) = [[int]]T (P, s, d)
Per il Teorema s-m-n (dimostrato in T ), vedendo P ed s come input per int so
che esiste uno specializzatore spec tale che:
[[int]]T (P, s, d) = [[[[int]]T (int, P, s)]]T
Ora, poiché int è fissato a priori, lo posso anche togliere dai parametri di input.
Inoltre tale programma di conversione (che nella dimostrazione sopra avevamo
scritto in While) lo si può sicuramente scrivere con un altro linguaggio Turing
completo (quello con cui preferiamo scrivere gli algoritmi—p.es., il C o il Prolog).
Dalla combinazione di interpreti int e specializzatori spec è possibile costruire
una famiglia di programmi assai complessi e utili, quali i compilatori e programmi
che generano compilatori. Per ottenere questi strumenti a supporto della programmazione, utilizziamo le proiezioni di Futamura [15]. Esse permettono di costruire
semplici compilatori e programmi che generano compilatori a partire dai concetti
e dall’esistenza di interpreti e specializzatori. Chiaramente, più è sofisticato lo
specializzatore, es. programmi per la ottimizzazione del codice, più è possibile
costruire in questo modo strumenti di compilazione sofisticati ed utili. Nei tre
risultati sotto riportati assumiamo che S, T , L, I siano linguaggi di programmazione Turing-completi operanti sul medesimo insieme di dati D.
Teorema 16.20 (I proiezione di Futamura). Dato un programma source ∈ S
possiamo generare un programma equivalente target ∈ T .
146
16. CALCOLABILITÀ E LINGUAGGI DI PROGRAMMAZIONE
Proof. Dimostreremo che tale programma è:
def
target = [[spec]]I (int, source) ∈ T
Sia spec uno specializzatore, scritto in I di L programmi in T programmi.
Allora:
[[source]]S (d)
=
[[int]]L (source, d)
=
[[[[spec]]I (int, source)]]T (d)
=
T
Per Def. di Interprete in L
Per Def. di Specializzatore
Per Def. di target
[[target]] (d)
Dunque, dato uno specializzatore spec, assegnandogli come input l’interprete
di S programmi scritto in L e il programma source, restituisce il T programma
desiderato.
Teorema 16.21 (II proiezione di Futamura). Possiamo generare un compilatore da S a T scritto in T .
def
Proof. Dimostreremo che: comp = [[spec]]L (spec, int).
Sia target ∈ T come stabilito dalla I proiezione. Supponiamo che I = L (uso
lo stesso linguaggio per implementare). Allora:
target =
[[spec]]L (int, source)
=
[[[[spec]]L (spec, int)]]T (source)
=
[[comp]]T (source)
Per definizione di specializzatore
Dunque, per generare un compilatore, basta prendere uno specializzatore e
mettere come input il suo codice e quello di un interprete.
Il terzo risultato di Futamura riguarda i generatori di compilatori:
Teorema 16.22 (III proiezione di Futamura). Possiamo generare un generatore di compilatori scritto in T .
Proof. Dimostreremo che il generatore di compilatori è:
def
compgen = [[spec]]L (spec, spec)
Anche in questo caso assumiamo che I = L. Dalla definizione di specializzatore e dalla II proiezione segue che:
comp
=
[[spec]]L (spec, int)
=
[[[[spec]]L (spec, spec)]]T (int)
=
[[compgen]]T (int)
8. SPECIALIZZATORI E PROIEZIONI DI FUTAMURA
147
Dunque compgen accetta come input un interprete e genera un compilatore.
Esercizio 16.23. Dimostrare che compgen = [[compgen]](spec).
Part 3
Teoria matematica della ricorsione
CHAPTER 17
Insiemi ricorsivi e ricorsivamente enumerabili
Abbiamo visto nel Cap. 10 diverse formalizzazioni per definire ciò che noi riteniamo effettivamente calcolabile. Queste comprendono le MdT, le funzioni parziali
ricorsive di Kleene & Robinson, ed i linguaggi di programmazione imperativa, tipo
While. In questo capitolo studieremo alcuni risultati classici della teoria della ricorsività, indipendentemente dal sistema formale adottato per esprimere algoritmi,
o funzioni calcolabili. L’equivalenza tra le diverse definizioni di calcolabilità e la
Tesi di Church-Turing ci permettono di studiare l’effettiva calcolabilità in un modo
per cosı̀ dire “più astratto”. In particolare la Tesi di Church-Turing ci permette di
astrarre dalla particolare MdT o programmi while, e trattare in modo formale il
concetto (informale) di funzione effettivamente calcolabile, indipendentemente dal
formalismo adottato per rappresentarla. Nel seguito assumeremo di avere fissato
una data enumerazione {ϕi }i∈N delle MdT o delle funzioni parziali ricorsive o dei
programmi While. Il materiale contenuto in questa parte è tratto in larga parte
dai testi di riferimento [27, 2, 19, 15, 22].
In questo capitolo studieremo le proprietà ricorsive di insiemi di numeri naturali. La scelta di studiare insiemi di numeri naturali non è affatto limitativa per
gli scopi dell’informatica. Tutti i dati manipolabili da un algoritmo sono infatti
numerabili e possono quindi essere messi in corrispondenza biunivoca con l’insieme
N. Un analogo ragionamento si può applicare ad insiemi di stringhe su di un alfabeto, ovvero ad un linguaggio, o ad insiemi di alberi sintattici, rappresentanti
While-programs. È infatti noto l’isomorfismo tra N e rispettivamente Σ∗ e DA ,
essendo Σ un dato alfabeto e A un dato insieme di atomi. Da questo consegue
che studiare proprietà degli elementi di ℘(N) equivale a studiare le proprietà degli
elementi di ℘(Σ∗ ) e ℘(DA ), e che ogni definizione/proprietà che vedremo per insiemi di naturali, si applica senza restrizione alcuna a linguaggi (ovvero elementi di
℘(Σ∗ )) e a insiemi di alberi sintattici. In questo senso, il passaggio da funzioni ad
insiemi è naturale e rappresenta una astrazione successiva verso una più astratta
definizione di problema ricorsivamente risolvibile. Questo ci permetterà di studiare, analogamente a quanto visto per le funzioni calcolabili, insiemi che risultano
essere effettivamente costruibili, ovvero i cui elementi possono essere determinati
in modo algoritmico.
151
152
17. INSIEMI RICORSIVI E RICORSIVAMENTE ENUMERABILI
Consideriamo un insieme I ⊆ N. Ci chiediamo se I è costruibile in modo
algoritmico, ovvero se è possibile generare gli elementi di I mediante una funzione
che sia effettivamente calcolabile.
Definizione 17.1. Un insieme I ⊆ N è detto ricorsivamente enumerabile
(r.e.) se esiste una funzione parziale ricorsiva ψ tale che I = dom(ψ).
Nel seguito indicheremo con RE ⊆ ℘(N) la classe degli insiemi ricorsivamente
enumerabili. Pertanto A ∈ RE sse A è r.e. Inoltre, nel seguito indicheremo con Wx
l’insieme r.e. associato alla funzione ϕx secondo una opportuna Gödelizzazione,
ovvero Wx = dom(ϕx ) essendo ϕx l’x-esima MdT nella numerazione fissata.
Sia A ∈ RE. La funzione parziale:
1 se x ∈ A
ψA (x) =
↑ se x 6∈ A
è detta funzione semicaratteristica dell’insieme A. È chiaro che A = dom(ψA ).
Inoltre, essendo per ipotesi A r.e. , la verifica della condizione x ∈ A è semidecidibile, ovvero se y è tale che A = Wy , x ∈ A sse ϕy (x) ↓. Sia dunque
def
Py = read x; C; write z un programma While equivalente a ϕy . È naturale
definire il seguente programma: read(x); C; z := 1; write(z). Questo programma
corrisponde ad una MdT, ed implementa la funzione ψA . Pertanto abbiamo dimostrato che un insieme è detto r.e. sse ha una funzione semicaratteristica parziale
ricorsiva.
All’interno della famiglia degli insiemi r.e. , esistono insiemi con caratteristiche
più forti. Per questi insiemi vale la condizione aggiuntiva che la loro funzione caratteristica (che è sempre una funzione totale) è ricorsiva, ovvero esiste un algoritmo
in grado di decidere l’appartenenza o meno di un elemento all’insieme.
Definizione 17.2. Un insieme A è detto ricorsivo se esiste una funzione
ricorsiva (totale) fA tale che:
1 se x ∈ A
fA (x) =
0 se x 6∈ A
Esempio 17.3. I seguenti sono insiemi ricorsivi:
• L’insieme {0, 2, 4, 6, . . .} dei numeri pari;
• N e ∅;
• Ogni insieme finito (Dimostrare per esercizio);
• Ogni insieme A tale che Ā è finito (Dimostrare per esercizio).
Esisteranno dunque ℵ0 insiemi r.e. di cui una parte di uguale cardinalità sarà
ricorsiva. Si nota infatti subito della definizioni che
A ricorsivo ⇒ A ∈ RE
17. INSIEMI RICORSIVI E RICORSIVAMENTE ENUMERABILI
153
Dato A insieme ricorsivo, basta infatti definire la funzione parziale e chiaramente
ricorsiva:
1 se f (x) = 1
A
ϕ(x) =
↑ altrimenti
Esercizio 17.4. Dimostrare che i seguenti insiemi sono ricorsivi:
• x ∈ N : ∃n∃p primo . x = pn ;
• hx, yi ∈ N × N : x4 + y6 = 1238 ;
• x ∈ N : ∃n. x = 2n + 1 .
Teorema 17.5 (Teorema di Post). Un insieme A è ricorsivo se solo se A e
Ā sono r.e.
Proof. Sia A ⊆ N e Ā = N \ A.
(⇒) Sia A ricorsivo e fA la sua funzione caratteristica (totale) ricorsiva. Definiamo:
1 se f (x) = 1
A
ψ(x) =
↑ altrimenti
È chiaramente una funzione parziale ricorsiva. Ad esempio il seguente frammento
def
di programma implementa ψ: Sia FA = read x; CA ; write y.
G:
read x;
CA ;
while y = nil do y := y endw;
write y
A ha quindi una funzione semicaratteristica parziale ricorsiva. Analogo discorso
si applica a Ā, essendo fĀ = 1 − fA la corrispondente funzione caratteristica.
Abbiamo pertanto dimostrato che A e Ā sono r.e.
(←) Supponiamo A e Ā r.e. , con funzioni semicaratteristiche ψA e ψĀ . Definiamo:
1 se ψ (x) ↓
A
f(x) =
0 se ψ (x) ↓
Ā
È banale osservare che, dato x ∈ N: o x ∈ A o x ∈ Ā, allora eseguendo a turno
un’istruzione della MdT che calcola ψA e un’istruzione della MdT che calcola ψĀ ,
prima o poi una delle due MdT termina, rendendo la funzione f totale ricorsiva.
Si osservi come la ricorsività di N segua anche dal fatto che sia N̄ = ∅ che N
sono r.e.
154
17. INSIEMI RICORSIVI E RICORSIVAMENTE ENUMERABILI
Passi
ϕx (0) ϕx (1) ϕx (2) ϕx (3)
...
◦
◦??
◦??
◦??
◦ ◦??
◦??
??
◦
◦
◦
ϕx (2) ↓
◦
•??
Argomenti
..
.
Figure 1. Dovetail: ϕx (2) converge dopo 2 passi
Il seguente teorema caratterizza gli insiemi r.e. come la famiglia degli insiemi
che possono essere effettivamente enumerati da un algoritmo, ovvero i cui elementi possono essere “listati” in modo effettivo come “output” di un programma.
Gli insiemi r.e. possono dunque essere visti come liste, eventualmente infinite, di
elementi generabili da un algoritmo.
Il teorema di caratterizzazione fa uso di una metodologia effettiva per esplorare
il campo di convergenza di una funzione parziale, ovvero l’insieme degli elementi su
cui una funzione parziale converge (è definita). Questa metodologia per esplorare il
campo di convergenza di una funzione parziale è detta a coda di colomba (dovetail)
e sarà spesso utilizzata nel seguito. In essa ha un ruolo essenziale il concetto di
passo di calcolo o derivazione. Come abbiamo visto nel Cap. 10 ogni sistema
formale introdotto definisce il concetto di calcolo come sequenza discreta di passi
elementari. Pertanto, il procedimento a dovetail è perfettamente accettabile come
metodo algoritmico per esplorare il campo di definizione (o convergenza) di una
funzione parziale ricorsiva.
Sia dunque ϕx una funzione parziale ricorsiva. Applicare la tecnica dovetail a
ϕx significa costruire l’insieme L in modo induttivo, tale che:
Passo 0: Si faccia un passo di calcolo per ϕx (0), e se termina allora L :=
{ϕx (0)};
Passo n + 1: Si faccia un passo nel calcolo di tutte le funzioni ϕx (0), . . . , ϕx (n)
ove il calcolo non sia già terminato al passo precedente, e per ogni m tale
che ϕx (m) converge, si pone L := L ∪ {ϕx (m)}.
In pratica si esplora il grafico della funzione seguendo un andamento a zig-zag,
come evidenziato nella figura 1.
17. INSIEMI RICORSIVI E RICORSIVAMENTE ENUMERABILI
155
L’insieme L è stato dunque effettivamente costruito ed i suoi valori corrispondono al codominio definito di ϕx . Siamo ora in grado di dimostrare il seguente
teorema di Kleene di caratterizzazione degli insiemi r.e.
Teorema 17.6 (Caratterizzazione degli insiemi r.e. ). Le seguenti affermazioni
sono equivalenti:
(1) A ∈ RE;
(2) esiste ϕ ∈ PR: A = range(ϕ);
(3) A = ∅ oppure esiste una funzione f ∈ R tale che A = range(f).
Proof. Consideriamo una data enumerazione delle MdT (o delle funzioni
parziali ricorsive) {ϕx }x∈N .
(1 ⇒ 2) Sia A ∈ RE. Per definizione, esisterà ϕx parziale ricorsiva t.c. A =
dom(ϕx ). Definiamo:
y se ϕ (y) ↓
x
δ(y) =
↑ altrimenti
δ è parziale ricorsiva e range(δ) = dom(ϕx ) = A.
(2 ⇒ 3) Sia A = range(ϕx ) e A 6= ∅. Applicando il dovetail alla funzione:
ϕ (y) se ϕ (y) ↓
x
x
ψ(y) =
↑
altrimenti
poiché A = range(ϕx ) 6= ∅, esiste n0 ∈ N tale che all’n0 -esimo stadio del dovetail
si osserva una terminazione. Definiamo la funzione f induttivamente sullo stadio
n-esimo del dovetail, in modo tale che f abbia valori tutti e soli nella lista L
generata dal dovetail di ψ.
• Se ψ converge in {m0 , . . . , mj0 } con 0 ≤ j0 ≤ n0 allora per ogni i ∈ [0, j0 ]:
f(i) = ψ(mi );
• Supponiamo che per definire f(jp ) si sia usato lo stadio np -esimo del
dovetail. Possiamo avere i seguenti casi:
– Se nell’np + 1-esimo passo del dovetail non si rivelano nuove terminazioni, allora f(jp + 1) = f(jp ).
– Se nell’np +1-esimo passo del dovetail ψ converge con input {p0 , . . . , pk }
con 0 ≤ k ≤ np + 1, allora per ogni i ∈ [0, k]: f(jp + 1 + i) = ψ(pi ).
• Iteriamo il precedente punto ponendo jp+1 = jp + 1 + k.
La procedura che abbiamo descritto è effettiva e definisce una funzione ricorsiva
totale f tale che A = range(f).
(3 ⇒ 1). Se A = ∅ allora A = dom(λx. ↑). Sappiamo che λx. ↑∈ PR, e
quindi A è r.e. Se A = range(f) con f ∈ R, basta porre:
ϕ(x) = µz.(f(z) − x = 0)
ϕ ∈ PR ed inoltre è immediato osservare che ϕ(x) ↓ ⇔ ∃z. f(z) = x, quindi
A = dom(ϕ) che dimostra che A è r.e.
156
17. INSIEMI RICORSIVI E RICORSIVAMENTE ENUMERABILI
Nota 17.7. Nel testo di Rogers [27] la definizione di insieme r.e. viene data
con la caratterizzazione (3) del Teorema appena visto. Tale caratterizzazione
spiega anche il nome “ricorsivamente enumerabile” ovvero enumerabile mediante
l’impiego di una funzione ricorsiva. Nei testi più recenti (p. es., [15, 6]) si adotta
invece la definizione 17.1. Il Teorema sancisce che le due sono del tutto equivalenti.
Esempio 17.8. N è r.e. , poiché λx. x ∈ R e N = range(λx. x).
Corollario 17.9. Esiste una funzione totale ricorsiva f tale che Wx =
range(ϕf(x) ).
È chiaro che non è detto che ϕf(x) sia totale, pur essendolo f.
Teorema 17.10. Le seguenti affermazioni sono equivalenti:
(1) A è ricorsivo;
(2) A = ∅ oppure A = range(f) con f ∈ R non decrescente.
Proof. (⇒). Sia A 6= ∅ e A ricorsivo. Poiché A ⊆ N, esiste a = min(A)
minimo valore tra quelli contenuti in A. Definiamo:
f(0)
f(n + 1)
= a
n + 1 se n + 1 ∈ A
=
f(n)
altrimenti
È chiaro che f è totale ricorsiva non decrescente e che A = range(f).
(←). Se A è finito, allora A è banalmente ricorsivo. Supponiamo che A
sia infinito e A = range(f) con f totale ricorsiva non decrescente. Sia x ∈ A e
zx = µy. (f(y) > x). È chiaro che un tale zx esiste (perchè?) e che
x ∈ A ⇔ x ∈ {f(0), . . . , f(z)}
È quindi decidibile l’appartenenza di x ad A, ovvero A è ricorsivo (definire la
funzione caratteristica di A).
Teorema 17.11. Ogni insieme r.e. infinito ha un sottoinsieme ricorsivo infinito.
Proof. Sia A ⊆ N tale che A = range(f) con f funzione ricorsiva totale e
|A| = ω. Definiamo la funzione g tale che:
g(0)
g(n + 1)
= f(0)
= f(µy. f(y) > g(n)).
g(n + 1) è dunque il più piccolo elemento di A più grande di g(n). g è crescente,
quindi range(g) è ricorsivo per il Teorema 17.10. Inoltre range(g) ⊆ A, poiché
per definizione di g: range(g) ⊆ range(f) = A.
Esercizio 17.12. Si dica se i seguenti insiemi sono ricorsivi o r.e. :
17. INSIEMI RICORSIVI E RICORSIVAMENTE ENUMERABILI
157
(1) A = range(f) con
= n0
f(0)
f(n + 1)
= 2f(n).
(2) B = range(g) con
g(0)
=
n0
g(n + 1)
=
h(n)g(n).
essendo h una generica funzione ricorsiva (totale).
(3) Data una funzione ricorsiva f, dimostrare che
l’immagine inversa di f e
Wx , con x ∈ N, ovvero l’insieme f−1 (Wx ) = y : f(y) ∈ Wx , è r.e.
(4) Sia D = range(ψ) con:
ψ(0) =
ψ(n + 1)
=
n0
ϕn (n)ψ(n).
D è ricorsivo?
Fino a questo momento abbiamo visto insiemi ricorsivi e r.e. Tuttavia non abbiamo ancora visto un esempio di un insieme che sia r.e. ma non ricorsivo. Questo
insieme (se esiste) rappresenta l’insieme che discrimina le due classi di insiemi che
abbiamo definito. È chiaro che un insieme di questo tipo deve essere r.e. ma non
ricorsivo, ovvero l’appartenenza ad esso deve essere un predicato semidecidibile.
È naturale chiedersi se questo insieme possa essere in qualche modo correlato alla
non decidibilità della terminazione. Al solito, fissiamo una numerazione delle MdT
o delle funzioni parziali ricorsive {ϕx }x∈N .
Definizione 17.13. Definiamo K = x : ϕx (x) ↓ .
È chiaro per la definizione sopra che K = x : x ∈ Wx 1 .
Teorema 17.14. K è r.e. ma non ricorsivo.
1 Questo insieme ricorda l’insieme utilizzato per contraddire la teoria classica degli insiemi,
ovvero il Paradosso di Russell [28]. Se infatti fosse possibile definire un insieme del tipo:
M = x : x 6∈ x allora otterremmo immediatamente un paradosso poiché M ∈ M sse M 6∈ M.
L’idea utilizzata per risolvere questo paradosso nella teoria degli insiemi è quella di non ammettere M come insieme, ovvero di definire una “disciplina” per la costruzione di insiemi che vieti
una costruzione tipo M. Si osservi come la teoria della ricorsione è immune da questo tipo di
paradossi. Infatti K̄ = x : x 6∈ Wx , l’insieme dei numeri non appartenenti agli insiemi r.e.
da essi generati, non può essere r.e. , altrimenti, essendolo K per il Teorema 17.14, K sarebbe
ricorsivo (Teorema di Post) il che per il Teorema 17.14 è assurdo. Quindi gli insiemi r.e. non
includono K̄, ovvero non esiste y0 tale che K̄ = Wy 0
158
17. INSIEMI RICORSIVI E RICORSIVAMENTE ENUMERABILI
✬
✬
℘(N)
·
✬
r.e.
K
·
✬
Ricorsivi
✫
✩
✩
N
✩
∅
✪
✪
✪
✪
·
Finiti
✫
✫
✫
K̄
✩
·
Figure 2. La gerarchia degli insiemi Ricorsivi
Proof. definiamo la funzione ψK tale che:
1 se ϕ (x) ↓
x
ψK =
↑ altrimenti
ψK è parziale ricorsiva (perchè?) e semicaratteristica di K, quindi K è r.e.
Supponiamo K ricorsivo. Allora K̄ è r.e. Sia y0 tale che K̄ = Wy0 . Segue che:
che è chiaramente assurdo.
y0 ∈ Wy0 = K̄ ⇔ y0 ∈ K
Esercizio 17.15.
(1) Dimostrare che K2 = hx, yi : x ∈ Wy è r.e. ma non ricorsivo;
(2) Dimostrare che le seguenti proprietà caratterizzano gli insiemi finiti;
• tutti i sottoinsiemi sono r.e.
• tutti i sottoinsiemi sono ricorsivi
(3) Dimostrare che esistono funzioni ricorsive (totali) f e g tali che: Wx ∩
Wy = Wf(x,y) e Wx ∪ Wy = Wg(x,y) ;
(4) Dimostrare che A è ricorsivo infinito sse esiste una funzione f ricorsiva
crescente (i.e. ∀n. f(n) < f(n + 1)) tale che A = range(f).
Abbiamo quindi la seguente rappresentazione in “classi” di risolvibilità degli
insiemi di numeri naturali. La classe degli insiemi ricorsivi corrisponde alla classe
degli insiemi decidibili, quella degli insiemi r.e. corrisponde alla classe degli insiemi
semidecidibili, mentre gli insiemi non r.e. (che sono la maggior parte di ℘(N)) sono
17. INSIEMI RICORSIVI E RICORSIVAMENTE ENUMERABILI
159
gli insiemi per cui non è possibile dare alcun metodo effettivo per verificare sia
l’appartenenza che la non appartenenza di un elemento a questi insiemi. Valgono
quindi le seguenti inclusioni, mostrate anche in Figura 2, tra le varie classi di
insiemi viste fino ad ora:
ricorsivi ⊂ r.e. ⊂ non − r.e.
CHAPTER 18
I Teoremi di Ricorsione
I teoremi di ricorsione, detti anche di punto fisso, sono tra i risultati più
importanti che si incontrano nello studio della teoria della ricorsività. In questo
capitolo studieremo i teoremi di ricorsione e le loro conseguenze nello studio di
proprietà di programmi.
1. Il primo Teorema di ricorsione
Teorema 18.1 (Teorema di ricorsione di Kleene). Sia t ∈ R una funzione
ricorsiva (totale). Allora esiste n ∈ N tale che ϕn = ϕt(n) .
Proof. Sia u l’indice di una generica MdT ϕu . Definiamo la seguente funzione parziale:
ϕ
ϕu (u) (x) se ϕu (u) ↓
ψ(u, x) =
↑
altrimenti
È chiaro che la funzione cosı̀ definita è parziale ricorsiva (si descriva a parole una
MdT che “calcola” ψ). Quindi, per il Teorema s-m-n, esiste una funzione ricorsiva
(totale) g tale che: ψ(u, x) = ϕg(u) (x). Sia ora t una funzione ricorsiva (totale).
Poiché la composizione di funzioni ricorsive è ricorsiva, t ◦ g è ricorsiva. Quindi
t ◦ g = ϕv per qualche v. Pertanto t ◦ g(v) = ϕv (v). Vista la genericità di u,
sostituendo u con v nella definizione di ψ(u, x) otteniamo:
ϕ
t(g(v)) (x) se ϕv (v) = t(g(v)) ↓
ψ(v, x) = ϕg(v) (x) =
↑
altrimenti
Poiché t ◦ g è totale, ϕg(v) (x) = ϕt(g(v)) (x). Posto quindi n = g(v), abbiamo che
ϕn = ϕt(n) .
Il significato intuitivo del teorema di ricorsione di Kleene sarà più chiaro in
seguito, in particolare nella Sezione 4. Per ora ci basti utilizzarlo come potente
metodo per dimostrare proprietà di ricorsività sugli elementi di ℘(N). Vediamo
qualche esempio:
Esempio 18.2. Esiste un n ∈ N t.c. range(ϕn ) = {n}? Utilizziamo il Teorema
di Ricorsione per stabilire ciò. Sia g(x, y) = x. g è calcolabile e totale. Per il
161
162
18. I TEOREMI DI RICORSIONE
Teorema s-m-n avremo che esiste h ricorsiva tale che per ogni y:
ϕh(x) (y) = g(x, y)
Per definizione, range(ϕh(x) ) = {x}. Per il Teorema di ricorsione, si ha che esiste
n ∈ N t.c. ϕh(n) (y) = ϕn (y). Pertanto range(ϕn ) = range(ϕh(n) ) = {n}.
Esempio 18.3. Esiste un n ∈ N t.c. Wn = {n}? Sia g(x, y) definita come:
0 y=x
g(x, y) =
↑ altrimenti
g è calcolabile. Per il Teorema s-m-n avremo che esiste h ricorsiva tale che
per ogni y:
ϕh(x) (y) = g(x, y)
Per definizione, Wh(x) = {x}. Per il Teorema di ricorsione, si ha che esiste n ∈ N
t.c. ϕh(n) (y) = ϕn (y). Pertanto Wn = Wh(n) = {n}.
Esercizio 18.4. Sia, per i > 0, pi l’i-esimo numero primo (p1 = 2, p2 =
3, . . . ). Si dimostri che esiste un indice u tale che Wu = {p1 , p2 , . . . , p(u!) } e
Eu = {1, 2, . . . , u}.
Esercizio 18.5.
Si mostri che esiste ν ∈ N tale che:
2y
ϕν (y)
5
y≤ν
y>ν
2. Il secondo Teorema di ricorsione
In questo paragrafo enunceremo il secondo teorema di ricorsione che estende il
risultato del primo. Utilizzeremo questo teorema per la dimostrazione del teorema
di Myhill nel Capitolo 19.
Teorema 18.6. Sia f : N2 −→ N ∈ R una funzione ricorsiva (totale). Allora
esiste una funzione ν : N −→ N ∈ R ricorsiva totale tale che:
∀y ∈ N : ϕf(ν(y),y) = ϕν(y) .
Proof. Definiamo la seguente funzione parziale:
ϕ
f(ϕx (x),y) (z) se ϕx (x) ↓
ψ(x, y, z) =
↑
altrimenti
Per il Teorema s-m-n applicato al primo argomento, esiste una funzione calcolabile
totale s tale che ψ(x, y, z) = ϕs(x,y) (z) . Si noti che, se ϕx (x) ↓, allora
(2.1)
ϕs(x,y)
=
ϕf(ϕx (x),y)
3. IL TEOREMA DI RICE
163
Per il Teorema s-m-n applicato al secondo argomento di s sappiamo che esiste
una funzione calcolabile totale t tale che:
(2.2)
s(x, y) =
ϕt(y) (x)
Definiamo la funzione
def
ν(y) = ϕt(y) (t(y))
Poiché sia t che ϕt(y) sono ricorsive e totali, lo sarà anche ν (dunque in particolare
ϕν (ν) ↓). Dunque:
ϕν(y)
=
ϕϕt(y) (t(y))
Def di ν
=
ϕs(t(y),y)
(2.2), con x = t(y)
=
ϕf(ϕt(y) (t(y)),y)
=
ϕf(ν(y),y)
Poiché ϕν (ν) ↓ e (2.1)
Def di ν
3. Il Teorema di Rice
Uno dei risultati più importanti dimostrabili mediante il teorema di ricorsione
è il Teorema di Rice. Esso afferma che ogni proprietà non banale che rappresenti
ciò che viene calcolato dalle MdT (o dai programmi, vedi Sezione 4) non è ricorsiva. Definiamo innanzitutto cosa si intende per proprietà sulle MdT. Sia al solito
{ϕi }i∈N una enumerazione delle MdT.
Definizione 18.7. Una proprietà sulle MdT è un qualsiasi sottoinsieme di
{ϕi }i∈N (con abuso di notazion, qualsiasi sottoinsieme di N).
Una proprietà sulle MdT è pertanto univocamente identificata dall’insieme
degli indici delle MdT in essa contenuti. Nel seguito quindi non faremo distinzione
tra proprietà intesa come insieme di indici (e quindi di numeri) di MdT o proprietà intesa come insieme di MdT. Nel primo caso, ovviamente potremo parlare
di proprietà ricorsive (o decidibili), r.e. (o semidecidibili), e proprietà non r.e.
Definizione 18.8. Sia dunque Π una proprietà sulle MdT, Π ⊆ N. Π è
estensionale (o chiusa per eguaglianza estensionale) se per ogni x, y ∈ N:
x ∈ Π ∧ ϕx = ϕy ⇒ y ∈ Π
Intuitivamente, una proprietà è estensionale quando questa “parla” di cosa
calcolano le MdT in essa contenute, e non di come queste macchine sono “fatte”.
Il seguente risultato sancisce l’esistenza di un numero arbitrario di MdT tra loro
equivalenti.
Lemma 18.9 (Tecnica del Padding). ∀i, k. ∃j : (ϕj = ϕi ∧ j > k).
164
18. I TEOREMI DI RICORSIONE
Proof. Siano dati i e k e sia ϕi l’i-esima MdT. Se i > k allora ponendo j = i
si ha la tesi. Se i ≤ k allora è sufficiente aggiungere a ϕi un numero congruo di
quintuple (con stati mai raggiungibili dal calcolo) in modo tale che si ottenga ϕj
con j > k.
Esercizio 18.10. Si dimostri una proprietà analoga per i programmi While.
Mediante la tecnica del padding si osserva che esistono infinite MdT equivalenti
ad una MdT data. Pertanto, una proprietà estensionale sulle MdT non può che
essere un insieme infinito.
Esercizio 18.11. Si dimostri che le seguenti proprietà sono estensionali:
•
•
•
•
∅ e N. In particolare,
tali proprietà sono dette banali;
Dato
F
⊆
PR,
i
:
ϕ
= ψ ∧ψ ∈F ;
i
i
:
ϕ
=
λx.
x
;
i
i : ϕi è definitivamente ≥ 3 .
Al contrario, la proprietà {157} che include solo la MdT di indice 157 non è
estensionale. Infatti esistono infinite MdT equivalenti ad una data MdT (Lemma
18.9), e pertanto l’insieme {157} non può essere estensionale.
Teorema 18.12 (Teorema di Rice). Sia Π una proprietà estensionale. Π è
ricorsiva sse è banale (ovvero Π = ∅ oppure Π = N).
Proof. La direzione (←) segue per definizione.
(→) Sia Π ricorsivo e g la sua funzione caratteristica:
1 se x ∈ Π
g(x) =
0 se x 6∈ Π
Supponiamo per assurdo che Π non sia banale. Allora esistono a, b ∈ N tali che
a ∈ Π e b 6∈ Π. Definiamo la funzione
b se g(x) = 1
h(x) =
a se g(x) = 0
Essendo g ricorsiva per ipotesi, chiaramente h è ricorsiva (e totale). Per il Teorema
di ricorsione di Kleene, esiste n0 tale che ϕn0 = ϕh(n0 ) . Da questo fatto deriviamo
l’assurdo:
• Se n0 ∈ Π allora essendo Π estensionale, h(n0 ) ∈ Π, ma questo è assurdo
poiché h(n0 ) = b e b 6∈ Π.
• Se n0 6∈ Π allora essendo Π estensionale, h(n0 ) 6∈ Π, ma questo è assurdo
poiché h(n0 ) = a e a ∈ Π.
Quindi Π non può essere ricorsivo e non banale.
3. IL TEOREMA DI RICE
165
Il Teorema di Rice dunque afferma che ogni funzione parziale ricorsiva o MdT
ammette un insieme infinito non ricorsivo di indici di funzioni o macchine equivalenti. In particolare osserviamo che i seguenti problemi, di chiaro interesse informatico, non sono decidibili in seguito al Teorema di Rice.
Esempio 18.13. Data ψ ∈ PR, ed una MdT M, non è in generale decidibile
se M “calcola” ψ. Infatti se
j è l’indice della MdT M, allora questo problema
equivale a decidere se j ∈ i : ϕi = ψ che è una proprietà estensionale non
banale ( i : ϕi = ψ 6= ∅ poichéesiste una MdT che calcola la funzione parziale
ricorsiva ψ essendo ψ ∈ PR e i : ϕi = ψ 6= N poiché non tutte le MdT
calcolano la stessa funzione) delle MdT, e quindi per il Teorema di Rice è una
proprietà non ricorsiva.
Esempio 18.14. Date M1 e M2 , non è decidibile se esse sono equivalenti
(ovvero calcolano la stessa funzione). Dimostrare questo per esercizio applicando
il teorema di Rice.
È necessario fare molta attenzione ad utilizzare il Teorema di Rice. Esso
infatti ha come ipotesi il fatto che l’insieme di cui si vuole studiare la ricorsività
sia estensionale. Alcuni insiemi possono trarre in inganno, come nel seguente
esempio.
Esempio 18.15. Il seguente esempio, ideato da G. Longo [19], non può essere
risolto invocando il Teorema di Rice. Definiamo una nozione debole di equivalenza
tra MdT (o programmi) come segue:
ϕi ∼ ϕj ⇔ (∀x. (ϕi (x) ↓ ∧ϕj (x) ↓ ⇒ ϕi (x) = ϕj (x)))
Ci chiediamo se l’insieme D = range(ψ) con ψ(i) = µj. (ϕi ∼ ϕj ) è ricorsivo.
Si osservi che la funzione sempre indefinita λx. ↑ è debolmente equivalente
ad ogni altra funzione parziale ricorsiva, ovvero, per ogni i ∈ N: ϕi ∼ ϕu con
u indice della più piccola MdT nella numerazione delle MdT {ϕn }n∈N , tale che
ϕu = λx. ↑. Quindi, D ⊆ {0, . . . , u} è un insieme finito e quindi ricorsivo.
Tuttavia interpretando erroneamente la proprietà come estensionale, l’utilizzo
del Teorema di Rice porterebbe ad affermare che D non è ricorsivo. L’insieme D
non caratterizza una proprietà estensionale: infatti, = ⇒ ∼, se i ∈ D e ϕk = ϕi
con k > i (k è facilmente ottenibile con la tecnica del Padding), allora k 6∈ D
(dimostrare per esercizio).
Esercizio 18.16.
(1)
Dire (giustificando formalmente) per quali A ⊆ N il seguente insieme
i : Wi = A è ricorsivo.
(2) Dire (giustificando formalmente) se i : ∀x. ϕi (x) ↑ è ricorsivo, r.e. o
non r.e.
(3) Dimostrare che i : ϕi è definitivamente ≥ 3 è non ricorsivo.
(4) Dimostrare che i : ∃x. ϕi (x) = x è non ricorsivo.
166
18. I TEOREMI DI RICORSIONE
Il Teorema di Rice è assai disarmante di fronte a proprietà estensionali che
riguardano MdT (quindi programmi). Di fatto, ogni proprietà non banale che
tratti ciò che calcolano le MdT è non ricorsiva (non decidibile). Tuttavia, è possibile rappresentare alcune proprietà estensionali non ricorsive (ma r.e. ) in modo
decidibile, ricorrendo a proprietà su MdT non estensionali decidibili.
Definizione 18.17. Sia Π una proprietà. A ⊆ N è una rappresentazione di Π
se per ogni ϕi ∈ Π esiste j ∈ A tale che ϕi = ϕj e, viceversa, per ogni j ∈ A esiste
ϕi ∈ Π tale che ϕi = ϕj .
Ogni insieme A ⊆ N può essere esteso in modo estensionale ad una proprietà
Π(A) tale che A ⊆ Π(A) e A rappresenta Π(A). Basta definire Π(A) = i : ∃j ∈
A. ϕi = ϕj .
Teorema 18.18. Sia A un insieme r.e. . Allora esiste una rappresentazione
ricorsiva B della proprietà Π(A).
Proof. Se A = ∅ allora basta porre B = A = ∅. B è chiaramente una rappresentazione ricorsiva di Π(A) = ∅. Supponiamo che A 6= ∅. Per il Teorema 17.6,
esiste una funzione ricorsiva (totale) f tale che A = range(f). Definiamo g tale
che:
g(0) = f(0)
g(n + 1) =
jn
dove ϕjn = ϕf(n+1) e jn > g(n)
Mostriamo che g è ben definita: per il Lemma del Padding jn cosı̀ costruito esiste
sempre, basta porre i = f(n + 1) e k = g(n) nel Lemma del Padding. g è inoltre
crescente e ricorsiva. Quindi per il Teorema 17.10 B = range(g) è ricorsivo.
Inoltre, è chiaro che B è una rappresentazione per Π(A) (segue banalmente per
costruzione, verificare per esercizio).
Esercizio 18.19. Si mostri che esiste una rappresentazione ricorsiva della
proprietà Π(A) per i seguenti insiemi:
(1) A = {i : 2 ∈ Wi }
(2) A = {i : ϕi (2) = ϕ2·i (4)}
(3) A = {i : ∃j ≤ i.j ∈ Wi }
(4) A = {i : ∀j ∈ {1900, . . . , 2100} : j ∈ Wi }
Una ulteriore importante caratterizzazione di proprietà r.e. o delle rappresentazioni è dato dal seguente Teorema:
Teorema 18.20 (Rice e Shapiro). Sia Π una proprietà estensionale. Se Π è
r.e. allora per ogni i ∈ N
i ∈ Π sse ∃j ∈ Π.ϕj ⊆ ϕi ∧ Wj è finito
Proof. (→) Sia i ∈ Π. Se Wi è finito, si prenda j = i. Supponiamo che
i ∈ Π, Wi è infinito, ma j ∈
/ Π ogni qual volta ϕj ⊆ ϕi e Wj è finito. Sia P una
4. PROPRIETÀ DI PROGRAMMI
167
MdT t.c. P(z) ↓ sse ϕz (z) ↓ e sia g definita come segue:
ϕ (t) se P(z) non termina in ≤ t passi
i
g(z, t) =
↑
altrimenti
Per il Teorema s-m-n esiste s ricorsiva tale che: g(z, t) = ϕs(z) (t). Ora:
• z ∈ K implica che Ws(z) è finito e ϕs(z) ⊆ ϕi . Per ipotesi, s(z) ∈
/ Π.
• z∈
/ K implica che ϕs(z) = ϕi . Pertanto s(z) ∈ Π.
Dunque l’appartenenza a Π è equivalente all’appartenenza a K̄ che sappiamo non
essere r.e.: assurdo.
(←) Supponiamo che esista i ∈ N tale che: ∃j.ϕj ⊆ ϕi e inoltre Wj è finito e
i∈
/ Π. Sia g definita come segue:
ϕ (t) se t ∈ W oppure z ∈ K
i
j
g(z, t) =
↑
altrimenti
Per il Teorema s-m-n esiste s ∈ R tale che g(z, t) = ϕs(z) (t). Abbiamo che:
• z ∈ K implica che ϕs(z) = ϕi . Per l’ipotesi su i, s(z) ∈
/ Π.
• z∈
/ K implica che ϕs(z) |Wj = ϕi . Dunque s(z) è estensionalmente equivalente a ϕj e pertanto s(z) ∈ Π.
Anche in questo caso l’appartenenza a Π è equivalente all’appartenenza a K̄ che
sappiamo non essere r.e.: assurdo.
Esercizio 18.21. Si mostri che i seguenti insiemi non sono r.e.:
(1) {i : Wi = N}
(2) {i : Wi è infinito }
(3) {i : ∀x ∈ N(x è pari → x ∈ Wi )}
4. Proprietà di programmi
Il Teorema di Rice fornisce un importante risultato per comprendere i limiti
dei sistemi automatici per dimostrare o verificare proprietà di programmi. Consideriamo un linguaggio di programmazione Turing-completo L. π è una proprietà
dei programmi di L se π ⊆ L, ovvero π è l’insieme dei programmi che verificano
la proprietà. Possiamo distinguere tra proprietà sintattiche e semantiche dei programmi. Le prime riguardano come i programmi sono scritti, mentre le seconde
riguardano il comportamento. Esempi di proprietà di programmi sono:
P ∈ L : ∀x ∈ D. P termina
proprietà semantica
P ∈ L : P contiene un ciclo while
proprietà sintattica
P ∈ L : |P| < 1000
proprietà sintattica
P ∈ L : P è un programma sicuro
proprietà semantica
168
18. I TEOREMI DI RICORSIONE
È evidente che le proprietà dei programmi di maggior interesse sono quelle semantiche, ovvero quelle che riguardano il comportamento dei programmi, ovvero ciò
che questi fanno/calcolano.
Il concetto di proprietà estensionale caratterizza le proprietà semantiche dei
programmi. Una proprietà π ⊆ L è estensionale se per ogni P, G ∈ L:
P ∈ π ∧ [[P]]L = [[Q]]L ⇒ Q ∈ π
Data una proprietà π ⊆ L è sempre possibile estendere π alla più piccola proprietà
estensionale che contiene π. Sia P ∈ L un programma. Definiamo:
def
Pз = Q ∈ L : [[P]]L = [[Q]]L
def S
È chiaro che πз = P∈π Pз è la più piccola proprietà estensionale che contiene
π. Una proprietà π è estensionale sse π = πз . È importante osservare che, per il
Lemma del Padding, una proprietà estensionale non vuota è infinita.
Il Teorema di Rice afferma dunque che una proprietà estensionale non vuota
di programmi, ovvero in grado di caratterizzare almeno un programma, è ricorsiva, ovvero decidibile, se e solo se questa è banale, ovvero π = L. La proprietà
π = L corrisponde ad affermare che un programma è un programma. Solo quindi
questa banale affermazione sui programmi è decidibile, ovvero esiste un algoritmo
(programma) in grado di verificarla automaticamente.
Diamo di seguito una dimostrazione alternativa del Teorema di Rice per le
proprietà del linguaggio While. Questa dimostrazione mette in luce il legame
diretto tra il problema della terminazione e la decidibilità di proprietà estensionali.
Proof. Supponiamo che π sia estensionale (π = πз ) non banale (π 6= L e
π 6= ∅) e decidibile. Dimostriamo che questo implicherebbe la decidibilità del
problema della terminazione.
Consideriamo il programma W sempre divergente:
read(x); while (nil.nil) do x := x endw; write(y)
È chiaro che per ogni d ∈ D, [[W]](d) ↑. Supponiamo che W ∈ π. Poiché π è non
banale (π 6= L), esiste un programma R ∈ L tale che R ∈
/ π:
read(h); CR ; write(k)
Consideriamo un generico programma P:
read(x); CP ; write(y)
4. PROPRIETÀ DI PROGRAMMI
169
e sia e ∈ D. Possiamo assumere che i programmi P ed R non condividano variabili.
Costruiamo il seguente programma Q:
read(x);
h := x;
[memorizzo l’input x in h]
x := e; CP ; ResP := y;
[ResP := [[P]](e)]
CR ; ResR := k;
[ResR := [[R]](x)]
write(ResR)
Se [[P]](e) =↑ allora ∀d ∈ D. [[Q]](d) =↑. Se [[P]](e) =↓ allora ∀d ∈ D. [[Q]](d) =
[[R]](d). Pertanto abbiamo che:
• Se [[P]](e) ↑ allora [[Q]] = [[W]] che per estensionalità di π e poiché W ∈ π
implica che Q ∈ π.
• Se [[P]](e) =↓ allora [[Q]] = [[R]] che per estensionalità di π e poiché R 6∈ π
implica che Q 6∈ π.
Pertanto, avremo che [[P]](e) ↓ se e solo se Q ∈ π: la decidibilità di π implicherebbe
la decidibilità della terminazione di [[P]](e). Vista la genericità di P e dell’argomento
e ∈ D, avremmo dimostrato la decidibilità della terminazione per un linguaggio
Turing-completo, il che è assurdo.
Un assurdo analogo si dimostra se W 6∈ π [completare per esercizio].
L’indecidibilità delle proprietà estensionali deriva quindi direttamente dalla
indecidibilità della terminazione. È importante osservare che questa indecidibilità
non deriva necessariamente da complicati programmi di raro interesse pratico. La
terminazione del seguente semplice programma, ad esempio, costituisce da anni
un enigma per gli scienziati, enigma che a tutt’oggi non risulta essere stato risolto
(utilizziamo un linguaggio While esteso con semplici operazioni aritmetiche e con
l’istruzione if-then-else):
while (n ≥ 2) do
if n mod 2 = 0 then n := n div 2
else n := 3n + 1
endw
L’impatto del Teorema di Rice sullo sviluppo di strumenti automatici di verifica
non è tuttavia cosı̀ drammatico. Lo sviluppo crescente di software di grandi dimensioni (dell’ordine di 10Mlinee) rende impossibile la verifica manuale di importanti
proprietà quali la correttezza, la sicurezza, l’assenza di guasti, la fault-tolerance,
etc. Queste proprietà, tutte chiaramente estensionali, risultano di vitale interesse
per l’utilizzo del software in ambienti safety-critical, quali i sistemi di controllo del
traffico aereo, delle transazioni bancarie, di centrali per la produzione di energia.
170
18. I TEOREMI DI RICORSIONE
Risulta quindi necessario sviluppare strumenti automatici (programmi) che analizzano altri programmi. Il Teorema di Rice, pur imponenedo severe limitazioni
a ciò che è effettivamente possibile dimostrare sui programmi mediante i programmi stessi (eventualmente definiti in linguaggi diversi), non deve scoraggiare
la ricerca di strumenti e metodi per verificare proprietà di programmi. Mentre
infatti questo risultato impedisce definitivamente la realizzazione di algoritmi di
verifica automatica per tutti i programmi, esso non impedisce che strumenti di
verifica automatica siano realizzabili per classi significative di programmi, ovvero
che per alcune classi di programmi, proprietà estensionali significative siano decidibili. Il Teorema di Rice stabilisce che non esiste una soluzione universale in
grado di verificare in tempo finito se un generico programma soddisfa o meno una
data proprietà estensionale. Tuttavia esistono potenti strumenti e metodi di analisi statica ovvero a tempo di compilazione, quindi decidibili, per verificare ampie
classi di programmi significativi. Questo ambito di studio è competenza dell’area
dei metodi formali e costituisce uno degli ambiti di ricerca sia teorica che applicata
più attivi in questi anni.
CHAPTER 19
Riducibilità funzionale e gradi di risolvibilità
L’inclusione insiemistica, intesa come relazione tra insiemi, non dà nessuna
informazione circa la ricorsività di un insieme. In particolare, il fatto che un
insieme sia contenuto in un dato insieme ricorsivo non implica in alcun modo la
ricorsività del primo: la ricorsività di N non implica la ricorsività di ogni suo
sottoinsieme (e.g. K ⊂ N). Anzi, come si è visto, la maggior parte dei sottoinsiemi
di N non è nemmeno r.e. In questa sezione studieremo il concetto di riducibilità
funzionale. Questo concetto permette di correlare tra loro insiemi in modo tale
che la ricorsività (o la non ricorsività) di un insieme possa essere propagata agli
insiemi ad esso correlati mediante riduzione
funzionale.
Consideriamo l’insieme K2 = hx, yi : y ∈ Wx . Con hx, yi si intende in
questo contesto una possibile codifica delle coppie di naturali in numero naturale
(ad esempio 2x · 3y ). In questo modo ci occupiamo solo di relazioni tra insiemi di
numeri naturali e non di insiemi di tuple generiche di numeri naturali.
Lemma 19.1. K2 è r.e. .
Proof. Si definisca g nel modo seguente: g(z) = 1 se z = hx, yi e ϕx (y) ↓,
indefinita altrimenti. g è la funzione semicaratteristica di K2 ed è chiaramente
calcolabile.
K2 ha una struttura molto simile a K, che sappiamo essere un insieme non
ricorsivo. Ci chiediamo se anche K2 sia non ricorsivo. Si osservi che K2 rappresenta
una generalizzazione di K:
x ∈ K sse hx, xi ∈ K2
Pertanto, se K2 fosse ricorsivo, avremmo un modo per decidere l’appartenenza
a K (ovvero K sarebbe ricorsivo) che sappiamo essere assurdo. In altri termini,
l’esistenza di una funzione totale e calcolabile f tale che
x ∈ K sse f(x) ∈ K2
ci ha permesso di dire qualcosa (di negativo) su K2 .
Quello che abbiamo visto è un metodo per ridurre la decidibilità dell’appartenenza
ad un insieme alla decidibilità del’appartenenza ad un altro insieme. Questo esempio conduce alla seguente definizione.
171
172
19. RIDUCIBILITÀ FUNZIONALE E GRADI DI RISOLVIBILITÀ
Definizione 19.2. Siano A, B ⊆ N. A è (funzionalmente) riducibile a B
(A B) se esiste una funzione (totale) ricorsiva f tale che:
x ∈ A ⇔ f(x) ∈ B
Nel caso A B con funzione ricorsiva f, diremo che A si riduce a B via
f. L’ordine A B fa pensare che A non è più difficile di B. La relazione è
denominata <m (many-one reducibility) nel testo di Rogers [27].
1. La relazione di riducibilità
In questo paragrafo vedremo alcuni esempi e proprietà della nozione di riducibilità
funzionale.
Esempio 19.3. Sia T = {x : ϕx = 0}. Mostriamo che K T . Sia f(x, y) = 0 se
ϕx (x) ↓, indefinita altrimenti. f è calcolabile e per s-m-n esiste g totale ricorsiva
tale che ϕg(x) (y) = f(x, y). Si osserva che x ∈ K se e solo se g(x) ∈ T .
Lemma 19.4. La relazione ⊆ ℘(N) × ℘(N) è riflessiva e transitiva.
Esercizio.
Pertanto, possiamo definire una relazione di equivalenza ≡ tra insiemi di numeri, in modo tale che A ≡ B sse A B e B A. Denoteremo con [A]≡ la classe
di equivalenza di A, ovvero l’insieme B ⊆ N : A ≡ B .
In questa relazione l’insieme vuoto ∅ e l’insieme N giocano un ruolo particolare
e forse inatteso:
Lemma 19.5.
(1) Sia A un insieme. N A se e solo se A 6= ∅.
(2) Sia A un insieme. ∅ A se e solo se A 6= N.
Proof. (1) Sia A 6= ∅, sia a ∈ A e sia f = λx.a. Si osservi che x ∈ N ↔
f(x) ∈ A.
Viceversa, per assurdo N ∅. Allora esisterebbe f tale che x ∈ N → g(x) ∈ ∅.
Ma questo è assurdo in quanto, ad esempio 0 ∈ N, ma f(0) non appartiene a ∅.
(2) Mostriamo che se A ⊂ N, allora ∅ A. Sia b ∈ N \ A e sia g = λx.b.
Dobbiamo mostrare che x ∈ ∅ ↔ g(x) ∈ B. Poiché nessun x appartiene ad ∅ devo
solo mostrare che per x ∈
/ ∅ (ovvero x ∈ N) vale che: g(x) ∈
/ A. Ciò è immediato
per la definizione di g.
Viceversa, supponiamo per assurdo che ∅ N. Allora esisterebbe g tale che
x ∈ ∅ → f(x) ∈ N. Dunque dovrebbe valere in particolare per x = 0: 0 ∈
/ ∅ e
g(0) ∈
/ N. Ma g è totale e dunque g(0) ∈ N. Assurdo.
Pertanto ∅ e N sono degli insiemi minimali rispetto alla relazione ma tra loro
incomparabili (si veda Figura 1—l’insieme S sarà illustrato nel Teorema 19.24).
Vediamo alcune proprietà fondamentali della nozione di riduzione funzionale.
Teorema 19.6. Sia A, B ⊆ N.
(1) A B ⇒ Ā B̄;
1. LA RELAZIONE DI RIDUCIBILITÀ
173
non r.e.
K
r.e.
S
X ∈ R \ ?{∅, N}
???
??
??
??
∅
N
Figure 1. Diagramma della relazione
(2) A B e B ∈ RE ⇒ A ∈ RE.
(3) A B e B ricorsivo ⇒ A ricorsivo.
Proof. Il primo punto è banale. Dimostriamo che se A B e B ∈ RE allora
A ∈ RE. Sia ψB la funzione semicaratteristica parziale ricorsiva di B e supponiamo
che A B via f. Consideriamo la funzione parziale ricorsiva ψA definita come:
ψA (x) = ψB (f(x)). Allora abbiamo che
1 se f(x) ∈ B
ψA =
↑ se f(x) 6∈ B
Poiché x ∈ A sse f(x) ∈ B, ψA è la funzione semicaratteristica di A, e quindi
A ∈ RE. Il terzo punto ha una dimostrazione del tutto analoga al secondo punto,
considerando la funzione caratteristica di B.
Abbiamo quindi dimostrato che la proprietà di essere ricorsivo o ricorsivamente
enumerabile si propaga verso il basso secondo l’ordinamento tra insiemi. Il
seguente lemma evidenzia una importante classe di insiemi equivalenti.
Lemma 19.7. B ricorsivo e A 6= ∅ e A 6= N implica B A.
Proof. Sia a ∈ A e b ∈ N \ A e sia ψB la funzione caratteristica di B.
Definisco
a ψB (x) = 1
g(x) =
b ψ (x) = 0
B
g è ricorsiva e totale e vale che x ∈ B se e solo se g(x) ∈ A.
174
19. RIDUCIBILITÀ FUNZIONALE E GRADI DI RISOLVIBILITÀ
Come corollario, tutti gli insiemi ricorsivi diversi da ∅ e da N (insiemi ricorsivi
non banali) sono tra loro -equivalenti.
Analizziamo ora gli insiemi r.e. .
Teorema 19.8. Per ogni A ∈ RE, A K2 .
Proof. Sia A ∈ RE. Allora esiste y0 tale che A = Wy0 . Per definizione di
K2 , x ∈ A = Wy0 sse hy0 , xi ∈ K2 . Definiamo dunque fy0 = λx. hy0 , xi. È chiaro
che x ∈ A sse fy0 (x) ∈ K2 .
Pertanto, ogni insieme r.e. è riducibile a K2 . Questo significa che K2 ha un
ruolo speciale all’interno della classe degli insiemi r.e. : ogni elemento di questa
classe è riducibile a K2 .
Definizione 19.9. Un insieme C ∈ RE è completo (più precisamente, r.e.
completo) se per ogni A ∈ RE: A C.
K2 è dunque completo. Il seguente teorema dimostra che anche K è completo.
Pertanto, K ≡ K2 , ed entrambi giocano un ruolo speciale (massimale) all’interno
della classe degli insiemi r.e. È inoltre banale osservare che A è completo sse
A ∈ [K]≡ .
Teorema 19.10. K è completo.
Proof. Basta dimostrare che K2 K. Infatti, in questo modo, poiché ogni
insieme r.e. è riducibile a K2 (vedi Teorema 19.8), allora per transitività, ogni
insieme r.e. è anche riducibile a K, dimostrando la completezza di K.
Sia hx, yi ∈ K2 . Per definizione, ciò è vero sse y ∈ Wx . Definiamo la funzione
1 se ϕ (y) ↓
x
ψ(x, y, z) =
↑ altrimenti
È chiaro che ψ è parziale ricorsiva, quindi, per il teorema s-m-n, esiste una funzione
ricorsiva (totale) g tale che: ψ(x, y, z) = ϕg(x,y) (z). Si noti come, fissato x, y ∈ N,
la funzione λz. ϕg(x,y) (z) sia indipendente dal valore dell’argomento z. Si osserva
che hx, yi ∈ K2 sse ϕg(x,y) (z) ↓ per z ∈ N qualsiasi. Quindi, a maggior ragione
hx, yi ∈ K2 sse ϕg(x,y) (g(x, y)) ↓. Questo dimostra che hx, yi ∈ K2 sse g(x, y) ∈ K,
con g funzione ricorsiva.
Corollario 19.11. C ∈ RE è completo se e solo se K ≤ C.
Proof. Immediato dalla definizione.
Esempio 19.12. Sia F = x : |Wx | è finito . Dimostriamo che K F. Definiamo la funzione:
1 se ϕ (x) non converge in meno di y passi
x
ψ(x, y) =
↑ altrimenti
2. INSIEMI CREATIVI E PRODUTTIVI
175
Questa funzione è parziale ricorsiva (si descriva a parole una possibile MdT che
calcola ψ). Per il teorema s-m-n, esiste g ricorsiva (totale) tale che ψ(x, y) =
ϕg(x) (y).
• Sia x ∈ K. Allora, per definizione di K: ϕx (x) ↓. Dunque esiste n tale che
ϕx (x) ↓ in n passi. Dunque Wg(x) = {0, . . . , n − 1}. Pertanto g(x) ∈ F.
• Sia x ∈
/ K. Allora ϕx (x) ↑. Dunque Wg(x) = N e dunque g(x) ∈
/ F.
Si osservi che l’esempio non dimostra che F è completo. Manca infatti la
proprietà che F sia RE. Per mostrare che F non è r.e. si mostri per esercizio che
K̄ F.
Si osservi inoltre che se A è r.e. e non ricorsivo, allora non vale né che A Ā
né che Ā A. Se fosse A Ā allora avremmo un modo per semidecidere Ā che
è assurdo poiché A non è ricorsivo. Se fosse Ā A allora, in virtù del Teorema
19.6(1), varrebbe anche A Ā. Dunque in particolare K 6 A per ogni A che sia
complemento di un insieme r.e. . Nel seguente esercizio si mostra comunque che
esistono insiemi non r.e. che stanno sopra a K (ad esempio K join K̄).
Esercizio 19.13. Consideriamo due insiemi A e B. Chi è un insieme (possibilmente il minimo) X tale che A X e B X? Non può essere A ∪ B. Si giunge
infatti ad un assurdo partendo da B = Ā, con A 6= ∅ e A 6= N. Verificare che tale
insieme è:
A join B = {y : (y = 2x ∧ x ∈ A) ∨ (y = 2x + 1 ∧ x ∈ B)}
Si mostri inoltre che se A ≤ B allora A join B ≡ B.
Esercizio 19.14. Dimostrare che:
• Se A ∈ RE, A 6= ∅ e A 6= N, allora A ricorsivo ⇔ A Ā.
• A ∈ [B]≡ e A ricorsivo allora per ogni X ∈ [B]≡ , X è ricorsivo.
2. Insiemi creativi e produttivi
Abbiamo visto che all’interno della classe degli insiemi r.e. , esistono insiemi
(e.g. K) che hanno la proprietà per cui ogni altro insieme r.e. è ad essi riducibile.
Questi insiemi, detti completi, rappresentano quindi la classe degli insiemi r.e. ,
quasi fossero dei rappresentanti “canonici” di questa classe.
In questa sezione caratterizzeremo gli insiemi completi. Caratterizzare gli
insiemi completi significa dare una condizione che determini in modo univoco se
un dato insieme è o meno completo, senza dovere ricorrere ad altri insiemi r.e.
K̄ non è r.e. . Ma questa proprietà può essere mostrata anche in modo costruttivo e convincente: ogni volta che un insieme r.e. Wx è incluso in K̄ siamo in grado
di trovare un elemento che appartiene a K̄ \ Wx . Tale elemento, in questo caso è
proprio x. La seguente definizione generalizza questa proprietà.
Definizione 19.15. Un insieme A ⊆ N è detto produttivo se esiste una funzione (totale) ricorsiva fA , detta funzione produttiva di A, tale che:
∀x ∈ N. (Wx ⊆ A ⇒ fA (x) ∈ A \ Wx )
176
19. RIDUCIBILITÀ FUNZIONALE E GRADI DI RISOLVIBILITÀ
Un insieme A è detto creativo se A è r.e. ed il suo complemento è produttivo,
ovvero se:
A ∈ RE e ∀x ∈ N. (Wx ⊆ Ā ⇒ fA (x) ∈ Ā \ Wx )
Intuitivamente un insieme è produttivo se per ogni tentativo di enumerarlo in
modo effettivo mediante un algoritmo di indice x (ovvero per ogni Wx ⊆ A), esiste
una trasformazione effettiva di x in un punto di A che sfugge all’enumerazione.
È evidente che un insieme produttivo non può essere r.e. , altrimenti avremmo
l’assurdo che A = Wx0 per un certo x0 e al tempo stesso A \ Wx0 6= ∅, poiché
f(x0 ) ∈ A \ Wx0 . Al contrario, un insieme creativo è sempre, per definizione, r.e.,
ma il suo complemento non lo può essere. Riassumendo:
Teorema 19.16. Sia A ⊆ N.
(1) A produttivo ⇒ A non r.e.
(2) A creativo ⇒ A non ricorsivo
Proof. Immediato per definizione.
L’insieme K = x : x ∈ Wx ancora una volta è rappresentativo per la classe
di insiemi creativi, come dimostrato nel seguente teorema.
Teorema 19.17. K è creativo e K̄ è produttivo.
Proof. Dimostriamo che K̄ è produttivo. Consideriamo la funzione ricorsiva
di base id = λx. x. Ci chiediamo in quale regione del diagramma di Venn possa
stare x:
x ∈ Wx ?
x ∈ K?
x ∈ K̄ \ Wx ?
Mostreremo che le prime due ipotesi portano ad un assurdo.
Se Wx ⊆ K̄ allora id(x) = x 6∈ Wx . Infatti, se x ∈ Wx , allora per definizione
di K: x ∈ K. Poiché Wx ⊆ K̄, allora x ∈ K̄ che sarebbe assurdo. Quindi id(x) =
x 6∈ Wx .
Se x ∈ K allora avremmo x ∈ Wx ⊆ K̄, ovvero x ∈ K̄, che è assurdo poiché
x non può essere al tempo stesso in K e K̄ = N \ K. Quindi se Wx ⊆ K̄ allora
id(x) = x ∈ K̄ \ Wx , ovvero K̄ è produttivo. La dimostrazione che K è creativo è
immediata poiché K ∈ RE e K̄ è produttivo.
Il seguente teorema dimostra una proprietà fondamentale degli insiemi creativi
e produttivi, ovvero che la produttività e la creatività di un insieme viene ereditata
per riduzione funzionale.
Teorema 19.18. Siano A, B ⊆ N.
(1) A produttivo e A B ⇒ B produttivo.
(2) A creativo, A B, e B ∈ RE ⇒ B creativo.
Proof. Dimostriamo il punto (2) e lasciamo il punto (1) per esercizio. La
dimostrazione è del tutto analoga al caso creativo.
2. INSIEMI CREATIVI E PRODUTTIVI
177
(2). Sia A creativo e B ∈ RE tale che A B via f. Sia h la funzione
produttiva
di Ā. Nella dimostrazione faremo uso della notazione f−1 (X) = x : f(x) ∈ X .
E’ immediato verificare che f−1 è monotona, ovvero X ⊆ Y → f−1 (X) ⊆ f−1 (Y).
f è ricorsiva, dunque esiste una funzione parziale ricorsiva ψ(x, y) tale che
1 se f(y) ∈ Wx
ψ(x, y) =
↑ altrimenti
Per il teorema s-m-n, esiste una funzione totale ricorsiva g tale che: ψ(x, y) =
ϕg(x) (y). Inoltre, per definizione di ψ, fissato x ∈ N: Wg(x) = f−1 (Wx ).
Dimostriamo ora che B è creativo. Per fare ciò basta dimostrare che B̄ è
produttivo. Mostreremo che se Wx ⊆ B̄, allora f(h(g(x))) ∈ B \ Wx .
• Mostriamo che f(g(g(x))) ∈ B.
Se Wx ⊆ B̄, dunque f−1 (Wx ) ⊆ f−1 (B̄). Poiché A B via f,
−1
f (B̄) = Ā. Inoltre sappiamo già che f−1 (Wx ) = Wg(x) . Pertanto
si ha che Wg(x) ⊆ Ā. Per la produttività di Ā si ha dunque che:
(2.1)
h(g(x)) ∈ Ā \ Wg(x)
Poiché A B via f, e h(g(x)) ∈ Ā si ha che f(h(g(x))) ∈ B̄.
• Mostriamo che f(h(g(x))) ∈
/ Wx . Se per assurdo fosse f(h(g(x))) ∈ Wx ,
per la definizione di ψ si avrebbe che h(g(x)) ∈ Wg (x) che contraddice
(2.1).
Il seguente teorema caratterizza gli insiemi completi come tutti e soli gli insiemi
creativi, ovvero aventi un complemento produttivo. Gli insiemi completi hanno
dunque complementi non r.e. . Nella dimostrazione del Teorema seguente faremo
uso del secondo Teorema di ricorsione (Teorema 18.6).
Teorema 19.19 (Myhill). Sia A ∈ RE.
A completo ⇔ A creativo
Proof. (⇒). Sia A ∈ RE. A è completo sse per ogni X ∈ RE: X A. Poiché
K è creativo e A completo, K A. Per il Teorema 19.18, anche A è creativo.
(←). Sia A creativo con funzione produttiva per Ā, f. Sia B un generico
insieme in RE che vogliamo ridurre ad A. Definiamo la funzione:
0 se y ∈ B e z = f(x)
ψ(x, y, z) =
↑ altrimenti
Per il teorema s-m-n, esiste una funzione calcolabile totale g tale che ψ(x, y, z) =
ϕg(x,y) (z). Quindi:
{f(x)} se y ∈ B
Wg(x,y) =
∅
se y 6∈ B
178
19. RIDUCIBILITÀ FUNZIONALE E GRADI DI RISOLVIBILITÀ
Per il secondo teorema di ricorsione (Teorema 18.6) esiste una funzione calcolabile
ν tale che per ogni y ∈ N
ϕg(ν(y),y) = ϕν(y)
Allora, per ogni y si ha che:
Wg(ν(y),y) = Wν(y)
{f(ν(y))}
=
∅
se y ∈ B
se y 6∈ B
Mostriamo che f ◦ ν è la funzione di riduzione da B ad A.
• Se y ∈ B allora Wν(y) = {f(ν(y))}. Dunque, poiché f è la funzione
produttiva di Ā, se fosse f(ν(y)) ∈
/ A, allora varrebbe che Wν(y) ⊆ Ā e
pertanto f(ν(y)) ∈ Ā \ Wν(y) ovvero che f(ν(y)) ∈
/ Wν(y) . Assurdo.
• Se y ∈
/ B, allora Wν(y) = ∅. Dunque Wν(y) ⊆ Ā, e pertanto f(ν(y)) ∈ Ā.
Come conseguenza di questo fatto, osserviamo che tutti gli insiemi creativi
sono tra loro equivalenti secondo ≡. Inoltre, se A è creativo, allora la classe di
equivalenza [A]≡ è massima nell’ordinamento indotto da sulle classi di equivalenza di insiemi r.e.
Esercizio 19.20.
(1) Studiare
la relazione o ≡ esistente tra i seguenti insiemi:
• x : x è primo ;
• x : x è pari ;
• x : Wx = ∅ ;
• x : |Wx | = ω ;
• x : |Wx | < ω ;
• x : ϕx è totale
.
(2) Dimostrare
che
x
:
ϕ
è totale è produttivo (Suggerimento: dimostrare
x
che K̄ x : ϕxè totale ).
(3) Dimostrare che i : Wi è ricorsivo è produttivo.
(4) Dimostrare che i : ϕi (i) = 0 è creativo.
(5) Dimostrare che x : ∃y. x ∈ Wy ∧ y ∈ Wx è creativo.
(6) Dimostrare che x : ∃u∃v.x = hu, vi ∧ ϕu·v (u + v) = 0 è creativo.
Il seguente teorema stabilisce che ogni insieme produttivo contiene un insieme
r.e. infinito.
Teorema 19.21. Sia A un insieme produttivo. Esiste n0 ∈ N tale che
Wn0 ⊆ A ∧ |Wn0 | = ω
ovvero esiste un sottoinsieme r.e. e infinito di A.
3. INSIEMI SEMPLICI
179
Proof. Sia f la funzione di produttività di A. Costruiamo una funzione totale
ricorsiva g tale che:
range(g) ⊆ A ∧ |range(g)| = ω
La definizione di g è data per induzione. ∅ è r.e. , e so costruire una MdT M tale
che M(x) ↑ per ogni x. Sia z0 il suo indice (dunque ∅ = Wz0 ).
def
• Poniamo g(0) = ψ(z0 ). Poiché Wz0 = ∅ ⊆ A per definizione di produttività abbiamo che: ψ(z0 ) ∈ A e ψ(z0 ) 6∈ Wz0 .
• Supponiamo di aver già calcolato g(0), . . . , g(n): so costruire una MdT
M tale che M(g(0)) = · · · = M(g(n)) = 0 e M(x) ↑ per ogni x diverso da
g(0), . . . , g(n). Sia zn+1 il suo indice: dunque Wzn+1 = {g(0), . . . , g(n)}.
def
Poniamo g(n + 1) = f(zn+1 ). Poiché Wzn+1 = {g(0), . . . , g(n)} ⊆ A per
definizione di produttività otteniamo: f(zn+1 ) ∈ A e f(zn+1 ) 6∈ Wzn+1 .
La iniettività di g deriva dal fatto che, per produttività di A, g(n) = f(zn ) ∈
A \ Wzn = {g(0), . . . , g(n − 1)}. Da questo segue che |range(g)| = ω. La ricorsiva
enumerabilità di range(g) ⊆ A deriva dal Teorema di caratterizzazione di Kleene
degli insiemi r.e. .
3. Insiemi semplici
Da quanto abbiamo visto: creativi ∪ ricorsivi ⊆ r.e. . Ci chiediamo se tutti
gli insiemi r.e. sono o ricorsivi o creativi. La risposta è no. Per stabilire questo
fatto, definiamo il concetto di insieme semplice.
Definizione 19.22. A ⊆ N è semplice se
• A è r.e. ,
• |Ā| = ω, ovvero Ā è infinito,
• ∀x ∈ N. |Wx | = ω → A ∩ Wx 6= ∅.
Teorema 19.23.
(1) A semplice → A non ricorsivo
(2) A semplice → A non creativo
Proof. Per dimostrare il punto (1), supponiamo che A sia ricorsivo. Per il
Teorema di Post, Ā è ricorsivo. Pertanto, esiste x0 ∈ N tale che Ā = Wx0 e, essendo
|Ā| = ω, |Wx0 | = ω. Per definizione di insieme semplice, A ∩ Wx0 = A ∩ Ā 6= ∅
che è assurdo.
Per dimostrare il punto (2), supponiamo che A sia creativo. Per definizione, Ā
è produttivo e, per il Teorema 19.21, esiste x0 tale che Wx0 ⊂ Ā e |Wx0 | = ω.
Per definizione di insieme semplice, questo implica che A ∩ Wx0 6= ∅, ma questo
è assurdo poiché Wx0 ⊂ Ā.
Resta ora da vedere se esiste un insieme di numeri con le caratteristiche degli
insiemi semplici. Il seguente teorema, dovuto a Post, dimostra che esistono insiemi
semplici, che non sono né ricorsivi né creativi.
180
19. RIDUCIBILITÀ FUNZIONALE E GRADI DI RISOLVIBILITÀ
Teorema 19.24. Esiste un insieme semplice.
Proof. definiamo il seguente insieme:
def
A = (x, y) : y ∈ Wx ∧ y > 2x = (x, y) : ϕx (y) ↓ ∧y > 2x
È ovvio che A è r.e. e che A 6= ∅. Pertanto, esiste x0 ∈ N tale che A = Wx0
e A = range(ϕh(x0 ) ). Definiamo una relazione di precedenza ✁ sulle coppie di
numeri tale che:
(x ′ , y ′ ) ✁ (x, y)
sse ∃n ≤ m. (x ′ , y ′ ) = ϕh(x0 ) (n) ∧ (x, y) = ϕh(x0 ) (m).
ovvero, (x ′ , y ′ ) ✁ (x, y) se (x ′ , y ′ ) viene generata prima nella enumerazione di A
da parte di ϕh(x0 ) rispetto a (x, y). Definiamo l’insieme:
def
B = (x, y) ∈ A : ∀z 6= y. (x, z) ∈ A → (x, y) ✁ (x, z)
Si noti che B è r.e. [perchè?].
Definiamo dunque l’insieme S:
def
S = y : ∃x. (x, y) ∈ B
e mostriamo che è semplice.
• S è r.e. . (in quanto lo è B)
• |S̄| = ω. Infatti, sia n ∈ N. Al più n elementi di {0, . . . , 2n} sono in S
poiché per ogni numero pari x c’è al più un elemento y tale che (x, y) è
la prima coppia di A.
• Inoltre, sia |Wx | = ω. Allora esiste y > 2x tale che y ∈ Wx (essendo Wx
infinito) e y ∈ Wx ∩ S, da cui segue la tesi.
Corollario 19.25. Vale che S K ma K 6 S.
Proof. S è semplice e pertanto r.e.; dunque S K per completezza di K.
Se fosse K S allora per il Teorema di Myhill S sarebbe creativo. Questo
condraddirebbe il Teorema 19.23.
Siamo dunque in grado di collocare S nel diagramma di Figura 1.
Part 4
Complessità Computazionale
CHAPTER 20
Classi di complessità e principali risultati
Una delle linee guida del testo è quella di investigare la nostra conoscenza
di sottoinsiemi di N o, equivalentemente, di Σ∗ (ovvero di insiemi di stringhe
di caratteri di lunghezza finita costruiti a partire da un dato alfabeto Σ). Più
precisamente, il criterio di conoscenza richiesto riguarda la capacità di stabilire
se un elemento appartenga o meno ad uno di questi insiemi. Si è mostrato come
per alcuni di essi tale problema sia decidibile (insiemi ricorsivi). In particolare
si sono caratterizzati mediante grammatiche e automi alcune importanti famiglie
di insiemi ricorsivi quali i linguaggi regolari, quelli liberi, e quelli dipendenti dal
contesto. Si è mostrato come per altri il problema sia solo semidecidibile (insiemi
r.e.), e che vi siano insiemi per cui l’appartenenza non è nemmeno semidecidibile
(ad esempio per gli insiemi produttivi).
Quando un programmatore è chiamato a risolvere un problema, il primo obiettivo è quello di scrivere un programma che lo risolva e che termini per tutte le
possibili istanze di input, al fine di evitare spiacevoli attese infinite all’utilizzatore
del programma stesso. Dunque, per quanto non esista un programma universale
in grado di stabilire se un programma goda della suddetta proprietà di terminazione, il programmatore deve scrivere il codice sincerandosi, mediante opportune dimostrazioni spesso di tipo induttivo, che la proprietà di terminazione per
quel particolare programma valga. L’impiego di linguaggi di programmazione ad
alto livello è particolarmente di aiuto in questa fase.
Tuttavia nella pratica questa proprietà è necessaria ma non sufficiente. Sapere
che il programma prima o poi terminerà può non essere d’aiuto a chi necessita
della risposta entro pochi minuti. Un passo avanti si potrebbe avere fornendo
all’utilizzatore una ulteriore informazione: se l’input consta di k bits, allora il
programma su quell’input terminerà entro f(k) secondi. L’utente ora sa quanto
k
aspettare. Ma se f(k) = 2(2 ) allora per un input di 1 Byte avremo bisogno di
256
69
2 s ∼ 3.7 · 10 anni. Se questo programma decide l’appartenenza ad un insieme,
sapere che quell’insieme è ricorsivo, non pare aiutarci troppo. Ci rimane però
un dubbio: quel programma è il più veloce possibile per decidere l’appartenza a
quell’insieme e dunque quella funzione doppiamente esponenziale è una misura di
complessità dell’insieme stesso, oppure è solo basato un algoritmo male architettato
per risolvere un problema più facile?
183
184
20. CLASSI DI COMPLESSITÀ E PRINCIPALI RISULTATI
Rispondere a domande di questo tipo è il principale scopo della teoria della
complessità. In particolare si cerca di suddividere i problemi, gli insiemi in classi
di complessità. Ciò permette di partizionare la classe degli insiemi ricorsivi in vare
famiglie a complessità crescente o, meglio, non decrescente. Sono infatti numerosi e
stimolanti i problemi aperti ancora esistenti riguardanti l’inclusione stretta o meno
?
tra classi di complessità, tra cui il più famoso è P = NP, aperto ufficialmente da
Cook nei primi anni 70 [5] ma esistente già nella sostanza nel contenuto di lettere
private intercorse nei primi anni 50 tra Gödel e Von Neumann [29].
1. Problemi, insiemi, linguaggi
Il concetto di problema, il concetto di insieme, inteso come sottoinsieme di
N, e quello di linguaggio vengono utilizzati in modo equivalente nella teoria della
complessità. Un problema (decisionale) viene tipicamente posto fornendo una
certa caratterizzazione degli input possibili e una condizione da verificarsi su ciascuna istanza dell’input. Vi saranno delle istanze con risposta affermativa (istanze yes) e istanze con risposta negativa (istanze no). Ad esempio, prendiamo
il problema: dato un grafo G = hN, Ei, il grafo è connesso? Vi saranno pertanto
istanze yes come G = h{1, 2}, {{1, 2}}i e istanze no come G = h{1, 2}, ∅i. Possiamo tranquillamente assumere che ogni istanza (o input) x sia una stringa di un
alfabeto Σ fissato (nel caso sopra Σ = {‘0’, . . . , ‘9’, ‘{’, ‘}’, ‘h’, ‘i’, ‘,’}). Il linguaggio associato al problema sarà l’insieme delle istanze yes ovvero L = {x ∈ Σ∗ :
x verifica la condizione del problema}. Tale linguaggio è per definizione stessa un
insieme. Si osservi che in base a tale definizione ogni stringa che non descrive correttamente un’istanza dell’input (nell’esempio sopra potrebbe essere: h{{{, , , 112i)
finisce nelle istanze no. Ovviamente, poiché, dato Σ finito, Σ∗ è numerabile,
potremmo senza perdita di generalità studiare semplicemente sottoinsiemi di N,
come fatto nella parte 3 del presente testo.
Si osservi che la scelta di definire un problema nel modo suddetto permette di
stabilire in modo naturale e rigoroso il concetto di dimensione dell’input come la
lunghezza della stringa x, al solito denotata come |x|.
Un’ulteriore precisazione va fatta relativamente alla scelta dei problemi decisionali. Abbiamo appena mostrato come questo permetta di concentrarsi sul
problema di appartenenza ad un insieme che è stata anche la guida per la classificazione di insiemi ricorsivi e riscorsivamente enumerabili. E’ inoltre vero che
ciò non rappresenta una limitazione significativa. Ragioniamo su un esempio. Si
considerino i seguenti due problemi, il primo funzionale, il secondo, ottenuto a
partire dal primo, decisionale:
(1) Dato un grafo completo G = hN, Ei e una funzione c : E −→ N che
assegna un costo ad ogni arco, calcolare il circuito di costo minimo che
parte e arriva nel nodo 1 e passa una sola volta per tutti gli altri nodi.
(2) Dato un grafo completo G = hN, Ei, una funzione c : E −→ N che assegna
un costo ad ogni arco, e un intero t, dire se esiste un circuito che parte e
2. CLASSI DI COMPLESSITÀ IN TEMPO E TESI DI CHURCH COMPUTAZIONALE 185
arriva nel nodo 1 e passa una sola volta per tutti gli altri nodi, di costo
minore o uguale a t?
E’ chiaro che sapendo risolvere (1) avremmo immediatamente un metodo per
risolvere (2). Viceversa, assumiamo di disporre dell’algoritmo due che risolve (2).
Procederemo per bisezione:
m := 0;
P
M := e∈E c(e);
while (M − m > 1) do
t := (M + m)/2;
if due(G, c, t) = yes
then m := t
else M := t
endw
Quando M = m + 1 ci si ferma. Il valore tf del circuito di lunghezza minima per
(1) va cercato solo tra M e m. Si tratta
P dunque di ripetere l’esecuzione di due un
numero di volte massimo pari a log( e∈E c(e)) che è proporzionale al numero di
caratteri necessari per descrivere la funzione c ovvero |c|. Pertanto il numero di
iterazioni risulta limitato linearmente dalle dimensioni dell’input.
Per scoprire quali sono gli archi utilizzati nella soluzione con valore tf , si parta
dal nodo iniziale. Uno ad uno, si assegni ad un arco in uscita da lui il valore tf .
Si lanci dunque due con limite tf . Se la risposta è yes, quell’arco non stava nel
cammino minimo. Altrimenti ci stava e dunque riassegnamo a lui il suo costo
indicato da c e passiamo al nodo successivo.
Dunque, dalla soluzione al problema decisionale si può ottenere la soluzione
a quello funzionale con un numero di operazioni che dipende polinomialmente
dall’algoritmo per il problema decisionale moltiplicato per le dimensioni dell’input.
2. Classi di complessità in tempo e Tesi di Church computazionale
Seguendo il testo [23], a cui rimandiamo il lettore per approfondimenti, useremo il seguente modello computazionale:
Definizione 20.1. Una k-MdT è una Macchina di Turing M = hQ, Σ, q0 , Pi
con k-nastri. Più precisamente:
(1) Q è l’insieme finito degli stati e q0 ∈ Q è lo stato iniziale.
(2) Σ è l’alfabeto dei simboli presenti sui nastri. Assumiamo che i simboli ✄
e $ appartengano a Σ.
186
20. CLASSI DI COMPLESSITÀ E PRINCIPALI RISULTATI
(3) L’insieme delle istruzioni P rappresenta una funzione di transizione δ che
assumiamo essere una funzione totale
δ : Q × Σk −→ (Q ∪ {h, yes, no}) × (Σ × {L, R, F})k
(4) h, yes, no sono degli stati finali non presenti in Q, rispettivamente, di
terminazione, accettazione, e refutazione. δ non è definita su di essi.
(5) Si osservi che l’insieme dei movimenti (per ogni nastro) consta di tre
possibilità: L (sinistra), R (destra), e F (fermo).
(6) Assumiamo inoltre che il simbolo ✄ non possa essere cancellato e quando
viene letto su un nastro, l’effetto è che la testina, relativamente a quel
nastro, si sposta a destra.
(7) La configurazione iniziale è del tipo:
✄, ε} , . . . , |ε, {z
✄, ε} )
(qo , |ε, {z
✄, x} , |ε, {z
nastro 1 nastro 2
nastro k
ove per ogni nastro 1, . . . , k i tre parametri sono: la stringa significativa
a sinistra della testina, il simbolo letto e la stringa significativa a destra
della testina, mentre x è l’input (sul nastro 1).
Si osservi che l’assumere δ totale permette di parlare di δ anziché dell’insieme
delle produzioni P. Le assunzioni (6) e (7) fanno sı́ che le k MdT lavorino su
nastri semi-infiniti (non si vai mai più a sinistra del ✄ iniziale presente su ogni
nastro). Vi è inoltre la possibilità del movimento nullo (F). Sebbene esso possa
essere facilmente mimato da due movimenti L–R consecutivi, avere a disposizione
anche il movimento F permette di scrivere del codice più snello.
Nota 20.2. Si osservi che per k = 1 la nozione di MdT appena definita risulta
essere una restrizione della definizione generale (salvo per il movimento nullo che
però, come appena detto, può facilmente essere simulato). La restrizione riguarda
la presenza del simbolo ✄ e le sue proprietà, la totalità di δ nonché gli stati
h, yes, no. E’ comunque facile convincersi che queste restrizioni non pregiudicano
la Turing completezza del modello delle 1-MdT (richieste analoghe sono state
fatte nelle dimostrazioni di equivalenza tra funzioni Turing-calcolabili e gli altri
formalismi nei Capitoli 13 e 16).
Le nozioni già viste di relazione di successore tra configurazioni istantanee,
computazioni eccetera, si adattano immediatamente a questo formalismo. In particolare, la macchina termina se e solo se raggiunge una configurazione con lo stato
h, yes o no.
Definizione 20.3. Una k-MdT M è di tipo decisionale se ogni qual volta essa
termina, raggiunge uno degli stati finali yes o no. L’output della macchina è, in
questo caso, lo stato raggiunto.
Una k-MdT M invece calcola una funzione se ogni qual volta termina, essa
raggiunge lo h. In questo caso l’output è il contenuto del k-esimo nastro.
2. CLASSI DI COMPLESSITÀ IN TEMPO E TESI DI CHURCH COMPUTAZIONALE 187
Nello studio classico della complessità si è interessati alle macchine decisionali. Per completezza, tuttavia, abbiamo fornito anche la definizione nel caso più
generale, che permette di definire le classi di complessità funzionali (si veda [23]).
Definizione 20.4. Data una k-MdT M e un input x, il tempo richiesto da M
per x è il numero di passi di computazione necessari a M con input x per terminare.
Sia f : N −→ N una fuzione totale. Si dice che una k-MdT M opera in tempo
f(n) se per ogni input x il tempo richiesto da M per x è minore o uguale a f(|x|)
(ove, al solito, |x| denota la lunghezza della stringa x).
Sia Σ un alfabeto. Assumiamo che ✄ ∈
/ Σ e $ ∈
/ Σ. Tali simboli invece
occorrono nell’alfabeto delle k-MdT di cui parleremo. Useremo tali k-MdT per
decidere l’appartenenza di un elemento a un dato linguaggio L ⊆ Σ∗ .
Definizione 20.5 (Classi in tempo). Un linguaggio L ⊆ Σ∗ è deciso da una
k-MdT M se per ogni x ∈ Σ∗ vale che:
• Se x ∈ L allora M con input x termina nello stato yes (in breve M(x) =
yes).1
• Se x ∈
/ L allora M con input x termina nello stato no (in breve M(x) =
no).
Se L ⊆ Σ∗ è deciso da una k-MdT M e M opera in tempo f(n), allora L ∈
TIME(f(n)).
TIME(f(n)) cosı́ definita è una classe di complessità in tempo. Essa rappresenta l’insieme di linguaggi (o, equivalentemente, di insiemi) che possono essere
decisi (dunque si tratta di insiemi ricorsivi) in tempo limitato da una funzione
nota a priori.
Il seguente Teorema mostra che il formalismo ora descritto non permette di
descrivere algoritmi sensibilmente più efficienti rispetto alle MdT a un nastro.
Teorema 20.6. Se M è una k-MdT che opera in tempo f(n), possiamo costruire una 1-MdT M ′ equivalente e che opera in tempo O(nf(n) + f(n)2 ).
L’idea della dimostrazione è semplicemente quella della simulazione di una
computazione su k nastri in un solo nastro. Il contributo nf(n) si può omettere
qualora, come spesso accade, f(n) ≥ n.
Per quanto riguarda la notazione O-grande, date due funzioni f, g : N −→ N, si
dice che f(n) è O(g(n)) se esiste c > 0 e n0 ∈ N tali che (∀n ≥ no )(f(n) ≤ cg(n)).
f(n)
≤ k per qualche k ≥ 0. Dire dunque che una
In altri termini, che limn→ ∞ g(n)
MdT opera in tempo O(g(n)) significa che opera in tempo f(n) con f(n) che è
O(g(n)).
Il seguente Teorema giustifica l’utilizzo della notazione O-grande per la caratterizzazione delle classi di complessità, mostrando come le ‘costanti’ non contino.
1 Se
vale solo questa condizione allora L è semi-deciso da M.
188
20. CLASSI DI COMPLESSITÀ E PRINCIPALI RISULTATI
Teorema 20.7 (Speed-up). Sia L ∈ TIME(f(n)). Allora:
∀ǫ > 0 : L ∈ TIME(ǫf(n) + n + 2).
L’idea della dimostrazione è la seguente: data una k-MdT M che decide L.
Costruiamo una h-MdT (ove h = k se k ≥ 2, 2 altrimenti) il cui linguaggio codifica
m-uple di Σ. In tal modo ad ogni passo di computazione di M ′ corrispondono m
passi di computazione di m. Scegliendo m in funzione di ǫ (m > ǫ6 ) si ottiene il
risultato.
Nota 20.8. Come corollario ‘pratico’ del risultato suddetto, si mostra che un
potenziamento dell’HW può permettere solo un miglioramento lineare dei tempi
di esecuzione. Algoritmi inefficienti rimangono tali anche se il calcolatore diventa
10, 100, 1000 volte più veloce.
Verificato che il formalismo delle k-MdT e quello delle 1-MdT sono sostanzialmente equivalenti a meno di un fattore polinomiale, può a questo punto sorgere il
dubbio che altri formalismi (per esempio, le URM [6], le RAM [23], i programmi
While [15]) permettendo di descrivere algoritmi più veloci, rendano la MdT non
adatta a ciò. Vale invece una versione computazionale della tesi di Church:
Tutti i formalismi di calcolo ragionevoli sono computazionalmente equivalenti a meno di fattori polinomiali.
Ad esempio, nel caso delle RAM, vale il seguente risultato [23]:
Teorema 20.9. Sia L ∈ TIME(f(n)). Allora esiste un programma RAM che
calcola la funzione caratteristica di L in tempo O(f(n)).
Sia P un programma RAM che calcola la funzione φ in tempo f(n). Allora
esiste una 7-MdT che calcola φ in tempo O(f(n)3 ).
Le dimostrazioni sono, ovviamente, basate su simulazione di un formalismo
Turing completo in un altro.
In Figura 1 sono definite le importanti classi P ed EXPTIME relative a quanto
abbiamo ora descritto.
3. Il non determinismo
In questa sezione presenteremo un modello di computazione non ragionevole
(nel senso della tesi di Church sopra). Il concetto che si vuol catturare è quello
del non-determinismo. Diversamente da quanto accade per DFA e NFA in cui
sostanzialmente si è provata l’equivalenza tra i formalismi, in questo caso i risultati
saranno meno ovvi e alcuni non sono noti.
Definizione 20.10. Una MdT M = hQ, Σ, q0 , Pi (a un nastro) è non-deterministica
(in breve, una ND-MdT) se P è una relazione:
P ⊆ (Q × Σ) × ((Q ∪ {h, yes, no}) × Σ × {L, R, S})
3. IL NON DETERMINISMO
P
def
EXPTIME
def
NP
def
NEXPTIME
def
L
def
PSPACE
def
NL
def
NPSPACE
def
=
=
=
=
=
=
=
=
189
S
TIME(nk )
S
NTIME(nk )
k≥0
k
S
TIME(2n )
S
NTIME(2n )
k≥0
k≥0
k≥0
k
SPACE(log n)
S
k
k≥0 SPACE(n )
NSPACE(log n)
S
k
k≥0 NSPACE(n )
Figure 1. Sommario delle classi di complessità introdotte
Per il resto questo modello eredita le definizioni fornite per il caso deterministico. La differenza sta che i successori a una data configurazione non sono solo
0 (quando è terminante) o 1 (tutti gli altri casi). Una configurazione (q, ℓ, s, r) ha
un numero di successori pari all’insieme delle quintuple in P che iniziano con q, s.
Data una configurazione iniziale (q0 , ε, ✄, x) esiste una famiglia di possibili computazioni non deterministiche. La relazione di successore −→∗ dunque è una vera
G
e propria relazione simile alla ⇒∗ usata nelle produzioni possibili per un linguaggio
CF.
Definizione 20.11. Un linguaggio L ⊆ Σ∗ è deciso da una ND-MdT M se
per ogni x ∈ Σ∗ :
• Se x ∈ L, allora esiste una computazione non deterministica tale che:
(q0 , ε, ✄, x) −→∗ (yes, u, s, v)
• Se x ∈
/ L, allora non esiste una computazione non deterministica tale che:
(q0 , ε, ✄, x) −→∗ (yes, u, s, v)
Si noti la grande asimmetria tra x ∈ L e x ∈
/ L. Quando x ∈
/ L tutte le
computazioni potrebbero addirittura essere non terminanti. Con la sola definizione
sopra, il linguaggio L appare solo semideciso.
Definizione 20.12. Una ND-MdT M opera in tempo f(n) se ogni computazione non deterministica sull’input x ha al più lunghezza f(|x|).
Se una ND-MdT M che opera in tempo f(n) decide un linguaggio L ∈ Σ∗ ,
allora siamo in grado anche di stabilire in modo effettivo se x ∈
/ L. Più precisamente:
Definizione 20.13. Se un linguaggio L ∈ Σ∗ è deciso da una ND-MdT M che
opera in tempo f(n), allora L ∈ NTIME(f(n)).
190
20. CLASSI DI COMPLESSITÀ E PRINCIPALI RISULTATI
In Figura 1 sono definite le importanti classi non deterministiche NP ed
NEXPTIME.
Uno dei problemi aperti più affascinanti dell’informatica teorica è collocare
insiemisticamente P e NP. Ovviamente vale:
Lemma 20.14. P ⊆ NP.
Proof. Se L ∈ P, allora è deciso da una k-MdT deterministica M in tempo
O(nh ) per qualche h ∈ N. Per il Teorema 20.6 sappiamo che esiste una 1-MdT M ′
che opera in tempo O(n2h ) equivalente a M. M ′ non è altro che una ND-MdT
nel cui insieme delle istruzioni vi è esattamente una quintupla in P per ogni coppia
q, s.
Non si sa ancora né se P = NP né se P 6= NP. Vale tuttavia il seguente:
S
Teorema 20.15. NTIME(f(n)) ⊆ c≥1 TIME(cf(n) ).
Proof. Sia L ∈ NTIME(f(n)) e M = hQ, Σ, q0 , Pi una ND-MdT che decide
L. Sia c il massimo grado di non determinismo di M, ovvero
c=
max
hq,si∈Q×Σ
|{hq, s, q ′ , s ′ , Mi ∈ P}|.
Si tiene un vettore di lunghezza f(n) che rappresenta un numero in base c di f(n)
caratteri inizializzato a [0, 0, . . . , 0]. Ad ogni passo di computazione il numero i ∈
{0, . . . , c − 1} indica quale delle scelte non deterministiche debba essere selezionata.
Si simula dunque la computazione che effettua, ad ogni passo, le scelte illustrate
dal vettore. Se alla fine si raggiunge yes, si accetta x. Altrimenti si incrementa il
vettore di 1 (in base c) e si riparte. Se dopo il tentativo con il vettore a [c − 1, c −
1, . . . , c − 1] non si è ancora raggiunto yes, si restituisce no.
Corollario 20.16. NP ⊆ EXPTIME.
4. Una inclusione stretta
In letteratura si trovano svariate classi di complessità che permettono di partizionare l’insieme dei linguaggi (insiemi) ricorsivi. Tuttavia, sono molto pochi i
risultati di inclusione stretta finora dimostrati. Uno di questi sarà presentato in
questo paragrafo.
Innanzitutto una premessa. Quali sono le funzioni che si prestano ad essere
funzioni
√ proprie per il calcolo della complessità? Sicuramente tra queste ci sono
log n, n, n, n2 , n3 , 2n , . . . . Non sembra accettabile una funzione f del tipo:
2n Se n è primo
f(n) =
Πn π
altrimenti
i=1 i
dove πi è l’i-esima cifra nell’espansione decimale di π.
Si può formalizzare la nozione di essere una buona funzione per la complessità.
4. UNA INCLUSIONE STRETTA
191
Definizione 20.17. f è una funzione propria di complessità se è debolmente
crescente (f(n + 1) ≥ f(n)) ed esiste una una macchina di Turing a k > 1 nastri
M tale che:
• non modifica mai il contenuto del primo nastro
• il k-esimo nastro è a sola scrittura e la testina in esso può solo (a) stare
ferma o (b) scrivere e spostarsi a destra.
•
(q0 , |ε, {z
✄, x} , |ε, {z
✄, ε} , . . . , |ε, {z
✄, ε} ) −→t
nastro 1 nastro 2
nastro k
j2
0jk−1} , |✄1f(|x|)
(h, |ε, {z
✄, x} , |ε, ✄,
{z0 } , . . . , |ε, ✄,{z
{z , $, ε})
nastro 1 nastro 2
nastro k−1
nastro k
• t = O(|x| + f(|x|)) e,
• per i = 2, . . . , k − 1, ji = O(f(|x|)).
Dunque una macchina di Turing per il calcolo delle funzioni proprie calcola f
in unario e non usa spazio inutile (in particolare è una I/O-k-MdT—si confronti
la Def. 20.23).
Sia f(n) ≥ n una funzione di complessità propria. Definiamo l’insieme Hf nel
modo seguente:
def
(4.1)
M; x : M accetta x in ≤ f(|x|) + 5|x| + 4 passi
Hf =
Hf è una versione ‘limitata’ dell’Halting problem.
Teorema 20.18. Hf ∈ TIME(f(n)3 ).
Idea della dimostrazione. Si costruisce una 4-MdT M che decide Hf . Si userà
la MdT universale, la macchina che permette lo speed-up per togliere le costanti
e la macchina per calcolare f che è propria.
A parte il limite cubico, per il quale è necessario sviluppare la dimostrazione
in dettaglio, è abbastanza facile convincersi della possibilità di realizzare una M
che decide Hf e che opera in tempo polinomialmente legata a f(n). E questo è
quello che veramente importa in questo contesto.
Teorema 20.19. Hf ∈
/ TIME(f(⌊ n
2 ⌋)).
Proof. Supponiamo per assurdo che esista MHf che decide Hf in tempo
f(⌊ n
2 ⌋). Allora possiamo costruire una macchina di Turing D con la seguente
funzionalità:
def
D = if MHf (M; M) = yes
then no
else yes
Quanti passi impiega D sull’input M? So che
|M; M|
= f(|M|)
MHf (M; M) = f
2
192
20. CLASSI DI COMPLESSITÀ E PRINCIPALI RISULTATI
D deve, partendo dall’input M, duplicarlo e aggiungere un ; in mezzo. Questo può
essere fatto in tempo 5|M| + 4. Inoltre va modificato P di MHf per terminare in
modo opposto. Dunque
(4.2)
D(M)
impiega tempo
≤ f(|M|) + 5|M| + 4
Cerchiamo dunque di giungere ad una contraddizione:
• Se D(D) = no allora MHf (D; D) = yes, ovvero D accetta D in ≤ f(|D|)+
5|D| + 4 passi. Dunque D(D) = yes: una contraddizione.
• Se D(D) = yes allora MHf (D; D) = no, ovvero D su D non termina in
≤ f(|D|) + 5|D| + 4 passi. Ma questo contraddice (4.2).
Corollario 20.20. Se f(n) ≥ n è una funzione di complessità propria, allora:
TIME(f(n)) ⊂ TIME(f(2n + 1)3 )
Corollario 20.21. P ⊂ EXPTIME.
Proof.
P ⊆ TIME(2n ) ⊂ TIME((2n )3 ) ⊆ EXPTIME
Dunque sappiamo che: P ⊆ NP ⊆ EXPTIME e che almeno una delle due
inclusioni è propria. Ma non sappiamo quale, né sappiamo se lo siano entrambe!
Se rimuoviamo l’ipotesi di essere propria, vale il seguente Teorema
Teorema 20.22 (GAP). Esiste f ricorsiva tale che TIME(f(n)) = TIME(2f(n) ).
Idea: si definisce f in modo tale che nessuna MdT su un input di lunghezza n
termini in un numero di passi compreso tra f(n) e 2f(n) .
5. Complessità in spazio
Sebbene il tempo di computazione sia considerato la risorsa più importante
da tenere bassa nello sviluppo di algoritmi (lo spazio, a differenza del tempo, può
essere riutilizzato), la quantità di memoria (nastro) utilizzata durante la computazione ci fornisce una misura interessante e meritevole di essere studiata. Per
studiare queste nozioni viene prima definito un ulteriore raffinamento del modello
k-MdT che permette di escludere, nel conteggio di tale misura, lo spazio necessario per l’input e l’output (input e output saranno infatti ugualmente presenti
in ogni k-MdT che risolve lo stesso problema e limitano dunque le possibilità di
discriminazione.
Definizione 20.23. Una macchina di Turing a k nastri e con input e output
(I/0-k-MdT ) M è una k-MdT con almeno due nastri. Il primo, che contiene
l’input, è un nastro a sola lettura. L’ultimo, che conterrà l’output, è a sola scrittura
e la testina in esso può solo (a) stare ferma o (b) scrivere e spostarsi a destra.
5. COMPLESSITÀ IN SPAZIO
193
Pertanto, se si volesse modificare il nastro che contiene l’input, sarebbe necessario prima ricopiarlo in un altro nastro e modificare quest’ultimo. Si osservi
che questo non è un nuovo modello di MdT ma semplicemente una restrizione al
formalismo della k-MdT definita in Def. 20.1.
Definizione 20.24 (Classi in spazio deterministico). Data una I/0-k-MdT M
e un input x, se
✄, ε} , . . . , |ε, {z
✄, ε} ) −→∗ (q, u1 , s1 , v1 , u2 , s2 , v2 , . . . , uk , sk , vk )
(qo , |ε, {z
✄, x} , |ε, {z
| {z }
| {z } | {z }
nastro 1 nastro 2
nastro k
nastro 1
nastro 2
nastro k
Pk−1
con q ∈ {h, yes, no} allora diremo che lo spazio richiesto da M per x è i=2 |ui | +
|vi | + 1.
M opera in spazio f(n) se per ogni input x lo spazio richiesto da M per x è
minore o uguale a f(|x|).
Se esiste una I/0-k-MdT M che decide un linguaggio L ⊆ Σ∗ e opera in spazio
f(n), allora L ∈ SPACE(f(n)).
Le classi di spazio deterministiche L e PSPACE sono definite in Figura 1.
Vediamo ora l’importante teorema che collega le classi deterministiche di
spazio e tempo:
Teorema 20.25. SPACE(f(n)) ⊆ TIME(2O(f(n)) ).
Proof. Sia L ∈ SPACE(f(n)) e sia M la I/O-k-MdT che decide L. Se durante
la computazione M si trovasse nella medesima configurazione (q, ℓ1 , s1 , r1 , . . . , ℓk , sk , rk )
per due volte, il determinismo garantirebbe che la situazione si ripeterebbe all’infinito
e dunque M non terminerebbe (assurdo). Il numero di configurazioni diverse
(l’ultimo nastro non conta in quanto non viene letto) è dell’ordine:
(Q + 3)
| {z }
stati
O(f(|x|)
Σ
| {z }
(|x| + 1)f(|x|)k
{z
}
|
stringhe sui nastri posizione testina
Corollario 20.26. L ⊆ P e PSPACE ⊆ EXPTIME.
Anche nel caso del non-determinismo si possono definire le classi in spazio.
Una I/0-k-MdT M è non deterministica se l’insieme P delle istruzioni descrive
una relazione anziché una funzione δ. Valgono dunque le stesse definizioni delle
ND-MdT e delle I/0-k-MdT.
Definizione 20.27 (Classi in spazio non deterministico). Una ND-I/0-k-MdT
M decide L ⊆ Σ∗ in spazio f(n) se M decide L e per ogni x ∈ Σ∗ , se
(qo , |ε, {z
✄, x} , |ε, {z
✄, ε} , . . . , |ε, {z
✄, ε} ) −→∗ (q, u1 , s1 , v1 , u2 , s2 , v2 , . . . , uk , sk , vk )
| {z }
| {z } | {z }
nastro 1 nastro 2
Pk−1
nastro k
allora i=2 |ui | + |vi | + 1 ≤ f(|x|).
In tal caso si dice che L ∈ NSPACE(f(n)).
nastro 1
nastro 2
nastro k
194
20. CLASSI DI COMPLESSITÀ E PRINCIPALI RISULTATI
L ⊆ NL ⊆ P ⊆ NP ⊆
|
PSPACE
NPSPACE
{z
⊂ stretta
⊆ EXPTIME ⊆ NEXPTIME
}
Figure 2. Classi in spazio e tempo presentate e loro posizioni reciproche
Si osservi che tale definizione permette che M che decide L in spazio f(n) sia
potenzialmente non terminante! Le classi NL e NPSPACE sono definite in Figura
1. Ovviamente vale:
Teorema 20.28. SPACE(f(n)) ⊆ NSPACE(f(n))
Pertanto si avrà:
Corollario 20.29. NL ⊆ L e PSPACE ⊆ NPSPACE.
Tuttavia vale il risultato (di cui si omette del tutto la dimostrazione):
Teorema 20.30 (Savitch). Se f(n) ≥ n ed è una funzione propria di complessità, allora NSPACE(f(n)) ⊆ SPACE(f(n)2 ).
Come corollario, si ottiene il risultato:
Corollario 20.31. NPSPACE = PSPACE
In realtà un sospetto su questo risultato lo si ha simulando una ND-MdT
mediante una MdT deterministica (Teorema 20.15). Prima di iniziare ciascuna
delle possibili (esponenziali in numero) simulazioni si può riportare prima le testine
nella configurazione iniziale. Si userà cosı̀ lo stesso spazio richiesto dalla ND-MdT
di partenza. Similmente, si può dimostrare:
Teorema 20.32. NP ⊆ PSPACE.
Il riassunto delle inclusioni discusse in questo capitolo si trova in Figura 2.
CHAPTER 21
Riduzioni e NP-completezza
Nel capitolo 19 è stata introdotta la riducibilità funzionale tra insiemi. Ridurre
A a B mediante una funzione ricorsiva f ci permetteva di dire che A non poteva
essere più difficile di B. In questo capitolo si rafforza la nozione di riduzione
aumentando le richieste per la funzione f. f deve essere non solo ricorsiva, ma
calcolabile in modo semplice rispetto alla complessità degli insiemi A e B che si
vuole correlare. In questo modo potremmo dire che sapendo calcolare B efficientemente, la proprietà positiva ricade su A. Viceversa, che se la decisione di A
richiede algoritmi inefficienti, lo stesso varrà per B.
1. Riduzioni tra problemi
Definizione 21.1. Dati due linguaggi L1 e L2 si dice che L1 ⊆ Σ∗1 è riducibile
a L2 ⊆ Σ∗2 (in breve, L1 L2 ) se esiste una funzione f : Σ∗1 −→ Σ∗2 tale che:
• f è calcolabile da una I/O-k-MdT (deterministica) in spazio O(log n) e
• per ogni x ∈ Σ∗1 si ha che:
x ∈ L1 sse f(x) ∈ L2
f è chiamata una riduzione da L1 a L2 .
In letteratura si può trovare la richiesta per f di essere calcolabile polinomialmente. Seguendo [23] facciamo una richiesta più forte (si ricorda che: L ⊆ P).
Qual’è il senso di questa scelta? Similmente a quanto fatto per la riducibilità usando funzioni ricorsive, si vorrebbe avere che L2 ∈ C e L1 L2 implica che L1 ∈ C.
Se ciò è vero, allora C è detta chiusa per riduzione.
Si consideri il seguente esempio:
• y ∈ L2 si decide in tempo |y|2 (ovvero L2 ∈ P)
• f riduce L1 a L2 nel senso che x ∈ L1 sse f(x) ∈ L2 , ma
• f si calcola in tempo 2|x|
Malgrado ci sia la riduzione non possiamo usarla per dire qualcosa sulla classe di
complessità di L1 . Pertanto la funzione di riduzione deve poter essere calcolata in
modo semplice rispetto alla complessità di L2 . La scelta della complessità—spazio
O(log n)—permette di usarla per tutte le classi da noi introdotte in Fig. 1: esse
sono tutte chiuse per riduzione.
Anche per questo tipo di riduzioni si può introdurre il concetto di completezza:
195
196
21. RIDUZIONI E NP-COMPLETEZZA
Definizione 21.2. Data una classe C, un problema L è C-completo se ogni
problema della classe può essere ridotto a L.
Quando si riducono problemi in P o in classi che contengono P, si può tranquillamente usare la richiesta più debole che f sia calcolabile in tempo polinomiale,
che alle volte è più semplice da dimostrare.
Vedremo subito un esempio di riduzione:
Definizione 21.3 (SAT). Input: Una formula logica proposizionale ϕ data
come congiunzione di disgiunzioni (clausole) di letterali, costruita su un insieme
di variabili V, ovvero:
m
ϕ ≡ (ℓ11 ∨ · · · ∨ ℓ1n1 ) ∧ · · · ∧ (ℓm
1 ∨ · · · ∨ ℓnm )
ove ogni ℓij è o una variabile X ∈ V o la negazione di una variabile ¬X con X ∈ V.
Problema: Esiste un assegnamento per le variabili in V che renda vera la formula?
SAT sta, ovviamente, per SATisfiability.
Definizione 21.4 (HP). Input: Un grafo (non diretto) G = hN, Ei.
Problema: Esiste un cammino che visita ogni nodo esattamente una volta?
HP sta per Hamilton Path. In entrambi i casi, come visto, la dimensione
dell’istanza del problema è la lunghezza dela stringa che descrive l’input.
Lemma 21.5. HP SAT .
Proof. Dato un grafo G = hN, Ei, costruiremo una formula f(G) su un insieme di variabili V. Assumiamo, per semplicità notazionale, che N = {1, . . . , n}.
• Definiamo V = {Xij : 1 ≤ i, j ≤ n}. Informalmente, Xij ha il significato: il
nodo j è l’i-esimo nodo del cammino Hamiltoniano (proprietà Booleana).
• Descriviamo ora le varie clausole di f(G) con cui vogliamo modellare il
problema del cammino Hamiltoniano:
– Prima di tutto, ogni nodo j deve stare nel cammino, anche se non
sappiamo in quale posizione. Questo può essere espresso come:
def
φj = X1j ∨ X2j ∨ · · · ∨ Xnj
– Ogni nodo j non può apparire sia come i-esimo nodo del cammino
che come k-esimo, con i 6= k. Questo si può esprimere con la clausola:
def
ψj (i, k) = ¬Xij ∨ ¬Xkj
– Inoltre, per ogni i = 1, . . . , n vi dev’essere un nodo che sia l’i-esimo
del cammino:
def
ηi = Xi1 ∨ · · · ∨ Xin
2. I TEOREMI DI COOK
197
– Per ogni coppia (i, j) tale che {i, j} che non sia in E devo escludere la
possibilità che i sia successore di j nel cammino o viceversa. Questo
può essere espresso ripetendo, per k = 1, . . . , n − 1:1
def
ρij (k) = ¬Xki ∨ ¬Xk+1j
• Possiamo dunque definire f(G):
V
V
def
f(G) =
i,j,k=1,...,n:i6=k ψj (i, k)∧
j=1,...,n φj ∧
V
V
i,j,k=1,...,n:k6=n,{i,j}∈E
/ ρij (k)
i=1,...,n ηi ∧
Per costruzione, f trasforma un’istanza di HP (ovvero un grafo) in un’istanza di
SAT .
(1) Dobbiamo mostrare che f si calcola in spazio logaritmico. L’idea della
dimostrazione è la seguente: sostanzialmente si tratta di eseguire dei
cicli for (annidati) ma limitati da n = O(|G|). n si rappresenta in spazio
logaritmico rispetto al suo valore.
(2) Dobbiamo inoltre mostrare che se π (una permutazione di {1, . . . , n}) è
un cammino Hamiltoniano in G, allora l’assegnamento che corrisponde
a π rende vera la formula f(G). Questo è immediato per costruzione di
f(G).
(3) Viceversa, se π non lo è, dobbiamo mostrare che l’assegnamento non
rende vera la formula. Equivalentemente, mostriamo che se θ è un assegnamento che rende vera la formula, da esso si desume un cammino Hamiltoniano per G. Ogni assegnamento θ che renda vere le tre prime famiglie
di clausole garantisce che per ∀j∃!i.Xij = true e ∀i∃!j.Xij = true.
Dunque θ descrive una permutazione. L’ultima famiglia di clausole
garantisce che tra nodi successivi nella permutazione vi sia un arco.
Avendo ridotto HP a SAT via f, quello che si può dire è che HP non può
essere più difficile di SAT . Infatti, per stabilire se x ∈ HP effettuo la riduzione (in
spazio logaritmico e dunque tempo polinomiale) e mi riduco cosı̀ al problema di
appartenenza a SAT .
2. I Teoremi di Cook
Studieremo ora l’esistenza di problemi completi per la classe P e per la classe
NP.
Definizione 21.6 (CIRCUIT VALUE). Input: Un circuito logico con porte
and, or, e not con una sola uscita e i valori per gli ingressi.
Problema: Stabilire se l’output è true.
1 Per
una dimostrazione analoga relativa al cammino Hamiltoniano nei grafi diretti questo
è l’unico punto da modificarsi leggermente (Esercizio).
198
21. RIDUZIONI E NP-COMPLETEZZA
Definizione 21.7 (CIRCUIT SAT). Input: Un circuito logico con porte and,
or, e not con una sola uscita e n porte di ingresso.
Problema: Stabilire se esiste un assegnamento per le porte di ingresso che rende
l’output true.
Teorema 21.8. CIRCUIT VALUE è P-completo.
Proof. Innanzitutto va osservato che CIRCUIT VALUE sta in P. Dati i
valori degli input, si percorre il circuito (aciclico) valutando gli output di ogni porta
in funzione dei suoi input. Ciò può ovviamente essere fatto in tempo polinomiale.
Mostriamo ora che è P-completo, riducendo ogni problema L in P a lui. Sia
L ∈ P. Allora esiste una 1-MdT M che decide L ⊆ Σ∗ e che opera in tempo
p(n) con p(n) un polinomio in n. Dobbiamo mostrare che esiste una f tale che,
dato x ∈ Σ∗ , mi permette di ottenere un circuito f(x) tale che f(x) ha output
true se e solo se x ∈ L. Il circuito lo si ottiene dalla forma delle computazioni
di M. In al più p(|x|) passi M termina su |x|. Sia k = p(|x|) + 1: al più k celle
del nastro possono essere utilizzate. Simuliamo la computazione con una matrice
k × k in cui ogni riga i rappresenta la configurazione dopo l’i-esimo passo. La
cella j rappresenta dunque il simbolo presente sul nastro oppure un simbolo che
identifica che la testina si trova nello stato q e legge il simbolo s (una sola cella
per riga avrà memorizzato questa informazione aggiuntiva). Assumiamo, senza
perdita di generalità, che M termini sempre a destra del ✄ iniziale. Inoltre, se M
termina prima di p(|x|) passi, si ricopia l’ultima configurazione. Sia x = a1 . . . an .
(✄, q0 )
a1
a2
...
an
$
...
$
✄
..
.
(a1 , q1 )
..
.
a2
..
.
...
..
.
an
..
.
$
..
.
...
..
.
$
..
.
✄
(y1 , yes/no)
y2
...
yn
yn+1
...
yk
Quello che si osserva è che ogni elemento Mi,j può essere calcolato a partire
dalla matrice di transizione di M e dai valori di sole tre celle: Mi−1,j−1 , Mi−1,j , Mi−1,j+1 .
A partire da una codifica binaria dei simboli di Σ e di Σ × Q è facile scrivere le
funzioni booleane che calcolano la codifica del contenuto della cella Mi,j .
Il tutto viene poi composto per ottenere un circuito che simula l’evoluzione
della tabella per righe, ovvero la computazione di M. L’output si otterrà dalla
cella Mk,2 con un circuito che restituisce 1 se (y1 , yes), 0 altrimenti.
Teorema 21.9. CIRCUIT SAT è NP-completo.
Proof. Bisogna innanzitutto mostrare che CIRCUIT SAT sta in NP. Scrivere una ND-MdT che risolve SAT è semplice. In una prima fase si aprono le
strade non deterministiche per assegnare i possibili valori per le variabili di input.
A questo punto in ogni strada non deterministica ci troviamo di fronte ad una
istanza di CIRCUIT VALUE che è in P.
3. PROBLEMI NP-COMPLETI
199
La dimostrazione di NP-completezza ricalca quella di P-completezza di CIRCUIT VALUE. Sia L ∈ NP. Assumiamo senza perdita di generalità che L sia
deciso da una ND-k-MdT che opera in tempo p(n) e tale che il suo grado di non
determinismo sia 2. Si ripete il ragionamento sopra solo che in ogni porta costruita
si aggiunge un input che tiene conto del grado di non-determinismo. La lista di
tali variabili di input è l’input di CIRCUIT SAT.
Teorema 21.10. SAT è NP-completo.
Proof. La dimostrazione che SAT ∈ NP è identica a quella per CIRCUIT
SAT, vista nel Teorema 21.9.
Per concludere la dimostrazione, riduciamo CIRCUIT SAT a SAT. Dato un
circuito G, si tratta di trovare una formula logica in forma clausale equivalente al
circuito dato. Grazie ai teoremi di De Morgan, possiamo assumere che il circuito
abbia solo porte or e not. Vi sono tre tipi di nodi; ogni nodo or ha 2 archi
entranti, ogni nodo not ha 1 arco entrante, mentre ogni nodo di input ha 0 archi
entranti. Assegnamo una variabile di output ad ogni nodo del circuito.
or: la variabile Y di output rappresenta una funzione Y ↔ A ∨ B ove A e B
sono le variabili di output dei due nodi da cui partono gli archi entranti.
Y ↔ A ∨ B è equivalente a:
(¬Y ∨ A ∨ B) ∧ (¬A ∨ Y) ∧ (¬B ∨ Y)
not: la variabile Y di output rappresenta una funzione Y ↔ ¬A ove A è
la variabile di output del nodo da cui parte l’arco entrante: Y ↔ ¬A è
equivalente a:
(¬Y ∨ A) ∧ (Y ∨ ¬A)
input: la variabile di output è la stessa dell’input.
La congiunzione delle formule ottenute è equisoddisfacibile al circuito finale. La
riduzione si può fare in spazio log n (guardo i nodi uno ad uno, dobbiamo solo scrivere temporaneamente degli indici che vanno da 1 a |G| e che dunque si rappresenta
in spazio log |G|).
3. Problemi NP-completi
La gran parte dei problemi pratici che un informatico deve affrontare (problemi su grafi, scheduling, problemi di logica) presenta molte caratteristiche simili
a quelle di SAT, che riassumiamo: facilità di verifica di una soluzione; ampiezza
esponenziale dello spazio di ricerca della stessa (delle stesse). In base al Teorema 21.10 e al fatto che la classe NP è chiusa per riduzione, è possibile cercare
di dimostrare formalmente la NP-completezza di un problema L che ci sembra
appartenere a questa famiglia. Per far ciò si tratta di dimostrare che:
(1) L ∈ NP; e
(2) SAT L.
200
21. RIDUZIONI E NP-COMPLETEZZA
Una volta mostrato che L è NP-completo, si potrà utilizzarlo, come si è fatto per
SAT nel punto (2), per dimostrare la NP-completezza di altri problemi.
Per quanto riguarda l’appartenenza a NP, si può utilizzare la seguente definizione
alternativa (ed equivalente): L ∈ NP se per ogni x ∈ L esiste una certificazione
‘succinta’ (un testimone polinomiale) di questa proprietà che può essere verificata
in tempo polinomiale. Nel caso di SAT, il testimone è un assegnamento di verità.
Nel caso di HP, è proprio il cammino. In entrambi i casi la dimensione del testimone è succinta, nel senso che è di dimensioni polinomiali rispetto all’input. La
verifica che si tratti di una soluzione è parimenti polinomiale. Ovviamente quello
che si cerca di fare è di rendere esplicita la parte di non-determinismo necessaria
per la definizione di una MdT che decida non deterministicamente il linguaggio:
in modo non deterministico va generato il testimone.
Per quanto riguarda la proprietà di riduzione, bisogna invece avere sufficiente
fantasia/esercizio. Questa proprietà (ovvero che esiste un NP-completo riducibile
a lui) è detta NP-hardness.
Vedremo ora alcune riduzioni famose, alcuni problemi NP-completi fondamentali.
3.1. Varianti di SAT.
Definizione 21.11 (3SAT). Input: Istanze di SAT in cui ogni clausola consta
di esattamente 3 elementi.
Problema: Come per SAT, stabilire se esiste un assegnamento che rende vera la
formula.
Teorema 21.12. 3SAT è NP-completo.
Proof. L’appartenenza ad NP si mostra in questo caso banalmente in quanto
ogni istanza di 3SAT è anche istanza di SAT.
Mostriamo ora che SAT 3SAT . Sia x input di SAT. Traduciamo ogni
clausola ℓ1 ∨ · · · ∨ ℓm di x in una equisoddisfacibile clausola di 3SAT.
• Se m = 1: la trasformo in ℓ1 ∨ ℓ1 ∨ ℓ1
• Se m = 2: la trasformo in ℓ1 ∨ ℓ2 ∨ ℓ2
• Se m = 3 non devo fare nulla.
• Se m > 3, introduco una nuova variabile X: restituisco la clausola ℓ1 ∨
ℓ2 ∨ X e riapplico la riscrittura alla clausola ¬X ∨ ℓ3 ∨ · · · ∨ ℓm .
Definizione 21.13 (NAESAT). Input: Tutti gli input di SAT.
Problema: Stabilire se esiste un assegnamento delle variabili di input che renda
vera la formula e tale che in ogni clausola non tutti i letterali sono a true.
In altri termini, non ci vanno bene gli assegnamenti che mettono tutti i letterali
di almeno una clausola a false o tutti a true: da cui il nome Not All Equal.
Teorema 21.14. NAESAT è NP-completo.
3. PROBLEMI NP-COMPLETI
201
Proof. Come per SAT, l’appartenenza a NP si può mostrare usando la
definizione Alternativa fornita in questa sezione. Fornito l’input e un assegnamento, la verifica di ‘bontà’ di quell’assegnamento è chiaramente polinomiale.
Per mostrare la NP-hardness in modo semplice, basta modificare appena la
dimostrazione del Teorema 21.8. Sia Z un’unica variabile aggiuntiva. Y ↔ A ∨ B
viene trasformata in:
(¬Y ∨ A ∨ B) ∧ (¬A ∨ Y ∨ Z) ∧ (¬B ∨ Y ∨ Z)
mentre Y ↔ ¬A viene trasformata in:
(¬Y ∨ A ∨ Z) ∧ (Y ∨ ¬A ∨ Z)
Sia σ che soddisfa l’istanza di CIRCUIT SAT. Consideriamo la traduzione non
estesa con Z.
Supponiamo, per assurdo, che nella prima clausola dell’or tutti i tre letterali
siano true. Allora la seconda e la terza non sarebbero soddisfatte. Dunque σ
non rende veri tutti e tre i letterali delle prime clausole dell’or. Ma allora con
σ ′ = σ ◦ [Z/false] si ha una soluzione per NAESAT.
Viceversa, sia σ una soluzione per NAESAT. E’ immediato verificare che σ è
una soluzione sse σ̄ lo è. In una delle due Z sarà true: quella sarà la soluzione
per CIRCUIT SAT.
La versione più estrema di SAT, ove ogni clausola ha esattamente 2 letterali
e nota come 2SAT, è invece polinomiale. Si provi per esercizio questa proprietà.
3.2. Problemi su Grafi. Dato un grafo non diretto G = hN, Ei e un insieme
I ⊆ N, si dice che I è un insieme indipendente (Independent Set) per G se per ogni
i, j ∈ I non vi è in E l’arco {i, j}.
Definizione 21.15 (IS). Input: Un grafo non diretto G = hN, Ei e un intero
k.
Problema: Stabilire se esiste un insieme indipendente I per G tale che |I| = k.
Teorema 21.16. IS è NP-completo.
Proof. L’appartenenza a NP è immediata.
Mostriamo che 3SAT IS. La dimostrazione è semplice. Tuttavia, per non
renderla troppo complicata da apici e pedici annidati, mostriamo la riduzione su
una istanza. Sia
ϕ = (X1 ∨ X2 ∨ ¬X3 ) ∧ (X1 ∨ ¬X2 ∨ X4 ) ∧ (¬X1 ∨ X4 ∨ X5 ) ∧ (¬X1 ∨ ¬X4 ∨ ¬X5 )
Per ogni clausola costruisco un sottografo completo triangolare A ogni nodo associo (intuitivamente) un letterale (ad esempio, per il primo X1 , X2 , e ¬X3 . Avremo
tanti triangoli quante sono le clausole in gioco (in questo caso 4), come raffigurato
in Fig. 1. Si aggiungano poi degli archi tra tutti (e solo tra quelli) i nodi che rappresentano letterali complementari. Ebbene, ϕ è soddisfacibile se e solo se il grafo ottenuto ammette un insieme indipendente di 4 (in generale, il numero delle clausole)
202
21. RIDUZIONI E NP-COMPLETEZZA
✓✏
¬X1
s
X1 s
✁❅
❆
✁❆
✒✑
✁ ❆❅
✁ ❆
✁
❆ ❅
✁
❆
✓✏
s
s
✁
❆
s
✁
❆s X5
❅
X2
❅
✒✑
X
X3
4
❅
❅
✓✏
✓✏ X
4s
s ¬X4
s ¬X5
❅
¬X2 s
❆
✁
❆
✁
❅✒✑
✒✑
❆
✁
✁
❅ ❆
❆ ✁
❅❆ ✁
❆s✁
❅❆s✁
X1
¬X1
Figure 1. Grafo usato per la riduzione 3SAT IS
elementi. I nodi cerchiati in figura costituiscono un insieme indipendente di 4 nodi
e forniscono una soluzione per ϕ: X1 = true, X2 = false, X4 = false, X5 = true.
Il valore di X3 non ha importanza.
Elenchiamo altri problemi NP-completi molto noti.
Definizione 21.17 (CLIQUE). Input: Un grafo non diretto G = hN, Ei e un
intero k.
Problema: Stabilire se esiste un sottografo di G di k elementi che sia un grafo
completo.
Si osservi che CLIQUE su G = hN, Ei corrisponde a IS su G ′ = hN, (N×N)\Ei
Definizione 21.18 (NODE COVER). Input: Un grafo non diretto G =
hN, Ei e un intero k.
Problema: Stabilire se esiste un insieme di nodi C ⊆ N di k elementi tale che
per ogni arco {i, j} di E i ∈ C o j ∈ C.
Si osservi che I è un IS di G = hN, Ei se e solo se C = N \ I è un NODE
COVER di G.
Esercizio 21.19. Si formalizzi la dimostrazione di NP-completezza di CLIQUE
e NODE COVER.
Anche il problema HP già discusso in precedenza è NP-completo (difficile
riduzione da 3SAT—omessa). Vediamo ora la versione decisionale del noto problema del commesso viaggiatore (Traveling Salesman Problem).
3. PROBLEMI NP-COMPLETI
203
Definizione 21.20 (TSP(k)). Input: Un grafo non diretto G = hN, Ei, una
funzione di costi c : N −→ N, e un intero k.
Problema: Stabilire se esiste un circuito che passa esattamente una volta per
ogni nodo e di costo complessivo ≤ k.
Esercizio 21.21. Assumendo la NP-completezza di HP, si formalizzi la dimostrazione di NP-completezza di TSP(k).
Definizione 21.22 (k-COLORING). Input: Un grafo non diretto G = hN, Ei
e un intero k.
Problema: Stabilire se esiste un assegnamento col : N −→ {1, . . . , k} tale che per
ogni arco {i, j} di E col(i) 6= col(j).
Per k = 2 il problema sta in P (verificare per esercizio). Per k = 4 e grafi
planari, il problema ha sempre soluzione. Per grafi qualunque, invece:
Teorema 21.23. 3-coloring è NP-completo.
Proof. L’appartenenza a NP è immediata.
Riduciamo NAESAT a 3-coloring. Come nella dimostrazione del Teorema
21.16, mostriamo la riduzione su un esempio. Sia
ϕ = (X1 ∨ X2 ∨ ¬X3 ) ∧ (X1 ∨ ¬X2 ∨ ¬X3 ) ∧ (¬X1 ∨ X2 ∨ ¬X3 ) ∧ (¬X1 ∨ ¬X2 ∨ X3 )
Costruiamo un grafo nel seguente nodo. Sia µ un nodo. Per ogni variabile vengono
introdotti due nodi (uno per Xi uno per ¬Xi ). Si inseriscono archi per avere i
triangoli µ, Xi , ¬Xi . Il nodo µ è comune a tutti i triangoli.
Per ogni clausola vengono introdotti tre nodi C1 , C2 , C3 ed archi tra loro per
ottenere un triangolo per ogni clausola. Ogni nodo è associato ad uno dei letterali
della clausola.
Si aggiungono dunque degli archi tra tutti (e solo tra quelli) i nodi dei triangoli
sopra i nodi relativi ai letterali corrispondenti.
Si mostra che σ è una soluzione di ϕ, allora la funzione che mette il colore 0 ai
nodi dei triangoli variabile corrispondenti a false, il colore 1 a quelli corrispondenti
a true e il colore 2 al nodo µ e a un nodo per ogni triangolo clausola è un 3-coloring.
Viceversa, se non esiste un 3-coloring, non esiste un assegnamento di verità per
σ.
Esercizio 21.24. Si consideri il seguente problema ST (k): Siano dati un grafo
non diretto G = hN, Ei e un intero k ∈ N. Si vuol sapere se esiste uno SPANNING
TREE per G tale che nessun nodo dell’albero abbia grado maggiore di k. In altri
termini, se esiste E ′ ⊆ E t.c.:
• |E ′ | = |N| − 1,
• il grafo hN, E ′ i è connesso, e
• nessun nodo di N è incluso in più di k archi di E ′ .
Si mostri che ST (k) è NP-completo.
204
21. RIDUZIONI E NP-COMPLETEZZA
Esercizio 21.25. Si dimostri la NP-completezza del problema del cruciverba,
ovvero, dato un insieme di parole p1 , . . . , pk in un dato alfabeto A (N.B. potrebbe
essere un alfabeto con più delle 26 lettere che usiamo di solito!) e uno schema
di cruciverba C (una matrice rettangolare di caselle bianche e nere) con le caselle
nere già fissate, stabilire se esiste un modo per scrivere le k parole nello schema,
con le regole di incrocio standard dei cruciverba, che riempia esattamente tutte le
caselle bianche.
Per l’esercizio sopra può far comodo conoscere ed utilizzare il seguente problema, che è NP-completo:
Exact Cover by 3 sets: Dato un insieme U = {1, . . . , 3 · m}, e dati n insiemi
S1 , . . . , Sn ⊆ U, con |S1 | = |S2 | = · · · = |Sn | = 3 stabilire se esistono m insiemi
(disgiunti) tra S1 , . . . , Sn tali che la loro unione sia U.
Esercizio 21.26. Si consideri il problema che chiamiamo ZSAT:
Input: Una congiunzione di clausole C1 ∧ · · · ∧ Cn ove ogni Ci è una disgiunzione
di esattamente i letterali (non necessariamente diversi tra loro).
Problema: Stabilire se esiste un assegnamento di verità per le variabili che compaiono nella formula che rende l’istanza vera.
Si dimostri che ZSAT è NP-completo.
Esercizio 21.27. Si consideri il problema della fila tra detenuti (FTD): vi sono
n detenuti ed è nota una relazione x odia y tra coppie di detenuti. Il problema è:
esiste un modo per mettere in fila gli n detenuti in modo tale che non vi sia mai
un detenuto con uno che lo odia alle spalle?
Si mostri che il problema FTD è NP-completo.
Bibliography
[1] P. Aczel. Non-well-founded sets, volume 14 of CSLI Lecture Notes. Stanford University
Press, 1988.
[2] M. Aiello, A. Albano, G. Attardi, and U. Montanari. Teoria della Computabilità, logica,
teoria dei linguaggi formali. Materiali didattici ETS, Pisa, 1976.
[3] H. P. Barendregt. The Lambda Calculus, volume 103 of Studies in Logic and the Foundations
of Mathematics. Elsevier North-Holland, 1984.
[4] A. Church. A note on the Entscheidungsproblem. J. of Symbolic Logic, 1:40–41, 1936.
[5] S. A. Cook. The Complexity of Theorem Proving Procedures. In Proceedings of ACM Symposium on the Theory of Computing, pages 151–158. ACM Press, 1971.
[6] N. J. Cutland. An Introduction to Recursive Function Theory. Cambridge University Press,
1980.
[7] H. B. Enderton. A mathematical introduction to logic. Academic Press, 1973. 2nd printing.
[8] G. Frege. —. In J. Van Heijenoort, editor, From Frege to Gödel: A Source Book in Mathematical Logic, 1879-1931. Harvard Univ Press, 2001.
[9] M. R. Garey and D. S. Johnson. Computers and Intractability – A Guide to the Theory of
NP-Completeness. W. H. Freeman and Company, New York, 1979.
[10] K. Gödel. —. In Solomon Feferman, Stephen C. Kleene, John W. Dawson, and Robert M.
Solovay, editors, Kurt Gödel Collected Works: Publications 1929-1936, volume 1. Oxford
University Press, 1999.
[11] C. Hankin. Lambda Calculi: A Guide for Computer Scientists, volume 3 of Graduate Texts
in Computer Science. Springer-Verlag, 1996.
[12] M. Hirvensalo. Quantum Computing. Springer-Verlag, 2001.
[13] J. E. Hopcroft and J. D. Ullman. Introduction to Automata Theory, Languages and Computation. Addison-Wesley, 1979.
[14] N. D. Jones and Y. E. Lien. New problems complete for nondeterministic log space. Mathematical Systems Theory, 10:1–17, 1976.
[15] N.D. Jones. Computability and Complexity. MIT Press, 1997.
[16] W. Just and M. Weese. Discovering modern set theory. I: The basics, volume 8 of Graduate
studies in mathematics. American mathematical society, 1996.
[17] S. C. Kleene. Introduction to Metamathematics. North-Holland, 1996.
[18] K. Kunen. Set Theory. An Introduction to Independence Proofs. Studies in Logic. North
Holland, Amsterdam, 1980.
[19] G. Longo. Metodi per il trattamento dell’informazione. Servizio Editoriale Universitario di
Pisa, 1984.
[20] E. Mendelson. Introduction to Mathematical Logic. Van Nostrand, Princeton, N. J., 1979.
[21] J. C. Mitchell. Foundations for Programming Languages. Foundations of Computing. MIT
Press, 1996.
[22] P. Odifreddi. Classical Recursion Theory. Studies in Logic and the Foundations of Mathematics. North-Holland, 1989.
[23] C. H. Papadimitriou. Computational complexity. Addison-Wesley, 1994.
205
206
BIBLIOGRAPHY
[24] G. Paun, G. Rozenberg, A. Salomaa, and W. Brauer. DNA Computing: New Computing
Paradigms. Texts in Theoretical Computer Science. Springer-Verlag, 1998.
[25] R. Péter. Rekursive funktionen. Akadémiai Kiadó, Budapest, 1953.
[26] G. Plotkin. A structural approach to operational semantics. DAIMI-19 Aarhus University,
Denmark, 1981.
[27] H. J. Rogers. Theory of Recursive Functions and Effective Computability. The MIT Press,
1988.
[28] J.R. Shoenfield. Axioms of set theory. In J. Barwise, editor, Handbook of Mathematical
Logic, pages 321–344. North-Holland, Amsterdam, 1977.
[29] M. Sipser. The History and Status of the P versus NP Question. In Proceedings of ACM
Symposium on the Theory of Computing, pages 603–618. ACM Press, 1992.
[30] A. M. Turing. On computable numbers with an application to the Entscheidungsproblem.
Proc. of the London Math. Society, 42(2):230–265, 1936-7.
[31] G. Winskel. The formal semantics of programming languages: an introduction. MIT press,
1993.