Come vi dicevo, oggi è una lezione molto
importante, proprio molto importante,
perché introdurremo
il concetto di riduzione, che è un
concetto che useremo per il resto del
corso, ok? Quindi è una cosa che va
proprio capita,
quindi oggi ci prendiamo il tempo,
facciamo le cose piano perché è una cosa
che vi deve essere proprio cristal clear
perché sennò diventa incase.
Iniziamo con una metafora, con una
storiella, una metafora. Vi faccio
capire che si intende per riduzione, poi
e poi la definiamo formalmente, poi
laeremo un po' per un po' di teoremi,
per un po' di teoremi e
vediamo un po' come si fa. Poi
ovviamente vedremo le esercizi, tutta
una serie di cose, ma gran parte dei
risultati che terremo da da qui al resto
del corso si aggiungono tramite
rifuzioni. Ok? Allora, perché
introduciamo questo concetto? Perché
avrete notato dalla lezione di ieri, la
lezione di ieri com'era? Intrigata?
Avete un glimpse di di come è stato
organizzato. Ok? Allora, sostanzialmente
noi abbiamo che per la definizione dei
concetti di linguaggio ricorsivamente
numerabile linguaggio ricorsivo, noi
abbiamo che se dobbiamo mostrare che il
linguaggio è in uno di quelli insieme
dobbiamo mostrare un algoritmo, un
algoritmo di R o un algoritmo di R,
dipende da che mostrare. Se dobbiamo
mostrare che invece un linguaggio non
sta in quegli insiemi, quindi dobbiamo
dire non esiste alcuna macchina che
ecco, ovviamente noi possiamo andare là
a contare dire questa no, questa no,
questa no. Ci inventare altro. Allora,
abbiamo fatto varie dimostrazioni ieri,
ma avevamo tutte più o meno lo stesso
flavor, quelle là in cui escludevano un
linguaggio da una classe. Che che
principio avevano? facevamo
un'assunzione per assurdo, diceva
supponiamo che questa cosa sia
decidibile. Dopodiché costruivamo
un'altra bella macchinina, infilavamo
dentro questa macchinina la macchina
assunta esistere e succedeva un casino.
Ok? Quindi questo era in genere
l'approccio che utilizzavamo per
dimostrare che i linguaggi non stavano
in R o non stavano in R. Ok?
Questo qui erano delle istanziazioni,
c'erano dei casi particolari di un
principio molto più generale che diciamo
stamattina che è il principio della
riduzione fra linguaggi. Ok?
E una volta che acquisiremo questo
strumento, praticamente tutte le
dimostrazioni in cui dovremo mostrare
che un linguaggio non appartiene a una
certa classe verranno fatte tramite
riduzione perché è più facile. Ok?
Allora, è più facile da un lato,
non completamente. Ecco perché oggi
andiamo piano e cerchiamo di capire che
se sia un concetto chiaro per tutti.
Perché il concetto di riduzione ho
potuto vedere negli anni che è una cosa
che è un po' più difficile da acquisire,
ma non perché il concetto di riduzione
sia intrinsicamente complicato, ma
perché il concetto di riduzione è fatto
di vari elementi, cioè per riuscire a
utilizzarlo dobbiamo stare attenti a una
serie di cose che diventa facile
perdersi per strada. Tutto qua. Però se
ci diamo una checklist le cose le
dobbiamo vedere, ok? Quando si fa una
riduzione dobbiamo controllare questo,
questo, questo, questo e quest'altro.
Allora, spero e immagino che sia che sia
che sia fattibile, ok? È veramente
comune, è veramente comune capirlo male,
ok? Però cercheremo di di farlo. Eh,
spesso nei nei nei nei compiti di esame
vedo che le riduzioni sono fatte male.
Ok?
E perché? Perché è abbastanza naturale
che uno non acchiappi alcuni elementi
della riduzione. Io stesso quando ero
uno studente di informatica teorica
all'esame fa riduzione sbagliata.
Professore, ma la riduzione qui? Ah, è
giusto. Lì imparai, lì capì, no? Quando
ero friggevo su quella sedia ho detto
"Ah, la riduzione così si fa e che
cavolo". A capirlo prima. Ok? Quindi
cerchiamo di capire per bene assieme
cos'è questo concetto. E
per affrontarlo
per affrontarlo partiamo da la prendiamo
proprio alla larga, eh, partiamo da una
storiella.
Quando ero studente,
a un certo punto stiamo facendo che era
il terzo anno, mi pare, una serie di una
serie di esami tra cui gesteva
ah l'esame di web services. È un esame
di algoritmica un po' avanzata, design
patterns ste cose così, no? E per
l'esame di eh web services loro volevano
un progetto da annare su un server
applicativo e ognuno si poteva scegliere
che volevamo fare e io col mio team
c'eravamo scelti di fare questa
applicazione su web che era la gestione
dei tornei di calcio di calcetto, ok?
Quindi gli utenti si registravano a
questo sito, potevano registrare se
stessi, la propria squadra. Dopodiché
una volta che erano
iscritte le squadre al sistema, questo
sistema doveva generare il calendario in
maniera automatica. Ok? È chiaro per
tutti il concetto del calendario di un
torneo? Che cos'è? Ok, allora va bene,
ci serve sto cavolo d'algoritmo, no?
Ehm, pensa che ti ripensa e non ci
tornava facile riuscire a generare sto
cavolo di sto cavolo di calendario,
perché il problema qual era? Avevamo
difficoltà a infilare
a essere sicuri che nella stessa
giornata non finissero partite
incompatibili, cioè che una stessa
squadra non poteva giocare contro altre
due squadre nella stessa giornata. Ok?
Quindi l'algoritmo doveva tirare fuori
questa riallocazione sottoinsiemi di
tutte le possibili ripartite tale per
cui questa partizione sostanzialmente
fosse coerente
e ci abbiamo provato per giorni e non ci
riuscivamo, non riuscivamo a capire sta
cosa. Si avvicinava il giorno dell'esame
a noi sto pezzo di algoritmo ancora
mancava. Detto come cavolo facciamo?
Allora, a quel punto mi venne un'idea.
Mi venne un'idea perché detto "Ok, ma se
non ci riusciamo così possiamo provare
ad attaccarlo
da un'altra via". Ok? A quel tempo,
quindi, stavamo seguendo il corso di
design patterns ed algoritmica,
eccetera, e stavamo guardando
backtracking, queste cose qua, no?
E proprio per quel corso
avevamo sviluppato un algoritmo che era
in grado di colorare
chiaro? Sapete qual è il problema della
colorazione di un grafo? Dato un grafo
fatto di pallette e archi, noi vogliamo
colorare i suoi nodi proprio con i
colori, tale per cui non accade che due
nodi agganciati da un arco abbiano lo
stesso colore. Ok?
Quindi avevo da un lato la generazione
del torneo e non riuscivamo a tirare
fuori sto coso, non funzionava.
Dall'altro avevo un algoritmo che mi
funzionava che era in grado di colorare
grafici. Mi sono chiesto, ma posso
convertire un'istanza della generazione
del torneo in un grafo da colorare e poi
mi sfrutto la colorazione. Cioè questa
era l'idea. Detto, siccome l'algoritmo
diretto ancora non riuscivamo a tirarlo
fuori, poi dopo tanto tempo arrivando
anche era fattibile la cosa, però lì per
lì che non trovavamo una soluzione, ho
detto "Ok, mi serve altro".
mi serve altro. c'ho quest'algoritmo che
è in grado di fare un problema
completamente diverso, eh, cioè là era
colorazione di un grafo da un lato,
generazione delle giornate, il
calendario dall'altro, cioè quindi erano
due cose completamente separate. L'idea
era posso trasformare le istanze di
questo problema in quest'altro problema,
così mi uso il mio algoritmo che mi
colora grafi e la sua colorazione poi la
prendo e la sputto per tirare fuori il
calendario di
un Ok? È chiaro il problema? Quindi, se
vogliamo astrarci da questa situazione,
adesso vedremo l'esempio specifico, però
l'astrazione è questa. Noi abbiamo un
problema di partenza
A, lo chiamiamo L1 piccolo, come volete
voi. Abbiamo un problema
che vogliamo vogliamo risolvere, ma non
lo vogliamo attaccare direttamente. Ok?
Quindi abbiamo un problema di partenza,
ok? Quindi iniziate a cogliere questo
elemento. C'è un problema di partenza e
un problema di arrivo nelle riduzioni.
Le riduzioni non sono
da simmetriche, ok? C'è un verso nelle
riduzioni. Abbiamo un problema di
partenza e un problema di arrivo.
Lo scenario è questo: vogliamo risolvere
le istanze del problema di partenza.
utilizzando un algoritmo pronto, diciamo
così, per il problema di arrivo.
È chiaro? Cioè, quindi siamo la vogliamo
inventarci come risolvere il problema di
partenza.
Cosa facciamo? ci andiamo a scegliere un
problema di attivo, ad esempio, magari
non troppo di simile,
e trasformiamo le istanze del problema
di partenza
nel in istanze del problema di arrivo,
tale per cui possiamo utilizzare un
solutore del problema di arrivo, ci
prendiamo la soluzione, la
ritrasformiamo all'indietro e la usiamo
come soluzione per l'istanza del
problema di partenza. È chiaro? Quindi
noi vogliamo risolvere un problema
utilizzando una subrutine per un altro
problema. Ok? Quindi la questione qui è
come trasformiamo istanze da un problema
dell'altro in maniera tale che le la
soluzione della istanza del sul problema
di arrivo possa essere ritrasformata
all'indietro perché se ci dà una
soluzione e non sappiamo che farci poi
diventa un po' problematico. Ok? Piano
per tutti? Quindi,
intuitivamente iniziamo a introdurre un
po' di elementi. Questa trasformazione,
poi daremo definizioni precise, però
iniziamo pezzettino a pezzettino. Questa
trasformazione
dal problema di partenza al problema di
arrivo si chiama riduzione. Ok? è la
riduzione,
poi dovrà avere delle proprietà, adesso
vediamo, eccetera, però intuitivamente
che significa ridurre un problema a un
altro? Significa trasformare le istanze
del problema di partenza, quindi di
nuovo sottolineiamo una riduzione a un
problema da cui partiamo e un problema a
cui arriviamo. Ok? Quindi, obiettivo di
una riduzione qual è? è trasformare le
istanze del problema di partenza
in istanze di un problema di arrivo,
tale per cui una chiamata ha un solutore
di un problema di arrivo ci dà una
soluzione che noi possiamo riutilizzare
per le istanze del problema di partenza.
Ok?
Se lo lo scriviamo, facciamo uno
schemino.
Quindi abbiamo
un problema di partenza A, un problema
di arrivo B.
Quello che noi ci vogliamo inventare è
partendo da un'istanza X di A.
Peristanza sappiamo che è un input,
però, ok, una stringa, quello che è.
Vogliamo trasformarla
in uno in un'istanza y che si ottiene
tramite l'applicazione di una funzione
f(x),
tale per cui
possiamo ottenere una soluzione partendo
da y e ritrasformandola indietro
possiamo ottenere una soluzione per x.
Ok? E questo è il giro che facciamo. No,
adesso per voi che avete programmato è
molto chiaro come stiamo facendo. Questo
è un pezzettino di codice tale per cui
vogliamo scrivere un solver per A
e prende X che è un'istanza di A.
Ok? Come funziona? Facciamo così,
ci generiamo y è
fx.
Dopodiché prendiamo la soluzione y che
si ottiene facendo solve
di b
e poi
otteniamo otteniamo S con X che è uguale
a
chiamiamola così g
s Y
[Musica]
dove G in qualche modo modo è legata
all'inversa di f, non è proprio
l'inversa perché stiamo trasformando
soluzioni, non stiamo trasformando
istanze e dopodiché facciamo return
di sx. Ok? Cioè questo è quello che noi
vogliamo fare. Vogliamo
inventarci una funzione f tale che per
cui questa soluzione, vedete voi se
questa soluzione candidata qua ci torna,
ok? Quindi questo pezzo di codice, il
solvere per A, funziona quando? Quando
questa f è sensata.
È chiaro per tutti? Quindi vogliamo
risolvere che cosa? Il problema A.
Riceviamo in input una stringa per A, la
trasformiamo in una stringa per B,
chiamiamo come sabrutin un solutore per
B. Otteniamo la soluzione. La soluzione
la trasformiamo indietro.
in maniera opportuna prendiamo sta
soluzione e la restituiamo. Ok? Tutto
ciò funziona se f make sense, ok? In
tutta questa procedura f intuitivamente
è la riduzione. Ok? Quindi la riduzione
cos'è? è una trasformazione
di stringhe verso stringhe in maniera
tale che noi mappiamo istanze di un
problema A su istanze di un problema B
al punto tale che le soluzioni che
otteniamo tramite un solutore per B
possono essere riconvertite in soluzioni
per l'estenza di partenza del problema
per A. È chiaro? È chiaro? Quindi
intuitivamente una riduzione che cos'è?
Not che una riduzione è un verso
che è questo qua. Vedete? In questa
linea qui di codice risulta evidente che
la riduzione è un verso. La funzione f
prende istanze di A e sputa fuori
istanze di B. Ok? Ecco perché parliamo
di problema di partenza e problema di
arrivo. Quindi, quando definiamo questa
funzione qui che è la riduzione, noi
dobbiamo stabilire qual è il suo input e
qual è il suo output. Quindi che istanze
di quale problema prende questa
funzione, che istanze di quale problema
sta restituendo questa funzione? Ok,
chiaro per tutti lo scenario qua. Ok,
facciamo l'esempino e poi formalizziamo
un po' le cose. Ok, ritorniamo alla
storiella. Quindi il nostro problema era
che noi dovevamo risolvere il problema
del calendario
da un lato e dall'altro io sapevo
risolvere la K colorabilità,
cioè quindi mi serviva un algoritmo per
il calendario,
ci abbiamo provato per giorni e nulla di
quello che provavamo funzionava.
dall'altro lato avevo un algoritmo che
era in grado di colorarmi grafici.
Allora la mia idea è stata ma posso
inventarmi
una funzione f opportuna tale per cui io
poi applico sta tecnica, cioè prendo le
istanze del calendario, le trasformo in
istanze di grafica colorare, faccio
colorare il grafo al solutore del del
Kcol, mi prendo la soluzione. Cos'è una
soluzione? è un grafo colorato e da
quella colorazione io mi tiro fuori qual
è il calendario per il torneo. Ok?
Questo è il principio, lo stiamo vedendo
su questo esempio specifico proprio
perché i due problemi sono molto diversi
e viene più semplice capire partiamo da
qui, arriviamo qui perché uno degli
errori più comuni che si fanno quando si
fanno le ritenzioni è non individuare
correttamente chi è il problema di
partenza e qual è il problema di arrivo.
Ok? Quindi ora lo facciamo in questo
modo che è molto distinto e così ce ne
rendiamo conto. Ok?
Poi definizione bla poi sì, ce la
potremmo fare. Ok,
supponiamo, ok? Supponiamo che abbiamo
questo esempio specifico, ok?
quattro squadre, Juventus, Milan,
Bologna, Roma e noi vogliamo generare
un torneo, il calendario di un torneo
all'italiana, cioè che tutti giocano
contro tutti. Ok? e facciamo solo il
girone di andata perché poi il girone di
ritorno è la stessa cosa. Quindi il
nostro obiettivo è generare tutte le
coppie possibili
di queste squadre che sono le partite
possibile e partizionare.
Questa è sostanzialmente la generazione
del torneo. Generiamo tutte le coppie
possibili che quindi che cosa sono?
Juventus- Milan, Juventus-Bologna,
Juventus Roma, Milan, Bologna, Milan-
Roma, Bologna Roma. Ok? Dopodiché l'idea
è partizionare
questo insieme in sottoinsiemi che ci
dicono quali sono le partite che vanno
giocate nella stessa giornata. Ok?
Questa è la cosa. Quindi, come vi
dicevo, questa roba a noi ci dava un po'
di problemi. Ok?
Allora, l'idea fu questa qua.
Ragazzionai un po' così, ha detto "Ok,
io mi devo partizionare con l'insieme di
partite", no?
E quello che so
qua si lavora molto creativi e molto
sostili. Quindi da un lato abbiamo la
generazione del calendario in cui
generiamo tutte le possibili coppie e
questo insieme va partizionato in
sottoinsiemi tale per cui una squadra
non giochi più di una partita in uno di
queste partizioni. Ok? Quindi da un lato
mi devo fare gruppi.
State attenti alle parole che usore, eh.
Da un lato mi devo fare una serie di
insiemi
tale per cui le partite al proprio
interno siano compatibili.
Ok? Questo è quello che vogliamo
ottenare per la generazione del
calendario.
Lato problema del calendario.
Lato problema della colorazione di un
grafo. Ok? Quindi noi ci andiamo per
riuscire a capire perché il nostro
obiettivo è inventarci con la funzione
f, ok? Per riuscire a capire come f, per
riuscire a inventarci f, eh, perché ce
ne stanno tante, però per riuscircene a
inventare una, come faccio io? Mi guardo
le istanze del problema di partenza, le
istanze del problema di arrivo, come
sono fatte le soluzioni del problema di
partenza, come sono fatte le soluzioni
del problema di arrivo e cerco di capire
se ci sta un mapping del problema di
partenza sul problema di arrivo. E
allora io vado proprio a guardare la
struttura delle soluzioni, ok? Quindi
cos'è una soluzione del problema del
calendario? La soluzione, il problema
del calendario è una partizione di
quell'insi insieme di elementi tale per
cui in ogni singola sottoinsieme, in
ogni singola parte di questa partizione
non accade che ci sono due partite
incompatibili e due partite sono
incompatibili se hanno una squadra in
comune. Ok? È chiaro? Chiaro per tutti?
Dall'altro lato che abbiamo il problema
di colorare un grafo.
Quindi abbiamo un grafo con i suoi nodi
e i suoi archi e abbiamo un certo numero
di colori. Ok?
Cos'è una soluzione per il problema
della colorabilità?
è un assegnamento
di colori
ai nodi
tale per cui
non accade che cosa? Che i due nodi
agganciati abbiano lo stesso colore. Ok?
Quindi queste sono le soluzioni.
Adesso giochiamo un pochino sulla
struttura delle soluzioni perché
cerchiamo di vedere se troviamo delle
similitudini perché trovato quello la
riduzione si fa. Eh,
quindi da un lato dobbiamo generare
insiemi di partite compatibili.
Dall'altro dobbiamo generare colorazioni
sensate.
Da un lato abbiamo che le partite
che finiscono nella stessa giornata non
devono collidere.
State attenti ora al passaggio.
Dall'altro lato che cosa abbiamo?
Dobbiamo avere una colorazione
tale per cui nodi con lo stesso colore
che proprietà hanno?
Non sono agganciati.
Quindi da un lato ci servono set di
partite compatibili.
Dall'altro vogliamo che col che nodi
colorati allo stesso modo non siano
collegati. Ok? Prendetevi una trentina
di secondi per pensare come sfruttiamo
sta faccenda.
Sì, io collegherei
i nodi a distanza uno con le partite
incompatibili. Ah eh in modo tale che
eh così cioè di conseguenza vengono
colorate con diverse partite
compatibili. Esattamente. Questo era il
trucco che mi sono inventato.
Ok io non riesco a generare sto
calendario, però un algoritmo che mi
colora gratis. Come lo scrutto? In quel
modo. Vediamo l'esempio e e poi
iniziamo a formalizzare la nozione di
riduzione. Ok?
Quindi
partito.
Dov'è? Non ciamo tutto.
Ok.
Quindi, che cos'è un'istanza del
problema del calendario?
È un insieme e una lista di squadre. Che
cos'è un'istanza del problema della
Kollorabilità? è una coppia grafo
K, che è il numero di colori che
vogliamo usare per colorare il nostro
grafo. Ok?
Ci dobbiamo inventare questa funzione
fasti
una lista di partite
in una coppia grafo colore. Ok? Questa è
F. f una funzione stramba
che prende in input
una lista di squadre
e tira fuori una coppia grafo numero di
colori in maniera tale che quando
coloriamo questo grafo specifico
e con quel numero di colori specifico
noi possiamo riconvertire all'indietro
la soluzione per tirarci fuori il
candal. Ok? Allora, l'idea è questa.
Questo è l'input di f e questo è
l'output
f. Quindi questo va qua dentro e questa
è l'uscita. Ok? Quindi in input f
abbiamo una lista di squadre. In uscita
di f ci sta un grafo e un numero di
colori. Un grafo è fatto di nodi e
vertice. Ok? Come funziona f?
F prende in input la lista delle squadre
e genera un grafo con tanti nodi quanti
sono tutte le possibili partite. Ok?
Quindi f prende in input questa lista
qui, inizia a costruire un grafo
che è fatto di noi in vertici in Sì.
vertici anche
i vertici di questo grafo tutte le
possibili coppie di squadre che
sostanzialmente per noi sarebbero le
partite. Quindi F che inizia a fare
Juventus, Milan,
Juventus, Bologna,
Juventus, Roma,
Milan, Bologna,
Milan, Roma,
Bologna Roma. Ok? Cioè f ha preso in
input una lista di squadre, sta sputando
fuori vertici. Che vertici sputa? Sputa
fuori un vertice per ognuna delle
possibili partite. Ok? E ci mettiamo
questa etichetta sopra in maniera tale
che sappiamo quel vertice a quale
partita il mio di principio è collegato.
Va bene? Chiaro?
Un grafo di cosa è fatto? di vertici
archi.
Siccome noi vogliamo ottenere un grafo
la cui colorazione ci dirà quale partite
possono essere giocate assieme e partite
che non possono essere giocate assieme
sono le partite che hanno una squadra in
comune da un lato e dall'altro lato i
nodi che non possono avere lo stesso
colore sono i nodi collegati da un arco.
La funzione f? Collega con un arco tutti
i nodi relativi a partite incompatibile.
Chiaro? Quindi F che fa? dopo che ha
generato questi sei nodi, inizia a
collegarlo questo con questo, questo con
questo, questo con questo.
Milan, Bologna, Bologna, Bologna,
Bologna, Roma, Roma, Milan, Milan,
Roma Roma
Roma Roma sta qua. Milan, Milan sta qua.
Bologna Bologna così e Milan Milan è
questo. Ok, è chiaro?
Quindi questa funzione fzione di
riduzione, guardate, eh dobbiamo essere
precisi, che prende come input
una lista di squadre, ok? Che è
l'istanza del problema di partenza. Cosa
deve sputare in output? Un grafo. E un
numero. Il numero ancora non l'abbiamo
visto. Ok? deve sputare fuori un grafo.
Quindi F partendo da una lista di
squadre
tira fuori che cosa? Tira fuori un
grafo.
Un grafo che ha tanti nodi quante sono
tutte le possibili partite.
E i vertici di questo grafo, no? vertici
gli archi di questo grafo collegano nodi
relative a relativi a partite
incompatibili, ok? Cioè, quindi F questo
fa sputa nodi, sputa archi e li assembla
così. Ok?
Siccome l'idea era che
i nodi colorati con un certo colore sono
le partite che vanno a finire in quella
in quella giornata, dobbiamo avere tanti
colori quante sono le possibili
giornate. Adesso in un campionato di
quattro squadre quante giornate ci
stanno?
Tre. Perché ognuno deve giocare con
qualcun altro, quindi ogni squadra,
ognuna di queste quattro squadre deve
fare tre partite contro le rimanenti
tre. Ok? Quindi questa funzione fera
fuori questa coppia grafo così è il
numero 3. Ok?
È chiaro?
Quindi dobbiamo essere su questo
preciso.
F cosa ha preso in input?
Una lista di squadre. Perché? Perché una
lista di squadre è l'istanza del
problema di partenza.
Cosa spuda fuori in output? Una coppia.
Grafo colori, numero di colori. Perché?
Perché la coppia, grafo, numero di
colori è l'istanza del problema di
arrivo.
Come la sputa fuori questo grafo, questo
e questo numero di colori f la butta
fuori a caso? potrebbe, però in realtà a
noi serve una f tale per cui il grafo
che lui che costruisce ci torna utile
per risolvere il problema di partenza,
perché se non c'è alcun tipo di
correlazione non ce ne facciamo niente,
ok? Quindi il grafo generato dalla
funzione f deve essere un grafo tale per
cui una volta colorato quella
colorazione noi la usiamo per generare
il calendario. Se il grafo sfrutato
fuori da f è un grafo a caso, non ce ne
facciamo niente, ok? Quindi f deve
essere progettata in maniera funzionale.
Ok?
Chiaro? Fin qua? Alr? Quindi cosa
abbiamo fatto? Questa è lo scenario in
cui siamo. Vogliamo un solver per il
problema del calendario. Prendiamo in
input che cosa? Una lista di squadre.
Noi ci siamo inventati questa funzione f
in input una lista di squadre sputa
fuori y una coppia grafo numero di
colore, ok? Dopodiché questa cosa qua lo
facciamo risolvere a un solutore per B.
Quindi il solver per B che cos'è? È un
algoritmo che colora grafici.
La colorazione ottenuta la trasformeremo
al contrario per ottenere il calendario
della partita. Ok? Adesso guardate.
Quindi la funzione fatto?
ha preso questa lista, ha tirato fuori
questa coppia,
c'è il passaggio della colorazione, ok?
La colorazione di questo grafo. Quindi
diamo questo grafo e questo numero di
colori a un sold diciamo, senti qua,
abbiamo questo grafo fatto in questo
modo e c'abbiamo tre colori. Me lo
colori? Ok? Noi non sappiamo come fa',
ma lo fanno. Ok? Mettiamoci qualcosa.
Eh, questo qua. Ok.
Alright.
Quindi con tre colori come possiamo
farlo? Per esempio, possiamo fare questo
verde,
questo blu perché è agganciato e quindi
non possiamo usare lo stesso colore. Ok?
Poi quest'altra qui deve avere un terzo
colore ancora perché lui è agganciato
sia a questo che a questo, quindi non
può essere né verde né blu e lo facciamo
rosso.
Dov'è? Qua.
Questo è rosso bello. Ma insomma. Ok, lo
coloriamo di questo colore. Va bene.
Si vede un cavo.
Bravo così. Eh,
ok, va bene.
Dobbiamo colorare colorare gli altri.
Focalizziamoci su Milan Bologna. Di che
colore deve essere? Blu blu perché è
colorato a una
Sì,
è colorato a due nodi verdi e rossi,
quindi questo deve essere blu.
Focalizziamoci su Juventus Bologna. Di
che colore deve essere?
Rosso.
Focalizziamoci su Bologna- Roma. Di che
colore deve essere?
Verde. Ok.
Quindi,
ricapitolando,
la funzione f ha preso in mano una lista
di squadre, ha sputato fuori ad arte
ad arte un grafo e un numero di colori
tale per cui una colorazione di quel
grafo costruito d'arte può essere
riutilizzato per generare il calendario.
Ok? Quindi glielo diamo impasto al
solver per B, quindi otteniamo Sy. Che
cos'è? È questa colorazione. Ad esempio.
Adesso da SY noi dobbiamo lavorarci
sopra per ottenere il calendario. Ok?
Secondo voi, qual è il calendario?
Un colore, una giornata. un colore, una
giornata. Quindi il calendario è
Juventus, Milan e Bologna- Roma.
L'altro è Juventus, Roma, Milan, Bologna
e l'altro cos'è? È Milan, Roma,
Juventus, Bologna e come vedete
magicamente abbiamo il nostro calendario
e non ci siamo proprio preoccupati
dell'algoritmo che lo risolve, ok? Ci
siamo chiesti, ma io sto problema lo
posso trasformare in istanze, le istanze
di questo problema lo posso trasformare
in istanze di un altro problema e uso
l'altro solutore.
Ok?
Adesso facciamo pausa. Eh,
quindi
ad alto livello, che cosa abbiamo
imparato finora? Poi facciamo una
considerazione e ci fermiamo. Ok?
intuitivamente cos'è una riduzione?
Intuitivamente una riduzione è una
trasformazione
di cosa? di istanze di un partenza in un
problema di arrivo. Guys, qui è
importante. C'è una direzione, c'è un
problema da cui partiamo e un problema
da cui arriviamo. Qui stiamo
trasformando una lista di squadre in un
grafo, non un grafo in una lista di
squadre. È chiaro? So due trasformazioni
diverse. Qual è il problema di partenza
nella riduzione che abbiamo appena
vista? è il calendario.
Qual è il problema di arrivo nella eh
riduzione che abbiamo appena visto? È la
colorabilità.
Stabilire chi è il problema di partenza,
stabilire chi è il problema di arrivo ci
permette di capire cos'è l'input della
funzione f, cos'è l'output della
funzione f. Ok? Questo a livello di
struttura delle stringhe che deve
gestire. Però la funzione f non è che la
può mettare da buffo, deve essere
disegnata, progettata in maniera tale
che la trasformazione che f ci propone
in output è tale per cui
la soluzione dell'istanza del problema
di arrivo può essere riconvertita
agevolmente
nella in una soluzione dell'istanza del
problema di partenza. Adesso il fatto
che noi riusciamo a generare il
calendario in quel modo, in maniera così
semplice, guardando la colorazione del
grafo, è perché il grafo ha quella
forma. Se noi c'era ci fossimo aumentati
un altro grafo, magari non serviva
niente, ok? Cioè, quindi la funzione f
deve essere progettata in maniera tale
che noi possiamo riutilizzare la
soluzione, senò non ce ne facciamo
nulla. Ok? Chiaro per tutti? Cos'è una
riduzione? è una trasformazione di
istanze
svolta al fine di riutilizzare le
risposte di un problema di destinazione.
Ok?
Quindi problema di partenza, problema di
destinazione è importantissimo perché
uno degli errori comuni invertire queste
due cose, cioè fare la trasformazione al
contrario. In quell'esempio, siccome
volevamo risolvere calendario,
sono le istanze di calendario che vanno
trasformate, non le istanze della
Korabilità. Ok?
Ultima cosa,
se siamo in grado di trasformare, quindi
di ridurre,
quando usiamo la parola ridurre è una
porting in italiano di una parola in
inglese che è reduction, però voi
pensate alla che è trasformare. Ok? Cosa
significa ridurre? Significa trasformare
istanze di un problema in istanze di un
altro problema. Quando noi siamo in
grado di ridurre
un problema
di partenza a un problema di arrivo, in
questo caso il problema calendario al
problema della K colorabilità lo
indichiamo con questo simbolo
minore o uguale. Ok? Questo è il simbolo
della riduzione. Quindi, se un problema
A si riduce a un problema B, noi a
simboli scriviamo A ug b. Una domanda
per voi e poi ci fermiamo. Quindi noi
abbiamo trasformato un problema di
partenza, ma si vede? un problema di
partenza in un problema di arrivo. Ok,
chiaro?
Che possiamo dire
della relativa difficoltà di questi due
problemi?
Sì, dipende dalla complessità della
funzione di trasformazione.
Assumiamo che la funzione di
trasformazione siamo nel reame del
supponiamo che sia facile, ok? Poi poi
dovremmo essere molto molto
specifici su quello, però supponiamo che
f sia una cosa facile o comunque
calcolabile. Cosa possiamo dire? Ok, mi
faccio meglio la domanda. Cosa possiamo
dire relativamente alla decidibilità dei
due problemi?
Sì, se è decidibile uno è decidibile la
l'uno chiolismo.
Se è decidibile il problema B, allora
anche il problema A sarà decis. Sì.
Allora, l'intuizione è questa. Adesso
vedremo formalmente tutte le cose dopo
la pausa. Se noi riduciamo il problema A
B e B è decidibile,
allora lo è anche A.
Perché una procedura per risolvere A
qual è?
trasformare trasformare le stanze, usare
il solutore per B che è decidibile e
così abbiamo le soluzioni per A e questa
è una procedura per risolvere A. E se A
è indecidibile
possiamo dire mh che possiamo dire di B?
Anche B è indeccibile. Anche B è
indecciibile
perché
se noi abbiamo che il problema A si può
trasformare nel problema B e il problema
B è indecidibile, il problema A è
indecidibile, non possiamo sperare di
risolverlo trasformandolo in un altro
problema, ok? Quello lì pure dovrà
essere indecidibile se la funzione di
trasformazione è calcolabile. Però
questi sono tutti i dettagli che vedremo
formalmente dopo la pausa, ok? Il quarto
d'ora
va.
Ok. la nozione di riduzione.
Chiaro?
Ok? Quindi è semplicemente la
trasformazione di un delle stanze di un
problema in istanze di un altro. Non una
trasformazione a cavoli, una
trasformazione che ci permetta di
riutilizzare le sue perché se
trasformiamo così a buffo non serve
assolutamente a niente. Ok?
Alr, quindi ora dobbiamo
introdurre la definizione formale di
questo concetto di riduzione che poi lo
useremo per il resto del nostro corso.
Ok?
Allora, nell'esempio che abbiamo visto
prima, che tipo di problemi erano il la
generazione del calendario e la
colazione
del di un grafo?
erano problemi di ricerca o di
decisione?
Problemi di ricerca perché abbiamo degli
output. Ok? Siccome noi ci interessiamo
di problemi di decisione,
la nozione di riduzione la possiamo
semplificare, ok? Perché? Perché le
risposte sono solo 01. Quindi la
trasformazione all'indietro della
soluzione data dal solver del problema B
in realtà non serve perché le risposte
che ci darà il solder per B è 01. Ok? E
noi in output dobbiamo restituire 01.
Quindi la nozione di riduzione fra
problemi di decisione è più semplice.
Ok? Quindi formalizziamo questo
Sì, siano
A e B due linguaggi.
La funzione f
è una riduzione
da
a
B. Ok? Quindi c'è il verso eh problema
di linguaggio di partenza, linguaggio di
destinazione. Ok? Quindi
siano e B due linguaggi. La funzione f è
una riduzione da A a B.
Se
per ogni
stringa
W,
la stringa W appartiene al linguaggio A
se e solo se il trasformato secondo f
appartiene al linguaggio B
e inoltre
F è calcolabile.
Ok? F non può fare magia, deve essere
una cosa che si può calcolare, ok?
Quindi non è che in F ci mettiamo un
problema impeccile. In F deve esserci
qualcosa che sta nel reame delle
funzioni ricorsive. Ok? Quindi
di nuovo, che cos'è una riduzione? una
riduzione è una trasformazione,
una riduzione da un linguaggio A un
linguaggio B o da un problema A un
problema B, che è la stessa cosa, è una
funzione falcolabile
tale per cui per ogni stringa W V è
un'istanza sì del linguaggio A se solo
se la trasformazione di doppia B tramite
F che unistanza sia del linguaggio B.
Ok?
Quindi questa è una semplificazione del
concetto che abbiamo visto prima perché
nel momento adesso in cui noi riduciamo
un problema di decisione A a un problema
di decisione B, quell'algoritmino
che avevamo visto in cui sfruttiamo la
riduzione per fare la eh
per calcolare la soluzione del problema.
In questo caso è molto più semplice
perché quello che dobbiamo fare è che
cos'è? Ci arriva w input, la
trasformiamo secondo F,
la facciamo risolvere a un solutore per
B. Il solutore per B sì o no. Quella
stessa risposta la di noi ok? Questo è
il significato.
Il fatto che W appartiene ad A, se è
solo se il trasformato di W appartiene a
V. Significa che se V se w è un'istanza
s di A, f deve essere un'istanza sì di
B. Se B è un istanza no di A, allora f
deve essere un'istanza di B. Ok? Questo
significa
qui abbiamo
una eh un pezzo che ci manca che cos'è
una funzione calcolabile, ok? Perché
finora non ci siamo occupati di
funzioni, ma ci siamo solamente occupati
di problemi di decisione, ok? una
funzione è una cosa un po' diversa
perché abbiamo un input, abbiamo un
output e problemi di decisione, quelli
sono tutti i linguaggi, o la stringa è
dentro o la stringa è fuori. Quindi ci
serve una definizione per calcolabilità.
Ok? Allora, per definire
quand'è che una funzione calcolabile,
dobbiamo definire non li guarderemo in
estrema precisione, ci serve giusto per
dare questa definizione. Dobbiamo vedere
le macchine di Turing che calcolano un
output, perché finora abbiamo visto
macchine di Touring che dicono solo s
No, ma una macchina di Touring sa pure
fare le somme per dire. Ok? Quindi come
come definiamo formalmente la
calcolabilità delle funzioni? Lo si fa
tramite questo concetto che è il
concetto di trasduttore.
Alrght. Cos'è un trasduttore? Un
trasduttore
è una macchina di ting,
quindi cos'è un trasduttore? È una
macchina di tuning,
caratterizzata da tre nastri.
Ok? Usiamo una definizione multinastro
perché è comoda. Questa cosa si può
adattare anche a un nastro solo, però
per essere chiare. Quindi è una macchina
di Turouring che calcola calcola
funzioni, ok?
Questa macchina ha un nastro di input,
il primo nastro che è il nastro di
input, come siamo abituati e questo qua
è un nastro a solo lettura.
Ok? Quindi questa macchina ha un nastro
di input sul quale può solamente
leggere, non ci può scrivere cose, può
spostare la testina eccetera, può
leggere quello che c'è sul nastro, ma
non lo può alterare.
Poi c'ha un secondo nastro che è un work
tape, quindi è della memoria dove può
fare roba e questo è un nastro a lettura
scrittura.
E poi c'è un terzo nastro
che è il nastro di output
che è un nastro a sola scrittura.
Ok.
Quindi l'intuizione di un trasduttore
che cos'è? È una macchina di touring che
legge l'input sul primo nastro, sul
secondo nastro ci fa il lavoro che ci
deve fare, quando ha la risposta pronta
la scrive sul nastro di e poi si ferma.
Quindi, intuitivamente, la funzione
calcolata da una macchina di Touring è
la funzione che ricevendo un input sul
nastro di input dà come output quello
che rimane scritto sul nastro di output
a termine, ok? Quindi non c'è un vero e
proprio una vera e propria nozione di
accettazione. Non è che la macchina dice
sì, dice no, la macchina parte e a un
certo punto si ferma. Quando si ferma,
quello che è stato scritto su output è
il risultato del calcolo. Ok?
Noi diciamo
quindi
un
trasduttore
M che quindi è semplicemente una
macchina. calcola
la funzione
f
se per ogni stringa
w
quando
m esegue
su W
al suo arresto
lascia
FW
in output.
Ok? Quindi
definizione abbastanza stida ad una
macchina. chiamiamo trasduttore giusto
per per
convenienza nostra, per capire
direttamente se la macchina è un
decisore o un calcolatore. Quindi quando
è un calcolatore lo chiamiamo
trasuttore, quando è un decisore lo
chiamiamo macchina di ok? Quindi un
trasduttore calcola una certa funzione f
se su ogni stringa che ricevi input
questa macchina M
al termine del calcolo, cioè quando la
macchina si arresta, sul nastro di
output troviamo il trasformato di doppi
secondo la funzione f. Ok? Sì, possiamo
dire che i classiori
eh abbiano sempre unazione.
Ah, ok, perfetto.
È una richiesta nostra. Sì, perché
essendo una macchina di Turing, essendo
un caso più generale delle macchine di
Turing, il trasguttore in linea di
principio lo
potrebbe non fermarsi. Allora, cos'è per
noi una funzione calcolabile? è una
funzione per il quale esiste un
trasmuttore che la calcola sempre in
tempo fisso. Ok?
Questo è il concetto. Quindi una
funzione per noi è calcolabile
se esiste un trasduttore che la calcoli
in tempo finito, cioè che per ogni input
che noi gli diamo,
stageggino a un certo punto si ferma e
sul un nastro di output avrà scritto il
risultato. Ok?
Quindi, nella definizione di riduzione
che abbiamo dato prima, che abbiamo
detto, cos'è una riduzione da AB? È una
funzione che trasforma le istanze di A
in istanze di B, tale per cui doppia per
ogni stringa W. w appartiene a se lo
sente trasformato di W secondo la
trasformazione, cioè secondo F
appartiene a B e la funzione f deve
essere calcolabile perché senò
ovviamente è un concetto che non
possiamo
non possiamo sfruttare per quello che ci
serve. Ok? Chiaro per tutti?
Possiamo adesso iniziare a vedere
un po' di proprietà delle riduzioni. Ok?
Vediamo se qua tempo finito.
Ok? Quindi, nel momento in cui noi
dobbiamo specificare una riduzione da un
problema ad un'alttro, no,
noi dobbiamo specificare qual è sta
funzione feme una funzione calcolabile e
per definizione formale la una funzione
è calcolabile se esiste un trasduttore
che la calcola in tempo finito. La
domanda quindi è: ma allora quando
dobbiamo ridurre un problema a un altro
dobbiamo mostrare la macchinina di
Turing che fa la trasformazione? No, non
è necessario.
Nelle prossime descrizioni e anche
quando farete l'esame, se vi chiedo una
riduzione,
quello che eh siamo interessati è un
algoritmo o in pseudocodice o in
linguaggio naturale che sia
sufficientemente preciso da far capire
che fa sta funzione f, no? Perché un
conto è tipo sul problema di prima del
calendario. La funzione f prende la
lista del delle squadre, genera tutte le
coppie per farne i nodi, collega i nodi
secondo questo criterio. Il numero dei
colori è questo. Questa è una
descrizione precisa. Una descrizione del
tipo: la funzione f prende la lista
delle delle squadre e tira fuori il
grafo che mi serve, quello è una
descrizione un po' meno precisa. Ok?
Quindi non è necessario tirar fuori il
trasuttore, la macchina di Touring, però
è indispensabile che la descrizione, che
sia anche in linguaggio lavorale, non
importa, sia sufficientemente precisa da
capire com'è che statput viene generato
dalla funzione di trasformazione. Ok?
Alright.
Quindi proprietà delle riduzioni e poi
vedremo un'applicazione di riduzioni
così vediamo che ci possiamo fare.
Formalizziamo l'intuizione che abbiamo
avuto prima. teorema
siano
A e B due linguaggi
tali che
A si riduce a B. Ok? Quindi noi abbiamo
due linguaggi
per i quali sappiamo esiste una
riduzione, ok? Da A B.
1.
Se A non appartiene a R,
allora
B non appartiene a R.
Ora dimostriamo. Eh, comunque è
un'intuizione che già abbiamo avuto.
Secondo,
se A non appartiene ad re, allora
B non appartiene a R-.
Induivamente ci siete? che avevamo
discusso un po' prima, cioè se A è
indecidibile
e lo trasformo a B tramite una funzione
falcolabile e lì vi indico perché se la
funzione di trasformazione è la funzione
del mago merlino, allora potremmo
tradurre trasformare le istanze di un
problema decidibile in istanze di un
problema, cioè istanze di un problema
indecidibili in istanze di un problema
semplicissimo perché la funzione di
trasformazione che è il potere della
bacchetta magica e trasforma ste cose
qua, no? Però se abbiamo il vincolo
stringente che la funzione di
trasformazione deve essere computabile,
cioè ci deve stare una macchina di
Touring che è in grado di calcolarla in
tempo finito, allora intuitivamente non
possiamo sperare di trasformare un
problema indecidibile in qualcosa che si
decide, ok? è quella è quella la chiave
rivolta, cioè che la funzione di
trasformazione deve essere calcolabile
perché senò tutta la difficoltà della
trasformazione della riduzione,
la difficoltà di prendere un problema
difficile e buttarlo su un problema
facile si anniditerebbe nella funzione
di trasformazione. Ok? Ma se noi
diciamo, guarda che la funzione di
trasformazione non è che può fa cose
strane, deve essere calcolabile, allora
non possiamo sperare di trasformare un
problema indecidibile in qualcosa di
decidibile. Ok?
Ok.
Dimostriamo formalmente queste proprietà
che poi è quello che useremo per il
resto del corso. Eh,
dimostrazione.
Partiamo dal primo.
Allora, procediamo. Procediamo assieme,
ragioniamoci assieme. Allora, noi
vogliamo mostrare che se A si riduce a B
e A
non è ricorsivo, allora nemmeno B può
esserlo. Ok?
Vi do io l'avvio e poi ci ragioniamo
assieme. Ok? Come vedete,
adesso stiamo sostanzialmente annidando
nella dimostrazione di questa proprietà
tutte quelle dimostrazioni che abbiamo
fatto ieri che avevano tutte la stessa
forma. Adesso noi fattorizziamo
quell'idea nella proprietà delle
riduzioni e dopodiché usiamo le
riduzioni. Ok?
Quindi, primo passaggio,
se A non appartiene a R, allora B non
appartiene ad R. Questa è
un'implicazione.
Dobbiamo assumere per assurdo la sua
negazione.
Qual è la negazione di questa
implicazione?
Sì, se B appartiene R, anche A
appartiene R che A non appartiene ad R
e B appartiene ad R.
Come ci si arriva? Eh, ok, questo è il
trucco.
A, B. Devo usare altri nomi. X e Y. X e
Y. Ok.
Quello che è scritto là sopra.
Se A non appartiene a R, allora B non
appartiene a R. È questa cosa qua. X
implica Y, in cui X A non appartiene a R
e Y e B non appartiene a R. È chiaro il
significato di quei due pezzi? Questa
questo pezzo di formula logica è uguale
a Lo sapete riscrivere senza la
frecettina?
Not X not X or Y. Not X or Y. Ok.
Se no prendiamo la negazione di questo,
che cos'è?
X
X and not Y. E guardate qua,
A non appartiene a Ro.
And
il negato di y
ho sbagliato un attimo.
E B appartiene a R che è il negato di Y
che Y era
Ok? Quindi così si nega una
un'implicazione. Eh, il negato di
un'implicazione che cos'è? È che
l'cedente è vero e il conseguente è
falso. Ok? Chiaro? Quindi
questo qua è in inciso. Rifocalizziamoci
sulla assunzione per assurdo. La nostra
assunzione per assurdo è che A Sì,
che A non appartiene a ricorsivo e
nonostante ciò B sia ricorsivo. Ok?
Chiaro? Quindi questa è l'assunzione di
partenza da cui ci stiamo muovendo. Ok?
Se B è riporsivo, che cosa possiamo
dire?
Sì,
sì.
Allora, se B appartiene ad R, allora
esiste una macchina MDB
che decide
B. Ok?
Ma allora
potremmo
costruirci
un decisore per A fatto in questo modo.
Quindi questa è un MA che è un decisore
per A.
prende in input w
calcola la funzione di f per ottenere f.
E questa cosa è fattibile perché stiamo
assumendo che A si riduce a b, quindi f
è una funzione che esiste ed è una
funzione calcolabile. Ok?
E questa cosa viene data in input a la
macchina MB.
Se questa risponde sì, rispondiamo sì.
Se questa risponde no, rispondiamo no.
Ok?
Qual è il linguaggio di questa macchina
che chiamiamo ma?
Mh.
Il linguaggio della macchinona, della
macchina grossa, della macchina esterna.
Vai. Mh. Ah, il linguaggio di questa
macchina
è A. Di conseguenza noi avremmo un
decisore per A.
Ma questo non è possibile perché noi
stiamo assumendo che A è indecci,
quindi per forza B deve essere esso
stesso indecidibile.
Ok? È chiaro? Quindi, se noi riduciamo
tramite una funzione calcolabile un
problema indecidibile a un altro, allora
quest'altro problema, il problema di
destinazione deve essere esso stesso
indecidibile. Ok?
Secondo
pezzo
si procede allo stesso modo.
Supponiamo da qui. Questa quindi è una
contraddizione. Contraddizione. Quindi
assumiamo che A
non appartenga ad RE and B appartenga ad
R.
la costruzione è identica,
nel senso che in questo in questo caso
il il la macchina che riconosce qui non
è un decisore, ma è un accettatore. Ok?
Però se costruiamo la macchina allo
stesso modo, eh qui prendiamo W in
input,
calcoliamo F di W. F cos'è? è proprio la
funzione che trasforma a in béch
sappiamo nell'assunzione del teorema che
A si riduce a B e poi abbiamo impasto
all'accettatore di B, noi otterremo
overall un accettatore per A. Ma ciò non
è possibile perché stiamo assumendo che
A non sia ricorsivamente numerabile,
quindi per forza anche qui non deve
esserlo.
È chiaro questo passaggio? Siamo andati
troppo veloce. Ok,
mi serve un po' di tempo ora per fare un
esemplino. Ok, chiaro? Quindi un'altra
proprietà che è in
è il
è è interessante che è un corollario
del problema del teorema precedente è
ottenuto tramite
l'inversione dell'implicazione. Ok? Noi
abbiamo visto che se A non è non
appartiene a R, allora B non non deve
appartenere a R. E se A non appartiene a
R, allora B non appartiene ad R. Però
noi possiamo fare, come si dice in
italiano? Il converso,
come converso. Il converso, sì,
l'opposto. Boh. Lo giriamo, ok? lo
svoltiamo, facciamo un calzino bla e lo
facciamo allora che si ottiene come
guardando la definizione di
implicazione, no? Che noi possiamo
ve la scrivo.
Se x implica y, allora questa cosa è
uguale a not x or y, però questa cosa è
uguale a not y implica noti
riscriviamo la cosa al contrario, ok?
Quindi, applicando questa trasformazione
al di prima otteniamo questo corollario.
Siano
A e B due linguaggi
tali che A si riduce a B. 1. Se B
appartiene ad R, allora
A appartiene a R. 2. Se B appartiene ad
R, allora
A appartiene e scritto proprio
appartiene ad re. Ok? Abbiamo
semplicemente preso il verso
dell'implicazione del teorema di prima e
l'abbiamo tutto qua. Eh, non abbiamo
fatto nulla di drammaticamente
complicato. Ok? Abbiamo ascoltato la
proprietà di definizione
dell'implicazione.
È chiaro? Chiaro come abbiamo ottenuto
sto corollario? Ok.
Alright.
Vediamo adesso un altro linguaggio
insieme a quelli di ieri che vogliamo
dimostrare essere indecidibile, però
stavolta lo dimostriamo la sua
indecidibilità tramite induzione. Quindi
vediamo che non ci dobbiamo inventare
più quei teoremi per assurdo, cose così,
ci dobbiamo inventare un'altra cosa,
cioè ci dobbiamo inventare la riduzione.
Ok?
Definiamo il linguaggio LNE,
il linguaggio not empty, che è l'insieme
dei codici di macchine di Touring,
tale che il linguaggio della macchina M
con I non è vuoto.
Ok?
Infinitivamente, che cosa definisce sto
linguaggio?
Le macchine che accettano almeno
stilica. Sì. In LNE ci stanno i codici
di macchine di touring, le quali dicono
sì su almenut. Quale? Non ci importa,
basta che sia uno, almeno uno. Ok?
Allora, vogliamo studiare la proprietà
di decidibilità di questo linguaggio,
ok? È come chiederci un programma, è
vero che risponde sì su almeno una
string input? Questa è la cosa che ci
stiamo chiedendo.
Allora, questo
linguaggio
si può dimostrare essere in re. Ok? Ln
appartiene, ve lo dico io, appartiene ad
re. Secondo voi come si può fare sta
cosa? Come si dimostra che ln? Sì, posso
ridurlo a quello dell'arresto?
Eh, ok. Qual è l'istanza del problema
dell'arresto?
È una macchina e una stringa. Che
stringa gli do? C può essere una cosa,
no? Più standard. Ok. Sì, cioè nel
principio uno potrebbe farlo. Facciamo
un po' facile. Vi ricordo che un
linguaggio appartiene ad RS se esiste
una macchina di tringo accetta. Ok? Noi
prendiamo questa strada per per questo
risultato. Ok? Qual è una macchina di
Turing che potrebbe accettare LN? Ok?
Quindi riceve input,
quindi abbiamo una una macchina di
Touring che riceve in input un'altra
macchina di Touring e deve stabilire se
la macchina ricevuta in input risponde
sì su qualche strilla.
Sì, cioè allora a livello nonistico
è immediato. Praticamente prendiamo come
indoviniamo ciò che accetta. Prendiamo
la st che accetta e prendiamo come inut.
Esattamente. E questa è la soluzione.
Ok? È semplicissima la cosa. Usiamo una
macchina non deterministica. Poi vi
spiego un attimo perché ci conviene far
questo. Allora, quindi una macchina
per LNE, la chiamiamo mne, ad esempio,
riceviamo in input m con i. Poi, come ci
suggeriva il nostro collega, gessiamo
una stringa
W, la diamo impasto alla macchina
universale. Se questa dice di sì, noi
diciamo di sì. Ok?
È chiaro che fa sta macchina? Allora, un
algoritmo per questa macchina è appunto
questo, cioè che la macchina MNE non lo
sa fin da principio qual è la stringa
che mi
può accettare che fa la Ok? Se una tale
stringa esiste, la macchina non è la
gessa, quindi la sputa fuori. Come la fa
a gessare? È quella cosa che abbiamo
visto in quell'esercizio in cui dicevo
scriviamo preventivamente le cose sul
nastro. Che fa la macchina? Sputa sul
nastro preventivamente sta W. Dice
questa verrà accettata dalla macchina
mail. Dopodiché simula su W tramite una
macchina universale. Se questa accetta
risponde di sì. Ok? È chiaro che questo
è il linguaggio che il linguaggio
accettato da MNE è LNE.
Ok? Perché la vogliamo gessare e non le
proviamo una dopo l'altra?
Sì, perché su alcune potrebbe non
terminare. Perché su alcune potrebbe non
terminare. Cioè, quindi se iniziamo a
testare una stringa dopo l'altro e mu
non si arresta, è un caso di problema.
Allora, la che siamo, ok? Poi sappiamo
che esiste una trasformazione da
macchina non deterministica a
deterministica, quindi questa ci
garantisce che il linguaggio LNE è in R.
Ok? La domanda ora è se LNE apparteng o
meno.
Teorema.
Intuitivamente il linguaggio,
secondo voi appartiene a R a a R o no?
Hai
intuito. Eh,
no, perché può essere l'infuzione e
la possibilità che non
possa terminare su una stringa. Mh,
quello No, non è quello, perché quello è
su una determinata stringa potrebbe non
terminare, però magari termina su
un'altra. La questione è l'intuizione
del l'intuizione della ragione per cui
questo linguaggio non è ricorsivo, cioè
io ragiono così. E se ho problemi a dire
di no. Se devo rispondere di no su
un'istanza di LNE, che sono le istanze
di LN?
Sono macchine. Se devo rispondere di no,
significa che quella macchina non
accetta niente. E io come faccio a
essere sicuro che quella macchina non
accetta niente? Che faccio? Le provo
tutte? Sì. Quanto tempo mi serve?
Infinito. Quindi intuitivamente
è difficile rispondere di noi su questo
problema. Poi magari esiste un modo più
furbo, però questo mi dà l'intuizione
che il problema non è ricorsivo perché
perché ho difficoltà a dire di no. Cioè
io così ragiono, ok? Perché ma riesco a
rispondere di no? M è difficile, quindi
forse forse non è di corivo. Ok? E di
fatto si può dimostrare che LNE non è un
linguaggio ricorsivo. Ok?
Si dovrebbe dimostrare con la solita
cosa dimostrazione per assurda eccetera
bla. Quello che noi facciamo invece è
che dimostriamo che LNE non è ricorsivo
tramite
una riduzione. Ok? Se vogliamo
dimostrare che LNE non appartiene a R,
LNE
dovrà essere il il problema di
destinazione
di una riduzione
da un problema di partenza che non sia
R. Ok? Se io voglio dimostrare che lne
non appartiene a R, devo ridurre a LNE
un problema che so non essere iner ok?
Per la proprietà del teorema che abbiamo
visto prima. Quindi, se io
se io riduco un problema indecidibile a
LNE, allora lne lo dovrà essere pure.
Ok? Come problema di sorgente prendiamo
le Ok? Quindi dimostreremo che lne
è indecidibile perché ridurremo Llu a
LN.
Ok?
Wah! Dobbiamo fare tutto in 10 minuti.
Spero che ce la facciamo perché qua
arriva l'intrigasì. Ok.
processo che è quello che faccio io
nella mia testa e vi suggerisco di
seguire. Dobbiamo ridurre lu a ln. Ok?
Vedete il simbolo maggiore uguale?
Minore uguale. Ecco, una delle ragioni
per cui usiamo minore uguale perché
mette una relazione fra la decidibilità
dei problemi.
Ed è facile da ricordarselo così.
Siccome scritto così significa che lne è
almeno difficile quanto lu se lu è
indecidibile allora lne è indecidibile.
Io così me lo ricordo minore o uguale
che c'ha quel significato. Ok?
Ok. Dobbiamo ridurre lu a ln. Qual è il
problema di partenza?
Dai su che abbiamo solo 10 m
è me lo scrivo da questo lato. Vi faccio
vedere proprio come le faccio io. Eh,
qual è il problema di arrivo? Ln. Ln e
me lo scrivo di qua.
Ok.
Quali sono le istanze del problema di
partenza?
e m coppie macchine e stringhe Mw.
Quali sono le istanze del problema di
arrivo?
Macchine.
Gli diamo un nome diverso per non
confonderci, ok? Chiamiamolo m'.
Quindi la riduzione che è una
trasformazione, io faccio proprio così,
eh, me le scrivo da un lato e
dall'altro, senò confondersi diventa
semplicissimo.
La riduzione che deve fare? Deve
prendere coppie, macchine e stringhe e
ottenere una macchina M'
che dipende da M e W. Ok?
È chiaro? Questo fa la riduzione. La
riduzione deve essere calcolabile
ovviamente
e deve deve soddisfare la proprietà
della risposta
e cioè che se mw è un'istanza,
quindi se Mw
appartiene ad LU, allora
f(mvw
V
deve essere un M'O che appartiene a Lne
e inoltre che se Mw
non appartiene a Lu, allora il
trasformato della coppia deve essere un
M'O che non appartiene a Ln. Ok? Queste
sono le cose.
Chiaro?
Quand'è che MW è un'istanza s di lu?
Quando la macchina
Sì. Quand'è che M prim' è un'istanza s
di lne?
Quando accetta qualcosa. Ok.
Qual è quand'è che Mw è un'istanza no
di?
Quando M non accetta W, quand'è che M' è
un'istanza? No, di lne
quando quando m' dice no su tutto. Ok?
Quindi la funzione f deve essere una
funzione che prende in input una coppia
MW, sputa fuori una macchina M' che
dipende da MW. ed è tale per cui la m'
che otteniamo mantiene questa relazione
fra le stanze. Se riusciamo a ottenere
questo, allora
lne è indecidibile. Ok?
Questa è la
riduzione che vi propongo che ci sta
anche
abbiamo una coppia MW,
otteniamo un M prim' in questo modo.
Partendo da MW otteniamo M' fatta così.
Non c'è spazio, no? Ovviamente
lo mettiamo qua, dai.
È chiaro? Adesso lo sto facendo grafico,
però la funzione di trasformazione che
fa? Prende la coppia di Mw e tira fuori
un M' che dipende da MW e deve
dipenderci avendo quelle proprietà
specifiche.
Allora, m' fa questo,
riceve un certo input, ok?
E cosa fa? Lo ignora.
Cioè poi riceve per l'input non gliene
frega una mazza, cioè nemmeno lo fa
tutt'altro. Che cosa fa?
Scrive w sul nastro.
Abbiamo visto ieri che è una cosa
possibile da fare. Quale W scrive?
Questa.
Ok? Perché M' è una macchina ottenuta
partendo da questa coppia, quindi la
costruzione di questa macchina dipende
da questi due elementi. Quindi M' è una
macchina che il suo input proprio non
gliene importa niente. Scrive W nastro.
Ma che è W? è questa qua. Dopodiché
esegue
M.
Ok? Se M dice sì, allora risponde sì.
Quindi
scoriamo di qualche minuto, cerchiamo di
stare attenti. Ok, abbiamo preso la
coppia M.
La funzione fendo
dalla coppia genera una macchina M
primo. Genera una macchina M primo, che
significa? Sputa fuori una funzione di
transizione per una macchina M'. Questa
macchina M' è una macchina particolare,
è una macchina che
ignora il proprio input, non gliene
frega niente, scrive sul nastro w quale
la whe
in input alla funzione di trasformazione
f, dopodiché esegue la funzione di
transizione di n sulla w che si è appena
scritta da solo, ok? E alla fine dà la
stessa risposta. Ok?
Dobbiamo vedere se ora la proprietà
della riduzione vale.
Supponiamo che MW sia un'istanza s di l
che significa che M
accetta w. Ok?
Guardate m'
se m
qua qua qui c'è ok lo rivedremo tante
volte quindi ci sarà modo di
specificarlo. Noi dobbiamo mostrare che
questa proprietà vale se solo se nel
senso che se Mw è un'istanza s di luas
secondo f è un m' che è un'istanza s di
ln. Se la coppia MW è un'istanza no di
lu, allora la sua trasformazione è un
m'o che è un'istanza no di ln. Ecco
perché abbiamo necessità di guardare le
due cose, ok? Perché noi dobbiamo
mostrare che il mapping tramite f di
istanze sta mappando istanze sì su
istanze sì e istanze no su istanze no.
Ok? Quindi non ci stiamo andando a
curare della decidibilità in sé dei
linguaggi, ci stiamo andando a curare se
la trasformazione mappi istanze sì su
istanze sì e istanze non su istanze no.
Ok? Quindi vediamo il caso delle istanze
sì. Supponiamo che MW sia un'istanza s
di luaggio
di M' cosa accetta M' o cosa non
accetta?
Mh. Sempre.
Attenzione, attenzione.
Input, chiamiamolo, dai, chiamiamolo X.
L'input di M'O chi è?
X.
m' su X risponde sì se
se M accetta w se M accetta w. Ok?
Quindi se Mw è un'istanza s di LO
che linguaggio accetta M prim?
Ogni X. Accetta ogni X. M' risponde sì
su tutto perché se ne forte dell'input
suo dà la risposta di M su w. Però
siccome m stiamo assumendo che accetti
W, m' sì su qualsiasi cosa ci sta
dentro. Ok? Quindi
il linguaggio di m' in questo caso è
l'insieme di tutte le stringhe che è un
linguaggio non vuoto. Quindi m' accetto
un linguaggio non vuoto. Ok?
Quindi se M accetta w v la funzione f ha
sputato fuori in output. una macchina M'
il cui linguaggio è non vuoto, che è
quello che volevamo.
Altro verso della riduzione.
Assumiamo che Mw non appartiene a Lu.
Qual è il linguaggio accertato da M'?
m' che è ottenuto dalla trasformazione
F. Non è che M' è una cosa a casa.
Supponiamo che MW sia un'istanza no. La
diamo in pasto ad f. F macchina sopra,
tira fuori M primo. Sto M' che ha tirato
fuori, sapendo che partivamo da
un'istanza, no, di LO, che proprietà ha?
Qual è il linguaggio di M'O?
[Musica]
Eh, è vuoto perché M'O ignora il suo
input e dà la risposta di m su w. Ma m
su w stiamo dicendo che stiamo partendo
da un stanza no, quindi dirà no su
tutto. Ok?
Da ciò
abbiamo che il linguaggio di M' è il
linguaggio vuoto.
Quindi, siccome siamo in grado di
mappare istanze sì di LU su istanze sì
di LNE e istanze no di lu su istanze no
di LNE, vuol dire che esiste una
riduzione da LU a LNE tale per cui LNE
non è ricordivo perché Lu non lo è.
Chiaro?
Ultima cosa, ultima cosa, così poi non
finiamo. Quindi abbiamo che
quest R questo R
ln
e che è il linguaggio
delle macchine che non accettano niente.
Quindi mi tale che il linguaggio di mi è
vuoto. Noi abbiamo che le è l'opposto di
Ln. Ok?
Abbiamo il complemento, stiamo
considerando il complemento di lne, solo
che gli abbiamo dato un nome, lo
chiamiamo L. Quindi se Lne sta in R e
non in R, che possiamo dire di L? Che
sta fuori re che sta fuori re perché?
Come?
Perché altrimenti sarebbero entrambi in
R, quindi L qua. Ok?
Chiaro? È chiaro il concetto di
riduzione? Perché è una cosa che la
useremo quasi ogni volta. Ok? Grazie
mille. Buona giornata.