Ok, iniziamo. Iniziamo per oggi. Ok. L'ultima cosa che abbiamo introdotto sono Ma che i Touring non determin abbiamo visto un po' di esercizi un po' di roba del finastro. Poi quello che abbiamo visto è che essenzialmente eh le macchine di touring non deterministiche possono essere simulate da macchine deterministiche che ci costa un fottio riuscirà a fare sta cosa costa exponential time. Ok? E sostanzialmente quello che abbiamo visto è che noi possiamo simulare queste macchine con altre macchine, quindi utilizzeremo utilizziamo il paradigma non deterministico perché ci torna comodo in quanto noi possiamo simulare macchine non deterministiche tramite macchine deterministiche e per quello di cui ci occupiamo al momento, che è soltanto la decidibilità dei problemi, che ci mettiamo exponentiale. Noi ci mettiamo double exponential time, a noi non ce ne importa assolutamente niente, l'importante è che sia parti, ok? Dopodiché abbiamo introdotto la classe R, la classe R dei problemi, i problemi decidibili, i problemi indecidibili, vi ricordo che tutto ciò che è in R è decidibile. Perché lo chiamiamo decidibile? Perché quello che è in R ammette un algoritmo, cioè ammette una procedura finita che ci risponde sempre sì o no. Ok? Quindi è decidibile, richiamo decidibili tutto ciò che ammette un algoritmo. Al di fuori di R ci stanno i problemi indecidibili, cioè problemi per i quali non esiste un algoritmo. Ok? Abbiamo poi introdotto il re che è questa piccola finezza, no? che sono i problemi semidecidibili, che comunque è una gran fregatura perché non è che se un problema è semidecidibile noi ce ne facciamo un granché perché gli algoritmi che abbiamo per i problemi semidecidibili ma non decidibili ci dà garanzia di risposta in caso di risposta sì, ma non ci dà garanzia di risposta in caso di risposta no. Di conseguenza non è che ci toglie fuori dal limbo se dopo due mesi di calcolo ancora non abbiamo una risposta e prima che se ranna ancora alla fine ci darà una risposta, non lo sappiamo. Di conseguenza i problemi semidecidibili in realtà sono per noi indecidibili, ok? Cioè da un punto di vista prettamente algoritmico non è che ci offrono chissà quale vantaggio rispetto agli altri. Adesso, se un problema è decidibile, noi siamo in grado con un algoritmo di dare sempre una risposta. Quindi avevamo R, re che sono i problemi semidecidibili. Al di fuori di R ci sono cose completamente indecidibili nel quale non abbiamo nessun tipo di garanzia, né per il sì né per il no che dipende, vedremo altre. Ok, ritorniamo con la mente alla simulazione della macchina non deterministica. Ok, noi abbiamo una simulazione deterministica di una macchina non deterministica. Preparare la lezione di oggi è stato interessante perché mi dovevo inventare una storia e non sapevo come appiccicarla. Questo è quello che mi sono inventato. Allora, se ritorniamo con la mente alla alle macchine non deterministiche, noi abbiamo che possiamo fare una simulazione deterministica. Ok? Focalizziamoci sulla simulazione deterministica. Il simulante, ok, che è deterministico, sa fare altro che non quello che faccia il simulato. È chiara la domanda? Cioè la macchina che noi tiriamo fuori, noi partiamo da una macchina non deterministica, ok? La trasformiamo con un'opportuna procedura in una macchina deterministica che sappiamo che si può fare. Ok? Allora, la mia domanda è la macchina simulante, cioè quello che noi otteniamo che è deterministico, fa altro, cioè è in grado di riconoscere cose diverse rispetto al simulato? Se dimostriamo che la macchina non deterministica riesce anche essa a dimostrare la simulante, ovvero una macchina deterministica, allora dimostriamo che entrambi hanno lo stesso potere espressivo. Sì. Ok. Questo è da un punto di vista di potere espressivo, è ovvio. Domanda domanda è passante. Secondo voi una macchina non deterministica è in grado di fare ciò che una macchina deterministica fa? Sì. Perché? Sì, non deterministica di particolare deterministerministica può fare quello che una macchina deterministica fa semplicemente non usando il non determinismo. Ok? è standard, proviene da definizione. Poi noi abbiamo che una macchina deterministica, cioè se partiamo da una macchina non deterministica, noi la possiamo simulare in maniera deterministica con un approccio particolare che è l'esplorazione per livelli delle dell'albero di computazione eccetera. Ok? Ma la mia domanda è la macchina deterministica che si è simulante la macchina non deterministica che è simulata? Fa è una macchina generica il simulante? Dico o fa solo quello che fa il simulato? fa solo quello che fa il simulato, cioè il simulante è una macchina la cui funzione di transizione viene definita apposta, cioè sa fare quello, sa simulare il simulato, non è che sa fare altro, ok? Cioè, quindi partendo da ogni macchina non deterministica, la macchina deterministica simulante è progettata apposta, è tenoro, cioè è sartoriale. Noi prendiamo la macchina non deterministica, c'ha quel tipo di transizioni, noi li ficchiamo nella funzione di transizione di una macchina deterministica simulante e il simulante fa solo quello. Ok? A voi risulta che una macchina deterministica o non deterministica durante il calcolo può cambiare il proprio programma di esecuzione? No, cioè la funzione di transizione è carded, cioè la macchina quello fa, non sa fare altro. Quindi il simulante deterministico, sebbene in maniera più lenta, sostanzialmente fa quello che fa il simulato, ok? Non è una macchina generica, è una macchina progettata apposta che fa solamente quella cosa là. Però questa non è la nostra esperienza diretta di computer. La nostra esperienza diretta di computer è che un computer noi gli diamo un programma e quello sempre il programma che gli diamo. Cioè un un computer per quella che è la nostra esperienza diretta è qualcosa di programmabile, cioè qualcosa che la sua funzione di transizione pare prendersela da un programma. Ok? È chiaro? Dov'è qui l'inghippo, cioè fino ora abbiamo visto modelli di calcolo che fanno una sola cosa, cioè una volta che fissiamo la funzione di transizione sulla macchina di Turing, la macchina quello fa, non è che sa fare, sa svolgere altri compiti, quello sa fare un linguaggio sa decidere o accettare e non fa nient'altro. Però abbiamo detto che le macchine in Touring sono potenti quanto i computer nostri. E com'è sta faccenda, cioè i computer nostri, a differenza delle macchine di Touring, pare possano ricevere in input un programma, ok? e adattare la loro funzione di transizione al programma ricevuto, cioè il computer che noi abbiamo sul nostro tavolo svolge compiti, svolge programmi vari, non è come la macchina di Touring che ne svolge uno solo. Com'è sta questione? E adesso dobbiamo risolvere questo mismatch. mostreremo che e poi introdurremo nella seconda parte altri altri problemi, mostreremo che i nostri computer sono in realtà delle macchine di touring particolari, macchine di touring che andremo a definire ora, ok? Un computer è sostanzialmente quello che noi chiamiamo una macchina di touring universale. Ok? Questo è un computer macchina di Turing Universale. La macchina di tuning universale è un tipo di macchina di tuning che è uguale alle macchine che abbiamo visto finora, solo che è in grado di eseguire un programma. Questa è la differenza. Ok? è questo che lo rende universale, cioè la ci sono delle macchine di tuning che adesso vedremo, la macchina di tuning universale che è una macchina che è in grado di comportarsi come altre macchine se gli diciamo come fare. Ok, questa è l'idea. I nostri computer ricevono un programma e sulla base del programma pare che iniziano a svolgere cose differenti, no? E di fatto questa è la nostra esperienza. Possiamo scrivere un programma per calcolare una certa cosa. Possiamo scrivere un programma per calcolare un'altra cosa, no? Quindi pare che il tipo di calcolo che loro facciano dipende che loro fanno, dipende da un programma che noi passiamo e questo è diverso dalle macchine di touring viste finora. Una macchina di Turing che andremo a vedere ora è una macchina di Turing che è in grado di comportarsi come qualsiasi altra macchina di Turing nel momento in cui gli diciamo come si fa. Ok? È chiaro? Questo è il nostro obiettivo. Quindi, in questo primo pezzo di lezione noi andremo a vedere questo modello di calcolo che è la macchina di tingers che praticamente fa da ponte fra quello che abbiamo visto finora e i computer che siamo abituati a utilizzare. Ok? Allora, quello che ci dobbiamo inventare però a questo punto è come facciamo a insegnare una macchina di Turing a comportarsi come un'altra macchina di Turing. Questa è la cosa, cioè noi ci dobbiamo inventare un codice per i programmi, sostanzialmente come abbiamo il codice Java, C++, Python, whatever, ci dobbiamo inventare un codice che ci permetta di insegnare a questa macchina universale a fare le cose che fanno le altre macchine di Turing. È chiaro? Ok? Per le macchine che abbiamo visto finora, cos'è il programma di queste macchine? E la funzione di transizione, cioè la funzione di transizione delle macchine è il loro programma che abbiamo detto è fissato. Una volta che la macchina è progettata, costruita, quello non cambia. Ok? Quindi noi ci dobbiamo inventare un modo per codificare una le funzioni di transizione in maniera tale che queste possono essere usate come input di un'altra macchina e questa macchina universale prende il codice della funzione transizione di altre macchine e si comporta come quelle. Ok? Quindi è una macchina che avrà il potere di comportarsi come tutte le altre macchine, ok? Per questa ragione la chiamiamo macchina universale. Ok? Allora, ovviamente uno si può inventare tutti i modi che vuole per codificare le funzioni di transizione. Ce ne stanno a Bizzef, noi ne prendiamo uno preso dal libro di eh di Hopcroft. Ok? Allora, facciamo alcune semplificazioni su sul tipo di macchine che andremo a a simulare. Semplificazioni che non tolgono il potere esecutivo alle macchine, hanno lo stesso potere delle macchine che abbiamo visto finora. Ah, solo per semplificarci un po' la vita a noi. Se siete interessati nella dimostrazione di eguaglianza ci sta sia negli appunti di Calautti che nel libro di Hccroft. Ok? La macchina che consideriamo è una macchina in cui al tabellin input ha solo due simboli, 0, ok? E il eh questo qua e poi invece l'alfabeto di l'alfabeto di nastro può essere 01 blank più altra roba. Ok? Quindi noi dobbiamo codificare questa cosa. Allora, siccome noi dobbiamo codificare la funzione di transizione, la funzione di transizione ha generalmente questa forma. Se siamo in uno stato qui e leggiamo un simbolo XJ, allora andiamo in uno stato QK, scriviamo un certo simbolo L e ci muoviamo di una certa direzione di M. Ok? E questa è la definizione generale di una di un'istruzione, diciamo, della funzione di transizione. Ok? Che ad esempio avremo qualcosa del genere. Delta di Q3 che leggo blank, vado in Q5, scrivo 0 e mi sposto a destra. Ok? Quindi questo è quello che dobbiamo codificare. Allora, la codifica che facciamo è questa qua. Noi codifichiamo questi simboli cui X con J, QK, X con L e D con M partendo dai loro perici. Ok? Allora, ogni volta che noi, quindi noi faremo questa cosa qua, la codifica di Q avverrà tramite una sequenza di I0, ok? Per dire Q5 metteremo 5 00. Ok? La codifica di X noi metteremo J0. [Musica] Per la direzione noi assumiamo che sinistra sia D1 e destra sia D2. Quindi facciamo un esempio. Supponiamo che vogliamo codificare questa questo pezzo di funzione di transizione, ok? Questo piccolo codice. Allora, noi la codificheremo così. Q3 00 e questo ci codifica Q3. Dopodiché devo scrivere blank. L'assunzione che faccio perché x con 1 è il simbolo 0. Facciamo scriviamolo da un'altra parte. X con 1 è il simbolo 0, X con 2 è il simbolo 1, X con 3 è il simbolo bianco, X con 4 e altri sono gli altri simboli di nastro della macchina. Ce li facciamo, ce li assegniamo come vogliamo. Q1 è sempre lo stato iniziale. [Musica] Mi state vedendo? Ok. Q2 è sempre il singolo stato accettante. Ok? Quindi sono semplicemente assunzioni sui nomi che diamo. Dobbiamo codificare la funzione di transizione. Allora, abbiamo che gli stati sono Q1, Q2, Q3, bla bla bla. Q1 è sempre lo stato iniziale, Q2 è sempre lo stato accettante. Gli altri sono altra roba. Cioè li potevamo chiamare Pippo e Paperino, li chiamiamo Q1 e Q2. È la stessima cosa. Ok? Il simbolo X1 è sempre 0, X2 è sempre 1, X3 è sempre, tutti gli altri gli diamo il nome che vogliamo. Direzione uno è sinistra, direzione 2 è destra. Ok? Sono convenzioni, possiamo decidere quello che vogliamo perché dobbiamo semplicemente trovare il modo di codificare i pezzi della funzione di transizione in una stringa binaria. Questa è la questione, ok? Perché sennò alla macchina universale non glielo riusciamo a dire in nessun modo come comportarsi, gli serve una stringa input e la stringa che gli diamo in input deve codificare la funzione di transizione della macchina che vogliamo che la macchina universale simuli. Ok? Ora, quindi supponiamo di voler codificare questo pezzo qua. Allora, facciamo così, e questo ci codifica Q3. Dopodiché, per separare Q3 dalla codifica del bianco, siccome questo qui sarà un simbolo, sarà in particolare X3 e sarà codificato come di nuovo 00, io ci metto un 1 che mi separa, ok? Quindi io separo questo pezzo da questo pezzo con un 1, metto 1 e poi di nuovo blank che sarebbe x3 00. Questo è x3. che è il blanco. Dopodiché un altro uno in maniera tale che separo qui X5 Q5 1 2 3 4 e 5 e questo è Q5. Dopo c'è un 1 0 abbiamo detto che è il primo singolo, quindi c'è un non 0 soltanto. Questo è x1, poi c'è uno di nuovo destra che è 2, questo è d Ok? Allora, cosa abbiamo fatto? ci siamo inventati fra 1000 modi possibili un modo per codificare in binario, una funzione, un pezzo di funzione di transizione. È chiaro cosa abbiamo fatto? Abbiamo semplicemente dato nomi agli stati, nomi ai simboli, nomi allezioni e usiamo un certo numero di zeri per dire q1, q2, q3, un certo numero di zeri per dire x2, x5, x1000, un numero di zeri per dire d1, d bla bla. Le cose le separiamo con 1 e quella stringa binaria 0 secondo la nostra codifica ci sta codificando quel pezzo di funzione di transizione. È chiaro? Però questo codifica un sola entry, una sola entry della funzione di transizione. Noi abbiamo che la funzione di transizione è definita per varie coppie. stato simbolo. E allora come facciamo? Ne mettiamo una in fila all'altra. Quindi avremo un pezzo di codice di funzione di transizione C1. Questa qui sarà una stringa di zeri e 1 dove l'uno non compare mai più di una volta di fila. Ok? Quindi che faccio? Per mettere in fila tanti pezzi di codice, di funzione di transizione, io uso doppio 1 1 1 e qua scrivo il secondo pezzo della funzione di transizione 1 c3 1 c4 e così via, ok? E li scrivo tutti. Sì. [Musica] Sì, però pericarci la vita ce ne mettiamo ok. Si potrebbe fare, però poi la macchina simulante che deve andare a contare quanti sono c'è da un pazzien. Allora, usiamo uno. Si può fare? Sì, si può fare. Ok, allora siete ci siete con me che questo modo ci permette di scrivere in una mega stringa binaria di codificare cosa fa una macchina di tuning, cioè il suo programma è codificato dentro una stringa binaria. È chiaro? È chiaro come facciamo? È un modo. Ci sono infiniti modi. Noi scegliamo questo, ok? Perché il nostro obiettivo è poter comunicare a una macchina di touring come comportarsi rispetto a un'altra. Se dici, guarda, quest'altra macchina in presenza di questo simbolo e questo stato fa questa cosa qua, ma io glielo devo dire in qualche modo. Glielo dico in questo modo. Ok? È chiaro? Quindi in qualche modo, questo poi lo vedremo anche dopo, però abbiamo una prima intuizione del fatto che alcune, cioè che le stringhe binarie possono mess possono essere messe in associazione con possibili macchine di tuning. Ok? Adesso ci saranno stringhe binarie che non codificano macchine di tuning, cioè per esempio stringhe con 4-1 di fila non fa parte della nostra codifica. Ok? Quindi recap, una macchina di Turing può essere codificata guardando la sua funzione di transizione. Se ci inventiamo un modo di codificare la funzione di transizione, noi siamo in grado di trascrivere il programma di una macchina di tuning in una stringa binaria e ci siamo inventati quello. A questo punto vuol dire che c'è un'associazione fra stringhe binarie e macchine diing. Però questa associazione non è così forte perché ci stanno stringhe binarie che non non seguono quella codifica. Ok? Allora, assumiamo che le stringhe binarie che non seguono quella codifica sono macchine con un solo stato non accettante, cioè sono macchine che rifiutano tutto, dicono no su qualsiasio. Ok? Giusto un'assunzione. Quindi avremo molti modi di descrivere una macchina che non fa niente, ok? solo per convenienza. Chiaro? Allora, possiamo a questo punto, siccome ci siamo inventati un modo per codificare il funzionamento di una macchina di Turing, possiamo far riferimento alla macchina di Turing universale che chiameremo mu da ora in poi perché la useremo un po' di volte. Allora, che fa sta macchina diuring? Questa macchina di uring riceve in input una codifica di una macchina secondo la codifica che abbiamo visto, più una certa stringa input per quella macchina. E la macchina universale è in grado di dirci se la macchina ricevuta input tramite la codifica accetti o meno la strunga input. Ok? Quindi la macchina universale sarà Ok, mo spiego così. Ritorniamo a questa cosa qua. Sì. Eh, ok. È utile fare così. Abbiamo detto che questa è una stringa binaria, ok? Quindi possiamo associare le stringhe binarie a macchine di tuning. Notate che queste stringhe son particolari perché iniziano con zero. Vedete che iniziano tutte con zero. Allora, noi possiamo usare questo trucchettino. Se noi infiliamo un bell uno di fronte a una codifica di una macchina di ting, quello là sarà un numero intero. È chiaro? Quindi le macchine di Turing possiamo in effetti contarle. La prima macchina di Turing che è uno seguito dalla stringa vuota, la seconda macchina di Turing che è uno seguito da Z0, la terza macchina di Turing che è uno seguito da 1 e così via. Ok? Quindi noi possiamo in effetti associare a ogni numero, possiamo dire la terza macchina di Touring, la milionesima macchina di Touring. Come si fa? Si prende la codifica binaria, si toglie l'uomo iniziale, quello è il codice di marketing. Ok? Ci serviva per la definizione di dopo. Ok? È chiaro? Le stringhe binari, di nuovo, le stringhe binari che non seguono quella codifica, allora sono per noi macchine di Turing che dicono no su qualsiasi cosa, cioè il loro linguaggio è vuoto. Gli diamo un input, no? Gliene diamo un input, un altro? No, dicono sempre di no. Allora, la macchina di Turing Universale una macchina che riceve input una stringa binaria codificando che codifica, per esempio, la iima stringa, la iesima macchina e poi una stringa W. Ok? E questa macchina più in generale possiamo semplificare riceve coppie MW tale per cui M è una stringa che codifica la funzione di transizione della macchina M e W è un input per M. La macchina MU è una macchina che ricevuto input il programma M e l'input w* m è in grado di dirci se M avrebbe accettato - w. Ok? Quindi questa macchina universale è una macchina un po' particolare perché è una macchina il cui suo programma è fissato. Vedremo che è fissato, però input è in grado di ricevere queste coppie. Macchina M, stringa binario. La macchina M è codificata con la codifica che abbiamo visto prima. L'input è l'input che vogliamo dare impasto a M. La macchina universale è cosa è in grado di fare? Sostanzialmente la macchina universale simula M su w. Se M accetta W, mu dirà sì. Se M non accetta W, MV dirà no. Ok? Quindi è un un decisore universale, cioè perché è un decisore in grado di comportarsi come altre macchine che gli vengono descritte input tramite la stringa doppia, la stringa M che codifica una funzione di transizione. Chiaro per tutti come funziona questa macchina? Cioè qual è l'obiettivo di questa macchina? Questa macchina è una macchina che prende in input un programma e un input, un programma che stiamo chiamando M, che è che cosa? La funzione di transizione della macchina che chiamiamo M. E prendo un input per questo programma. La macchina universale è in grado di dirci se il programma M quando riceve W input risponde sì o risponde no. È chiaro? Ok. Come funziona questa macchina? Stavolta son venuto senza appunti, guardo velocemente. Alri. Ci sono vari modi in cui sta simulazione si può fare. Prendo quella di OPCroft. Ok, questa 1 2 3 4. È una macchina a quattro nastri. 1 e 4. Questa è MU. Eh, quindi la macchina MU ha quattro nastri. Noi sappiamo che una macchina multinastro può essere convertita in una macchina monoastro, quindi siamo nel reame delle cose fattibili. anche standard. Sì, la Q è o no? È la string è una stringa binaria perché stiamo assumendo che m il suo alfabeto di sia binario, cioè quindi il wio quello che m prenderebbe input. Sì, cioè, quindi anche questo all'inizio. Sì, cioè quello che m dice input è la codifica binaria di M e la stringa W che andrebbe in passo ad M. Quindi la stringa Wien alfabeto originale di M che è binario per la funzione. Ah, ok. Perché per semplificità si può dimostrare che le macchine con alfabeto binario hanno lo stesso potere di tutte le altre macchine. Ok? Quindi lo facciamo per semplice. Quindi l'alfabeto di input di M è binario, quindi W è proprio la stringa che nell'impasto a M. Ok? Allora, cosa fa la macchina U? Riceve input M, che è quella codifica là, e w. Vi ricordo che m cioè la codifica che noi ci siamo inventati per le macchine di Touring che è una lista di codici, cioè che per ogni entry della funzione di transizione abbiamo quello w1, un altro pezzo W1 eccetera. In m non compaiono mai 31 di fila, quindi m e w li dividiamo con 3 uni. Ok? In questo modo abbiamo doppio input sul nastro di input. Allora, come si comporta MU? MU si comporta seguendo passo passo quello che fa M sostanzialmente. Come fa a farlo? Perché nel proprio input su primo nastro ha la descrizione completa della funzione di transizione di m. Questo è il trucco. Ok? Quindi mo che cosa fa? Come primo passaggio prende W e lo ricodifica secondo, in questo caso, i simboli che ci siamo inventati, X1, X2, ok? e gli mette là, che ne so, se W era 01, questo abbiamo detto che è x1, questo è x2, questo è x3, la sua ricodifica che va a finire sul secondo nastro è 0 1 Ok, qua ne mettiamo un altro perché fa così. Ok, quindi qua c'è il eh ehm nastro di M ricodificato. Ok? Dopodiché la macchina U si segna lo stato iniziale che è un partiamo da Q1. Ok? A questo punto posiziona sul secondo nastro la testina sul simbolo visto da M e posiziona sul terzo nastro la testina sullo stato corrente di Mulato. Ok? La macchina guardando il proprio secondo nastro e il proprio terzo nastro, sa in quella fase di simulazione in quale stato è la macchina M e quale simbolo la macchina M sta leggendo. È chiaro? Chiaro? Perché? Perché noi stiamo simulando sul secondo nastro quello che sarebbe il contenuto di nastro di M. Su terzo nastro noi teniamo traccia di qual è lo stato corrente di N. Ok? Quindi da terzo nastro noi ci prendiamo lo stato corrente. Qui sto descrivendo il funzionamento di MU. Quindi MU che fa? Va su terzo nastro. Guardando su terzo nastro può sapere qual è lo stato simulato di M. guarda sul secondo nastro e può sapere qual è il simbolo letto da Mulla simulazione del nastro di M, che è il secondo nastro di Mo. Da questa coppia la macchina che fa? Va sul primo nastro dove c'è la codifica della funzione di transizione di M e va alla ricerca di un pezzo di codice, cioè di un entry per la funzione di transizione che è esattamente per la coppia stato simbolo attualmente simulato. È chiaro? Quindi leggendo quel pezzo di codice, la macchina universale che cosa saprà? Saprà in quale stato transire, cosa scrivere e dove spostare la destina. Avete seguito fin qua? Quindi cos'è che fa sta macchina MU? La macchina MU simola la macchina M passo passo come tenendo traccia di due cose, il nastro della macchina M e lo stato corrente della macchina M. Il nastro della macchina M noi lo stiamo simulando sul secondo nastro di MU. Lo stato corrente della macchina M lo stiamo ottenendo traccia sul terzo nastro di MU. Quindi che facciamo? Andiamo a leggere il simbolo su secondo nastro, lo stato su terzo nastro. Andiamo alla ricerca su primo nastro, dove c'è la funzione di transizione di m, andiamo alla ricerca di un entry della funzione di transizione per la coppia stato simbolo che abbiamo appena guardato. È chiaro? Una volta che leggiamo questo, la macchina universale che può fare? può aguornare lo stato perché sappiamo quale sarà il nuovo stato di M e può andare a ritoccare il nastro simulato di M andando a sostituire il simbolo. Ok? Adesso i simboli che noi abbiamo sul secondo nastro sono dei simboli simulati, sono hanno questa forma qua, quindi potrebbe essere necessario che ci serve un po' di spazio o comprimiamo un po' di cose. Ecco a che serve il quarto nastro serve a fare un po' dii lavori di copia incolla, ok? Per estendere o accorciare il secondo nastro. È chiaro come funziona questa macchina? La macchina universale? Sì, prego. Ma sul secondo nastro eh viene tradotto sia M che W o soltanto R? Eh no, su primo nastro sul secondo nastro c'è la simulazione di cosa sta facendo M su do. Ma io non tocco mai il primo nastro? No. Ok? Perché il primo nastro c'è il programma e io il programma non lo voglio cambiare. Io sul primo nastro vado a guardare le entry della funzione di transizione per rendermi conto ma m in questa situazione che farebbe? Ah, transisce in questo stato, scrive sta cosa e sposta a destra. E io che faccio? Aggiorno il terzo nastro per dire qual è il nuovo stato. Modifico il contenuto del secondo nastro per simulare il contenuto della macchina simulata e poi sposto la testina sul secondo nastro indipendenza o a destra o a sinistra, indipendenza di dove m andrebbe, ma quindi eh io tengo anche traccia di cosa sta succedendo in input leggendo il primo nastro, oltre a simulare certamente sul secondo, no? Quello sul secondo nastro noi stiamo simulando quello che la macchina M farebbe sul proprio nastro, ma non va appunto con lo scopo di risolvere l'input che gli Sì, sì, sta facendo lo stesso lavoro di M, lo sta simulando. È chiaro? Perché fa passo dopo passo dice: "Ok, in che stato si trova la macchina M?" Che cosa farebbe? Ok, lo replico. Vado a guardare in che condizioni mi trovo. Ah, ok, stato singolo. Vado a riguardare la funzione di transizione. Che farebbe m in questo caso? Si addormenterebbe. No, si prende un gelato, no? Fa questa cosa qua, aggiorno il terzo nastro, aggiorno il secondo nastro e vado avanti. Cioè, per ogni passo di m io faccio replico la stessa cosa sul secondo nastro e tengo aggiornato il terzo nastro per sapere lo stato corrente di m. Il quarto nastro lo uso per scarabocchi. Ok, scusi se glielo chiedo, peròamente chiaro. E sul secondo nastro, cioè io quindi copio, traduco la solo la parte input, non la macchina di touring. Sì, solo il W e quindi eh secondo la codifica che ci serve. Ok. Mentre mi tengo conto di cosa devo fare mediante il primo nastro. Mediante il primo nastro. Sì. Sì, prego. Ma se la blop input mettiamo che c'è 01 Sì. E quindi 0 con 10 e quell'altro è 100. Sì. La magia mi dice quale? M o mu? M M mi dice trasforma questo 0 in uno. Sì. Quindi dovrei cambiare 0 e con Quindi ti serve dello spazio. Ok. Devi fare spazio. Usi il quarto nastro per fare scarabocchi, cioè per fare copia incolla e crearti lo spazio che ti serve. Ecco a che serve il portonastro. Chiaro? Chiaro per tutti che questa macchina MU fa passo passo quello che fa la macchina M. Quindi se la macchina M a un certo punto arriverà a uno stato accettante, la macchina MU se ne accorge e accetta. Se la macchina MU se la macchina M si blocca in uno stato non accettante, pure la macchina MU si bloccherà da qualche parte e rifiuta. Se la macchina M non si ferma mai, la macchina MU andrà avanti all'infinito e come M non accetta. Quindi è chiaro che la macchina MU sta sostanzialmente eseguendo il programma M. In questo modo ci siamo creati una macchina di Turing programmabile, cioè è una macchina di Turing universale, cioè una macchina di Turing che è in grado di simulare il comportamento di altre macchine di tuning. Domanda la funzione di questa macchina, questa la chiamiamo la macchina universale, no? Per convenzione chiamiamo la transizione, la funzione di transizione universale il programma della macchina universale, cioè il fatto che sta macchina simula le cose. È chiaro? Cos'è la funzione di transizione universale? Ok. La macchina di Turing universale durante la propria esecuzione cambia la propria funzione di transizione universale? No, è sempre quella, fa sempre quel lavoro. Ok? Quindi questa macchina, cioè, non è che cambia la sua funzione di transizione, ha una funzione di transizione fissata che è in grado di simulare il comportamento di macchine con altre funzioni di transizione. È chiaro? Cioè, questo deve essere chiaro. Cioè, quindi questa macchina non è che veramente caniante. Questa macchina di nuovo lo sa fare solo una cosa, mette in atto la funzione di transizione universale che però è particolare e gli permette di simulare il comportamento di ante. Ultime osservazioni e poi facciamo pausa. I computer che noi abbiamo sono computer, sono macchine universali di più, chiaro? Quindi avranno una funzione universale fissata. Qual è? Sì, il ciclopaspetto del CPU. È il micro della CPU. Cioè il microcodice della CPU è la funzione di transizione universale dei computer che usiamo. Pure i computer che noi usiamo in realtà loro non è che eseguono un programma che noi gli diamo. Loro un programma eseguono che è il microcodice della CPU che fanno? prendono da memoria un istrizione, fetch, la decodificano, la mettono in atto e vanno a prendere l'altra e continua così, all'infinito. Quindi un microprocessore reale è in effetti una macchina di Turing universale, la cui funzione di transizione universale è il micropodice del processore. È chiaro? Allora, quindi questo copre il gap tra Ma com'è che le macchine di puring non si programmano i computer non si si programmano, nemmeno quelli si programmano. Cioè la loro programmazione è che entrambi hanno una funzione fissata. Ok? La ma during ha la funzione di transizione universale, i processori hanno il microcodice. Una esegue la funzione di transizione universale per simulare altre macchine. I processori usano il loro microcodice che è fissato per eseguire programmi generici. Ok, è chiaro? Pausa. Facciamo qua. Alr [Musica] mi prendi. Ok, let's start again. Quindi a un certo punto abbiamo parlato dellesima macchina M eccetera e avete visto che poi non l'ho usata perché non serviva. Serve ora. Ok. Ah, ok. Quindi io questo lo cancello proprio così non vi confondete semmai doveste rivederlo. Questo ci serve dopo. Alrght, quindi l'input della macchina universale, abbiamo detto che una coppia macchina codificata in un certo modo stringa la macchina universale è in grado di fare questo lavoro interessante di simulare la macchina N su W e darci la stessa risposta. Ok? Quindi potremmo tra virgolette dire la macchina universale è una macchina programmabile, ok? Perché riceve in input il programma da eseguire. Ok? Allora, abbiamo introdotto l'altra volta enumerotto l'altra volta le due classi di complessità di calcolatità che sono R ed R e ci siamo detti che insomma i problemi possono stare in R, re R e e zone. Ok? Uno di voi mi ha chiesto "Ma ci stanno problemi qua?" "Sì, ci stanno problemi lì" e noi andiamo a vederli, ok? Andiamo a vedere un problema che non è ricorsivamente numerabile, ok? E lo definiamo ora. Poi vedremo un po' di proprietà di linguaggi e chiudiamo per oggi. Alri? Allora, se noi adesso entri in ballo la codifica che abbiamo parl di cui abbiamo parlato prima, allora riconsideriamo nuovamente macchine che processano in input stringhe binari, ok? Quindi da adesso in poi ci focalizziamo su macchine che il cui input è 01, ok? Perché comunque hanno il potere delle macchine che hanno 1000 simboli input, si può tutto riquare, ok? Allora, se noi prendiamo una stringa binaria, no, 0 1 0 bla bla bla, con quel trucchetto che vi dicevo prima, che ora serve prima non tanto, se noi mettiamo un uno davanti a questa stringa binaria, noi a quella stringa binaria possiamo associare un numero intero, ok? Che è il numero dato da quella codifica con l'uno davanti. Perché ci mettiamo l'uno davanti? Perché tante stringhe binarie possono iniziare con zero e quindi sarebbero indistinguibili. Allora, ci mettiamo l'uno in maniera tale che noi possiamo parlare della prima stringa binaria che è che è la stringa vuota. La seconda stringa binaria che è zero perché sarebbe 1 concatenato 0. La terza stringa binaria che è 1 perché avremo uno concatenato 1. La quarta stringa binaria, ok? E così via. A questo punto noi siamo in grado di enumerarle e in particolare abbiamo che le stringhe binarie con un indice più basso perché a questo punto abbiamo la prima stringa, la seconda stringa, la terza stringa, ok? Noi avremo che le stringhe binarie con indice più basso sono tendenzialmente più corte, ok? stringhe binarie della stessa lunghezza sono ordinate lessicograficamente, ok? Quindi noi abbiamo in effetti proprio un ordine di queste stringhe, le possiamo enumerare 1 2 3 bla bla bla. Ok? Quante sono le stringhe binari? Quindi infinite. Ma sono tante quanti i numeri inferi o più dei numeri interi? Numerabili. Ok, sono numerabili. Quindi abbiamo tante quante di numeri interi perché li possiamo contare la prima, la seconda, la terza. C'è quindi una associazione di uniloga fra l'indice e la stringa. Le possiamo proprio contare, quindi sono eh contabili. Ok? Lo stesso tipo di discorso si può fare per le macchine diing. Le macchine di ting, perché siccome ci siamo inventati con la codifica che abbiamo visto prima e quindi abbiamo che una macchina di turing può essere associata a una stringa binaria, ok? Se noi ci impacchiamo l'uno davanti, quello lì un di conseguenza pure le macchine di Turing possono essere contate. Adesso avremo macchine di Turing che riconosceranno lo stesso linguaggio perché magari hanno funzioni di transizione differente, però alla fine fanno la stessa cosa. Ok? Esattamente come noi possiamo scrivere infiniti programmi Python per risolvere un certo problema, ci saranno macchine di touring differenti con programmi differenti che accettano e si esano lo stesso identico linguaggio. Ok? Quindi poi come abbiamo detto ci stanno tante macchine di tanti codici di macchine di Turing che in realtà non rispecchiano nessuna macchina di tuning secondo il nostro codice. Lì assumiamo che la macchina di tuning è qualcosa che rifleta tutto, dice no su qualsiasi input. Ok? Quindi esattamente come possiamo contare le stringhe binarie, possiamo contare le macchine diingine di fiori non sono vulnerabili. Abbiamo la prima, la seconda, la terza, la quarta e così via. Quante sono? Sono tante quanti i numeri naturali, ok? Quindi abbiamo infinite stringhe binarie e infinite macchine di touring e la loro numerosità è la stessa, sono tante quanto i numeri naturali. Ci inventiamo ora un particolare linguaggio. Prima di andare là definiamo cos'è per un linguaggio L il vettore caratteristico Q di L del linguaggio L. Che cos'è il vettore caratteristico di L? Il vettore caratteristico del linguaggio L è una sequenza binaria 0 1 1 bla bla bla infinita che ci dice siccome noi siamo in grado di enumerare tutte le possibili stringhe, la prima, la seconda, la terza, la quarta, eccetera, Il vettore caratteristico del linguaggio L denotato in questo modo Q di L è un vettore binario in cui in posizione I ci dice se la stringa iima fa parte del linguaggio o meno. Ok? Recap Noi possiamo enumerare le stringhe, possiamo dire questa è la prima stringa, questa è la seconda stringa, questa è la terza stringa e così via, no? Di conseguenza, siccome un linguaggio, per definizione è un sottoinsieme delle stringhe costruibili su un certo alfabeto e in questo caso stiamo considerando un alfabeto binario, allora il vettore caratteristico di un certo linguaggio sostanzialmente ci dice quale stringhe fanno parte di quel linguaggio. Ci dicono in questo caso, la prima non fa parte, la seconda fa parte, la terza fa parte, la quarta no, la quinta no, la sesta sì, bla bla bla. Ok? È chiaro? E un linguaggio sull'alfabeto binario che cos'è? È un insieme di stringhe binarie. Ok? Alcune stanno nel linguaggio, altre stanno fuori il linguaggio. Per codificare un linguaggio, cioè per descrivere un linguaggio, possiamo inventarci questa lezione di vettore caratteristico del linguaggio. Lo possiamo fare perché siamo in grado di enumerare le stringhe, siamo in grado di dire qual è la prima e qual è la seconda. Di conseguenza, se noi ci mettiamo un bel vettore dove in prima posizione mettiamo zero, se la prima stringa fa parte del linguaggio o in prima posizione mettiamo uno se la prima stringa fa parte del linguaggio e così via. Quindi in posizione i di questo vettore mettiamo 0 se la stringa i possiamo parlare di stringa iima perché sappiamo come numerarle, in posizione iesima di questo vettore ci sta zero se la stringa w con i che noi assumiamo essereima stringa non fa parte del linguaggio. ci mettiamo uno in questo vettore caratteristico se la stringa i doc parte del linguaggio L. E chiaro che cos'è il vettore caratteristico di di un linguaggio L? È semplicemente una stringa binaria che ci dice se una certa stringa i fa parte o non fa parte del linguaggio. Tutto qua. È chiaro? Chiaro per tutti? Perché ora dobbiamo giocare su sta cosa e inizia un po' a girarci la testa. Quindi mi devo essere sicuro che cos'è un vettore caratteristico mi sia chiaro. È l'elenco, potremmo dire l'elenco delle stringhe che fanno parte del di un certo linguaggio. Mettiamo zero se la stringa i mettiamo 0 in posizione i se la stringa i esima non fa parte del linguaggio. mettiamo uno in posizione i di questo vettore se la stringa e iima fa parte del linguaggio. È una lista binaria. Sì, no, no, sì, sì, no, bla, bla. Ok? Quindi sostanzialmente il vettore caratteristico ci dice chi fa parte del linguaggio e chi non fa parte del linguaggio. Chiaro? Ok, ci facciamo una bella tabellina. Mettiamo sulle colonne le stringhe. V1, V2, V3, V4, bla bla bla bla. Quindi prima stringa, seconda stringa, terza stringa, quarta stringa, zone. Possiamo farlo perché le sappiamo enumerare. Sulle righe mettiamo le macchine di doing che anche queste possiamo enumerare perché abbiamo trovato un codice che associa macchine a numeri. Quindi mettiamo la prima macchina, la seconda macchina, la terza macchina, la quarta macchina e così via. Ok, ci siete? Anche le macchine Touring possono essere enumerate nel modo in cui abbiamo visto. Quindi possiamo parlare della prima macchina, della seconda macchina, della terza macchina. Sì. E nel taso me rigore ci sono particolari che non vanno bene perché tipo quando si definisce una funzione di transizione della psicologica m m eh qual è stata prima? Cioè, come scusi quali quali coppia ha stato eh ci sono più ricodifiche della stessa macchina. Sì, alcune sono duplicate. Sì, alcune sono duplicate, altre sono diverse, ma riconoscono lo stesso linguaggio, quindi sì, c'è dei doppioni, ci stanno. Sì, quello sì, quello è fuorigio. Eh, al più potremmo farlo, potremmo farlo, decidere che si codificano quelle prima con indice più basso. i pezzi di funzione di transizione che prendono le coppie con indici più bassi eccetera, uno se le può inventare, però in line principio al più ci stanno dei doppioni. Ok? Alright. Allora, ogni macchina di touring abbiamo detto che accetta un certo linguaggio, no? Quindi ogni macchina di touring su una certa stringa ci dirà sì, no, sì, no. Ok? Quindi supponiamo che la prima macchina di Turing rifiuta la prima stringa, accetta la seconda, rifiuta la terza, rifiuta la quarta e così via. Che la seconda macchina di Turing accetta la prima, accetta la seconda, non accetta la terza, accetta la quarta e così via. La terza macchina di Touring non accetta la prima, non accetta la seconda, non accetta la terza, accetta la quarta e così via. La quarta macchina di Turing accetta la prima, non accetta la seconda, non accetta la terza, accetta la quarta e così via. No, è giusto un esempio. Ok? Allora, nelle righe di questa tabella, che cosa abbiamo? Abbiamo il vettore, attenzione qua perché iniziamo a muoverci su vari livelli, eh, sulle righe di questa tabella che cosa compare se prendiamo una riga? Cioè, se noi prendiamo un'intera riga, tipo questa qua, bla, questa riga infinita, che cos'è quello? Quello è il vettore caratteristico del linguaggio accettato da lati. È chiaro? È chiaro che cos'è quello? Perché i vettori caratteristici ci siamo detti che sono liste di 0 1 che ci dicono questa stringa sì, questa stringa no, questa stringa sì, questa stringa no. Ok? Quindi, sulle righe di questa sulle righe di questa matrice abbiamo i vettori caratteristici, ok? I vettori caratteristici dei linguaggi accettati da queste marche di Turing. Ok, ci siete? Adesso iniziamo a fare una piccola operazione. Spazio C. Ok, più o meno. Sì, voi vedete? Yes. Ci concentriamo sulla diagonale di questo linguaggio, di questa matrice. Ok? Ci prendiamo questa qua, guardate, e la chiamiamo D. Ok? I matematici immagino che sappiano già dove stiamo andando a parare e quindi abbiamo 0 1 0 1. Ok? Questo qua che cos'è? È la diagonale di questa matrice. Ok? Che cos'è? È una sequenza di bit, ok? è una sequenza di buleania. Ok? Da Deriamo il suo complemento, cioè prendiamo la sequenza D e la invertiamo. Dove c'è Z0 ci mettiamo uno, dove c'è uno ci mettiamo Z0, quindi abbiamo 1 0 1 0 bla bla bla e così via. Ok? di soprasegnato. Che cos'è? È una sequenza di bule o no? Siccome di soprasegnato è una sequenza di buleani, possiamo interpretarlo come vettore caratteristico di un certo linguaggio? Sì. Chi ce lo vieta? Nessuno. Ok, ripartiamo. Abbiamo fatto questa matrice, abbiamo preso la diagonale, l'abbiamo complementata e otteniamo 10 bla bla. Questo è un esempio, eh, è giusto per ragionare. Siccome d complementata è una sequenza di bit e i vettori caratteristici dei linguaggi sono sequenze di bit, questa qui può essere interpretata come il vettore caratteristico di un certo linguaggio. Ok? chiamiamo LD il linguaggio il cui vettore caratteristico è questo qua. Ok? LDX sta per linguaggio di diagonalizzazione. È chiaro finora che abbiamo fatto? Ci siamo costruiti sta bella matricetta, ci siamo presi la diagonale, l'abbiamo complementata. Stiamo interpretando il complemento della diagonale come un vettore caratteristico di un linguaggio. Chiamiamo LD il linguaggio il cui vettore caratteristico è questo qua. Ok? Possiamo fare questa operazione? Abbiamo fatto cose strane? Non abbiamo fatto questa strada. Ok, adesso la domanda è: esiste o no una macchina di tuning il cui linguaggio è LD? Mh. Ok, aspetta, qua ho visto un po' di votanti per il sì. Ok, adesso seguite questo ragionamento. Allora, noi in questa in questa matrice, in questa matriciona, in questa matriciana, ok, che cosa abbiamo? Abbiamo la lista di tutte le macchine di Turing. Perché? Perché le macchine di Turing possono essere numerate, quindi noi ne abbiamo infinite, però noi sappiamo esattamente la 4 miliardesima qualè. sappiamo qual è la milionesima perché le possiamo enumerare la prima, la seconda, la terza e insomma. Quindi in questa matrice noi riga per riga sappiamo chi è la macchina diing e lì ce stanno tutte, eh, non ne stiamo saltando perché tutte le macchine di tingono essere codificate secondo la codifica che abbiamo visto prima della pausa e quindi tutte le macchine di Turing possono avere un numero associato. Ok? E quindi in questa mega lista, in questa mega matrice, ci sta una riga per ognuna delle possibili macchine di touring. Non ne saltiamo nemmeno una, ok? Quindi se esiste una macchina di tuning che è in grado di riconoscere LD deve apparire dentro una alla a una certa riga. Ok? Per forza, perché là ce stanno tutte. Ci siete fin qua? Domanda. Può essere M1 questa macchina che stiamo cercando? No, perché perché differisce perché differisce il vettore caratteristico di m1 sicuramente differisce almeno in posizione 1. Quindi la macchina 1 un può essere. Andiamo avanti. Può essere la macchina due. Matematici già lo sanno, quindi lasciate la suspense agli altri. La macchina M2 può essere. Sì. No, forse no, perché quale e poi eh anche il secondo riferiscono sul secondo bit. Guardate il pattern. Eh, la prima macchina ha un vettore caratteristico che differisce da d soprasegnato in posizione 1 per definizione, perché abbiamo preso la diagonale e l'abbiamo rivoltata. Quindi il vettore caratteristico della macchina numero uno differisce da di soprasegnato almeno in posizione 1. Poi può differire anche in altri posti, ma in posizione uno differisce. Quindi il vettore caratteristico della macchina 1 non è di soprasegnato. Ok, andiamo a guardare la macchina 2. Il vettore caratteristico della macchina 2 equivale a di soprasegnato? No, perché il vettore caratteristico della macchina 2 differisce da di soprasegnato, almeno in posizione due. Poi magari è diverso pure da altre parti, ma in posizione du è diverso. Quindi nemmeno la macchina 2 va bene? Guardiamo la macchina 3. Il vettore caratteristico della macchina 3 equivale a di soprasegnato? No, perché il vettore caratteristico della macchina numero 3 differisce rispetto a D soprasegnato in terza posizione. Prendiamo la generica macchina I. È possibile che la macchina I abbia come vettore caratteristico disoprasegnato? Mh. No, perché posizione No, perché miima posizione il vettore caratteristico della macchina numero i differirà da di soprasegnato in posizione i. Ma allora nessuna di queste macchine ha come vettore caratteristico di soprasegnato, il che significa che non esiste una macchina di tingetti il linguaggio il cui vettore caratteristico è di soprasegnato. Quindi non esiste una macchina di tuning che accetti LD. Ma siccome in questa mega matrice abbiamo la lista di tutte le macchine di Turing che noi potremmo mai inventarci, se non ci sta proprio una macchina di Touring che accetta, allora vuol dire che è un linguaggio che sta fuori e re. È chiaro? Ma dice, "Ma è un po' artificiale questo linguaggio? Che cavolo è questo LD? Perché pare che ce lo siamo inventato apposta." Sì, ce lo siamo inventati apposta, però ha un significato. Quindi LD è qualcosa che sta qua fuori, è un linguaggio che sta fuori e re. Prego. Ma noi nello stesso modo in cui stiamo prendendo tutte le macchine diing esistenti, non stiamo prendendo anche tutte le string del corpo esistenti. E questa diagonale non fa parte di questo esemp, cioè di questo insieme, di tutte le Tu non prendi la diagonale, lei prende la diagonale complementata. Però anche la diagonale complementata non fa parte di questo. Eh, ok, supponiamo che faccia parte. Deve far parte di una di una linea. Deve essere uguale a una delle linee di questa matrice. Mi sa dire quale? Spari a caso. Eh, non può essere perché comunque in una cioè in un in un indice della riga c'è a lungo che differisce che differisce quindi la diagonale complementata non apparirà mai come riga. in una di queste in una di questa matrice non apparirà mai come rida dentro questa matrice la diagonale complementare. Di conseguenza, siccome noi abbiamo tanterie quanto sono tutte le macchine di tuning che mai potremmo inventarci, allora vuol dire che non ci sta proprio una macchina di tuning il cui linguaggio è LD, perciò LD non è RE, non è ricorsivamente numerabile, non esiste una macchina di Turing che è in grado di accettare MD. Adesso che cavolo è LD? Perché è stato definito un po' ad eh LD è l'insieme delle stringhe vi tale che la macchina iesima non accetta la propria stessa codifica. Ok? Quindi che contiene LD? LD è l'insieme dei codici di macchine di Touring che quando ricevono in input la propria stessa codifica della funzione di transizione dicono no. È chiaro che è questo linguaggio LD? Le macchine di Turing che fanno? Piano in input string e rispondono o sì o no. Ok? Consideriamo le macchine di Touring che prendono input stringhe binari, ok? Perché ci semplifica la vita. Macchina di touring che prende in input, una stringa binari. Gliene possiamo dare tanti input di string binari. Fra le varie stringhe che gli possiamo dare input, gli possiamo dare in input la stringa binaria che codifica la funzione di transizione di quella macchina. Ok? Se quella macchina ricevendo input la propria codifica, risponde di no, allora quella stringa fa parte di LD. Quindi LD contiene i codici di macchine di Touring che quando ricevono un input, la propria stessa codifica, rispondono no. Quella è l'ID. Sì. Ma quindi con la codifica tr di altre macchine si può fare oppure no? Sì, sì. Ma quindi qua per stare nelle D la macchina I deve rispondere no quando riceve input w con I. Ma se ho se posso care un input eh la codifica di un'altra macchina non si andrebbe un po' come controsenso al No, no, aspetta, la definizione del linguaggio è questa la cioè noi vogliamo definireci il linguaggio come ci gira l'insieme delle stringhe che che codificano i grafi belli. Ok? definiamo cos'è un grafo bello, lo possiamo fare, ok? Noi definiamo L in questo modo. È l'insieme dei codici binari che codificano macchine per le quali se loro ricevessero in input la propria stessa codifica risponderebbero no. No, qualche cosa facciano sugli altre non ci importa, ma per stare lì in LD M con I deve essere una macchina che sulla propria codifica dice di no, poi sul resto fa quello che gli gira. Però la mia domanda se guardo il problema della fermata, cioè presumo stimata un po' a parlare su questa cosa qui e più o meno. Però quest è un'altra cosa. Linguaggio di diagonalizzazione poi lo facciamo il problema della fermata o domani o venerdì, cioè o giovedì o venerdì, ok? Che poi lo guardiamo più altri tempi. Però noi volevamo un linguaggio che non fosse il re. Questo non è R perché perché non esiste una macchina di Touring che è in grado di accettare le Come lo sappiamo? Perché nel momento in cui le listiamo tutte, ok? Allora nessuna di loro ha il vettore caratteristico della della diagonale complementata. Ok? Nessuno ha il vettore caratteristico di L. Quindi teorema adesso l'abbiamo abbiamo dimostrato così a chiacchiere, ma sostanzialmente si riduce a questa cosa. Teorema LD non appartiene ad R. Come si dimostra? in quel modo facendo la matriciona, facendo la lista delle macchine, facendo la lista delle stringhe e facendo notare che LD ha come vettore caratteristico la diagonale complementata. Ma per definizione la diagonale complementata non può essere la riga di non può essere nessuna riga di quella matrice, ok? Tale per cui quella macchina non c'è sta. Chiaro per tutti? Ok, intuizione dietro sta roba perché esistono i linguaggi non ricorsivamente numerabili e poi introduciamo sì alcune proprietà di linguaggi che ci servono per la prossima volta, cioè perché ci stanno i linguaggi non decisibili. Sì, scusi, ma eh per dimostrare alla fine usiamo nella matrice prima si usa la matrice, sì, però prima dimostrare che per come ha definito adesso eh coincide con quella regolandita. Esattamente. Sì, sì, sì, sì, sì. Beh, poi la dimostrazione è quella e poi eh scusi mi son perso la e il linguaggio che i codici binari i codici di macchine di Touring le quali ricevendo input la propria bonifica la rifiutano, quindi non riconoscono la propria non riconoscono rispondono di no sulla propria modifica. Ok? Tutto qua. È un linguaggio un po' strano, però uno si può inventare il linguaggio che vuole. Ok? Allora, giusto un'intuizione, perché ci stanno i linguaggi indecisibili. Questa è una cosa che immagino che i matematici l'avranno già fatta. Allora, noi abbiamo detto che le macchine di Turing sono enumerabili, noi le possiamo contare, possiamo dire la prima, la seconda, la terza. Il che vuol dire che le macchine di Touring sono tante quante i numeri interi. I linguaggi che sono sono sottoinsieme di quelle stringhe, sotto insieme di V1, V2, V3, V4. Quindi abbiamo che le stringhe sono infinite, tante quante i numeri interi. I linguaggi sono tutti possibili sottoinsiemi di un numero infinito di stringhe. Quanti sono tutti possibili sottoinsiemi di un numero infinito di stringhe? Numeri reali son tanti quanti numeri reali son molti di più. Quindi i linguaggi sono tanti quanti i numeri reali, le macchine di touring sono tante quanti sono i numeri naturali, quindi abbiamo molti più linguaggi che algoritmi. Ecco perché ci sono linguaggi indecidibili, perché sono tanti di più. Poi la nostra testa non li partorisce perché noi siamo abituati a pensare in maniera tale che le cose riusciamo a risolverlo, ma in realtà per definizione siccome i linguaggi sono tanti quanti i numeri reali e le macchine di curing, che sono gli algoritmi sono tanti quanti i numeri interi, i numeri naturali, ci stanno molti più linguaggi algoritmi. Ecco perché ci stanno i linguaggi per i quali non esistono macchine che sono in grado di leggire. Tutto qua perché sono tante di più. Questa è la ragione filosofica che ci sta dire perché perché son tanti. Sono tanti di più. Ok. Ok. Introduciamo un paio di proprietà sui linguaggi e poi chiudiamo. Sia l un linguaggio su un certo alfabeto sigma star. Ok. Su un certo Sì. Su un certo alfabeto sigma e quindi L sigma star. Definiamo L soprasegnato come il complemento del linguaggio L. Intuitivamente cos'è il complemento del linguaggio L? È il contrario di L. Quello che sta in L non sta in l sovrasegnato, quello che non sta in Lassegnato. Di conseguenza L sovrasegnato viene definito come sigma start - L. Ok? è il contrario. Tutto qua. Questo è il complemento di un linguaggio. Prima proprietà teorema. Se L appartiene ai linguaggi ricorsivi, allora L soprasegnato è quello di ricorsivo. Chiara la proprietà? Se per L abbiamo un algoritmo che è sempre in grado di dire sì o no, allora abbiamo un algoritmo che è sempre in grado di dire sì o no anche per il complemento. Dimostrazione. Siccome L è un linguaggio ricorsivo, esisterà una macchina MDL che è sempre in grado di dirci o sì o no. Ok? Per definizione, siccome L è ricorsivo, c'è una macchina deterministica, come vogliamo, che su ogni stringa è sempre in grado di dire sì, la stringa appartiene a L, no, la stringa non appartiene L. Ok? Perché esiste tale ML? perché L e lo stiamo assumendo ricorsivo. Allora, noi possiamo costruire una macchina ML soprasegnato, cioè una macchina per questa prende in input qualcosa, una cosa di questo tipo. ML Se questo è sì, rispondiamo no. Se questo è no, rispondiamo sì. Cioè, praticamente noi la macchina per L soprassegnato noi semplicemente come la possiamo ottenere? Prendiamo, siccome stiamo assumendo che L sia un linguaggio ricorsivo, quindi una macchina che decide L ci sta. Prendiamo la macchina per L e la rivoltiamo. Praticamente che facciamo? l'unico stato accettante di Lventa non accettante e tutte le transizioni non previste dalla funzione di transizione di L inseriamo in questa nuova macchina ML soprasegnato in maniera tale che tutto ciò che la macchina ML non si aspetta la macchina ML soprasegnata le va a far finire in un nuovo stato accettato. Ok? Quindi tutto quello che veniva rifiutato dalla macchina ML verrà accettato dalla macchina ML soprasegnato e tutto quello che veniva rifiutato dalla macchina ML verrà accettato dalla macchina ML soprasegnata. È chiaro? L'intizione è questa. Noi cambiamo gli stati. Quello che è l'accettante per un diventa non accettante. Tutte le computazioni, no? Le mosse non previste da ML verranno previste da ML sovrasegnato e vanno a finire in uno stato accettante aggiuntivo che aggiungiamo. Ok? Giusto per far sì che ogni volta che ml dice di no, ml soprassegnato dice di sì e ogni volta che ml dice di sì, ml soprassegnato dice di no. Ok? Prego. Un chiarimento. Eh, ML segnato. Sì. Cioè noi eh facciamo seguire le cose delle ml e poi invertiamo il risultato oppure quella è solo una descrizione intuitiva. Noi ml sovprasegnato lo possiamo tenere da ML come rivoltandogli la funzione di transizione, cioè la funzione di transizione di ML prevederà delle transizioni verso stati accettanti e prevederà dei punti nel quale ML si blocca. Ma è solo una definizione teorica per No, se la definizione di ML. No, scusi, ero avanti sul perché noi, appunto, vi sta scrivendo che non definiamo dei punti di transizione complementari, cioè tutti quelli che non esistono, però cioè no, tutte quelle che non esistono. Si prende la funzione di transizione di ml e la si modifica per ottenere la funzione di transizione di ml soprasegnato. Come facciamo? Lo stato accettante di ML diventa non accettante. Aggiungiamo uno stato accettante e inseriamo tutte le transizioni non previste da ML. Per esempio, se in ML sono in Q4, leggo il simbolo A e mi blocco, in ML soprasegnato, in Q4, io avrò una transizione uscente con A e vado in uno stato accettante, in maniera tale che accetto quello che è non accettabile. È chiaro? Prego. Se la condizione di questo teorema è che L appartenga R, Sì. Perché non possiamo fare la stessa cosa su LD di di prima? LD non appartiene ad R. Eh, però nel senso LD è la diagonale de Gata di di D. Poi facciamo, cioè se la diagonale di quella matrice perché la perché poi lo vedremo perché quello là il LD soprasegnato, dimostreremo che sta in R ma non in R. Ok? Questa è la cosa, lo vediamo o giovedì o venerdì perché ora introduco queste proprietà e poi risolveremo tutte queste questioni appese. Sì. Ok. Ok. Quindi questa è la prima proprietà. Se abbiamo un eh Aspetta, devo finire qua, dobbiamo fare la seconda. Se abbiamo un linguaggio decidibile, ricorsivo, allora il suo complemento è ricorsivo. Secondo teorema e poi chiudiamo. Eh, teorema. Se L appartiene ad RE e L soprasegnato appartiene ad RE, allora L appartiene ad R. Ok? Quindi, se un linguaggio è ricorsivamente numerabile e il suo componente ricorsivamente numerabile, allora L è un linguaggio ricorsivo. Ok? Come si fa? Abbiamo l'intuizione. Se L appartiene ad RE, allora esiste una macchina di Turing che lo accetta. Ok? Non siamo in grado di stabilire che la macchina lo decida, però se L appartiene a RE esiste una macchina di Turing che lo accetta, quindi è in grado di dire di sì in tempo finito. Per il no non abbiamo garanzie. Ok? Se L soprassegnato appartiene ad RE, allora esiste un'altra macchina di Touring ML soprasegnato che è in grado di rispondere. Sì. Ok? In tempo finito per L soprasegnato. Eh, L soprasegnato però è il complemento di L. Ok? Allora, noi facciamo così, le infiliamo in una macchinona, in una macchina più grossa che prende l'input e lo copia in input a questa e input a questa qua. Ok? Questa è l'intuizione. Poi vi spiego come si fa. e quindi le randamo allo stesso momento. Ok? Facciamo partire ML ed ML soprasegnato e fanno un passo assieme come si fa comp facendo il prodotto cartesiano delle funzioni di transizione così si definisce. Ok? Quindi io riesco a simulare un passo di ml, un passo di ml sovrasegnato. Se la stringa W input appartiene a L, allora a un certo punto ML soprassegnato ML si fermerà e dirà di sì e quindi dico di sì pure io. Se W non appartiene ad L, allora appartiene a ML soprasegnato. Di conseguenza, questa macchina durante la sua simulazione a un certo punto dirà di sì. Ci dirà sì, è vero, W appartiene ad L soprasegnato. Io prendo quella risposta e la flippo e dico di no. È chiaro? Quindi, se un linguaggio è ricorsivamente numerabile e il suo complemento è ricorsivamente numerabile, allora in realtà sono entrambi ricorsivi, ok? Perché io posso combinare le macchine e mi piglio la prima risposta che arriva. Chiaro? Queste proprietà le utilizzeremo nelle prossime lezioni per stabilire dove si colloca Lu, dove si colloca LD, il complemento di LD, eccetera. Ok? Vediamo se devo fare altro, ma non credo. Ma quindi allora nel teorema quella L che appartiene a a R sarebbe una insieme tra L e N? No, L è un linguaggio. Sì, sì, sì. Ok. Però noi abbiamo che se L facciamo un esempio. Ah, no, ok. Qua c'ho R e qua c'ho R. Se io so che L sta qua ed L soprassegnato sta qua, allora in realtà stanno qua dentro. Questo è il senso del teorema. Cioè, se io ho che un linguaggio e il suo complemento sono ricorsivamente numerabili, allora in realtà sono ricorsivi. Chiaro? Ok, possiamo chiudere con questo. Alrgri, ma ce l'amo fatta.