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