Ok, ok, ok. Iniziamo un nuovo argomento. Oggi e vedremo altre classi di complessità. Vedremo altre classi di complessità fino a chiusura e poi facciamo Mokizzam. Ok. quello di cui ci siamo occupati finora nella definizione nella definizione di di classi complessità, quello che abbiamo visto finora sono classi di complessità temporale, cioè quanto tempo ci impiega un algoritmo ad eseguire. Ok? Quello che non ci siamo chiesti finora, quanta memoria gli serve all'algoritmo, ok? Perché sicuramente nel corso di algoritmi e strutture dati avrete visto la complessità spaziale, la complessità temporale, eccetera. Oggi introduciamo, ci concentreremo oggi e domani su classi di complessità spaziale e ok, non non scenderemo troppo nei dettagli e le collocheremo insieme insieme al resto per vedere un po' come come si si collocano. Ok? Secondo voi una macchina che può usare, ok? Abbiamo due macchine, una macchina M e una macchina N. La macchina M può usare tempo polinomiale, mentre l'altra macchina, la macchina N, può usare spazio polinomiale. Intuitivamente ce n'è una più potente dell'altra. Sì. Spazio polinomiale. Perché? La prima eh per forza comunque come la prima per forza come bo uno spazio policidiale, quindi a tempo polinomiale, spazio polinormi, la seconda ha solo spazio polinormale, quindi può funzionare anche esattamente. Ma queste cose le avate già viste? No. Ok. Alr. Sì, l'ingip è quello. Siccome noi abbiamo spazio bounded e lo possiamo riutilizzare, allora in principio possiamo rannare più a lungo. Ok? Quindi sì, dato un certo vincolo, quel vincolo se è dato sul tempo o è dato sullo spazio fa fa la differenza. Ok? In realtà, come vedremo dopo, noi abbiamo che P time intuitivamente, adesso definiamo tutto più formalmente, tempo polinomiale, P sta dentro P space, che è la classe di spazio polinomiale. Però, come tante di quelle cose nell'area della complessità, noi non sappiamo se sono distinte, cioè noi sappiamo che P sta dentro P space, ma non sappiamo se P space è strettamente più grande di P. Ok? Qu è un altro di quei risultati come P versus NP. Sappiamo niente. Ok? Abbiamo questa catena di contenimenti, come abbiamo già visto l'altra volta, e ne vedremo altre fra oggi e domani, tale per cui sappiamo che, per esempio, fra Ptime ed exp time sono due classi distinte, ma tutto in mezzo c'è una marea di cose che da qualche parte devono essere distinte se tra Ptime ed exp time c'è un salto, però noi non sappiamo dove e in particolare crediamo che sia in tutti i passaggi, che c'è un contenimento stretto, ma nessun nessuno è mai riuscito a dimostrare una cosa del genere. Forse abbiamo tecniche di dimostrazione sbagliata, forse stiamo guardando in direzione sbagliata, non si sa. Sono problemi aperti da 60-70 anni e nessuno è mai riuscito ancora a chiudere le questioni. Ok? Definiamo quindi in maniera ehm più formale le classi di complessità spaziali, ok? e lo faremo assieme. Spero che vi ricordiate un po' di definizioni di classi di complessità temporali perché ragioneremo assieme. Vi faccio vedere come si fa a indurre, eh, perché alla fine i ricercatori quando si inventavano ste cose inducevano roba, generalizzavano cose, quindi le vediamo assieme per ragionare assieme e tireremo fuori assieme le definizioni in maniera tale che sia anche più semplice ricordarcele perché ci siamo arrivati. Ok? Allora, per prima cosa dobbiamo introdurre un modello differente di macchina di tuning, altrimenti viene un po' complicato definire la complessità spaziale. Si può fare, però sulle classi di complessità spaziali molto piccole che quelle che vedremo oggi, domani ne vedremo di più generiche, diviene difficile fare questo genere di definizioni. Di conseguenza ci serve un modello di macchine di Touring leggermente diverso, ok? Che poi generalmente se guardate la letteratura moderna le macchine di Touring si assumono in questo modo perché poi per vari teoremi sono equivalenti alle altre. Ok? Allora, noi abbiamo una macchina di Touring, si parla sempre di decisori, quindi macchine di Touring che rispondono sì, no? Poi più avanti nella lezione ci servirà un trasduttore, però è una facile generalizzazione. Allora, considereremo ora una macchina di touring fatta in questo modo. Ha un nastro di input che è un nastro di input sulla quale la testina può andare avanti e indietro, però è un nastro di sola lettura. La macchina sul nostro input non può fare niente, può solo leggere, può spostare la testina dove vuole, può spedirla a Milano se vuole, però non ci può scrivere. La testina può andare avanti e indietro, però il nastro di input è un nastro di sola lettura. Dopodiché c'è un nastro aggiuntivo che è il nastro di lavoro. Nella letteratura in inglese lo trovate scritto workape. Il nastro di lavoro è un nastro infinito, la testina del quale può andare avanti e indietro come siamo abituati ed è un nastro di lettura scrittura. Ok? Quindi gli scarabocchi, la brutta copia, la macchina di Touring la fa sul nastro di lavoro, sul workpape. Ok? Questa è la differenza rispetto alle macchine che abbiamo visto finora. L'input non si può scrivere, abbiamo un nastro, però lo possiamo leggere, un nastro aggiuntivo che è il nastro dei calcoli intermedi. Si può leggere e scrivere e la testina va avanti e indietro, quindi è standard. Ok? Machine, come si chiama? Macchine di Touring. Sì. No, no, non abbiamo un nome particolare. Le assumiamo fatte così per vari risultati che non andiamo a guardare. Siccome è una macchina multinastro, questa alla fine si si riduce a una macchina mononastro. Perché facciamo questa suddivisione? Perché il vincolo dello spazio usato lo metteremo solamente sul nastro di lavoro. Ok? Ecco perché ci inventiamo questa questa definizione in maniera tale che il bound dello spazio sia solo sul workape. Ok? Perché? Perché andremo a vedere oggi tra un po' una classe in cui una classe di complessità in cui imporremo un ridottissimo spazio. Gli algoritmi per esempio che funzionano in spazio logaritmico. Però se noi andassimo a contare lo spazio anche sul nastro di input e l'input ce lo dobbiamo leggere tutto, noi occuperemmo sempre spazio lineare perché lo dobbiamo leggere tutto. Allora, per poter categorizzare questa classe piccolissima che vedremo che è la classe dei problemi in spazio logaritmico, allora ci serve il nostro aggiuntivo in maniera tale che il bound logaritmico viene dato sul foglio di brutta. Ok? Puoi scrivere il tema, ma sul foglio di brutta devi usare poco spazio. Ok? Questa questa è la cosa. Allora, definite queste macchine in questo modo, introduciamo i concetti di computation space, running space e via dicendo che abbiamo fatto l'altra volta, ormai un po' di settimane fa, per il tempo. Ok. Definizione computation comp. Poi vedete un attimo così vedo anch'io che vedete qua si è spento. Vabboh. computation space della macchina M su input input W il numero di celle distinte. viste da M sul work tape, mentre processa W. Ok? Quindi il computation space che è una cosa diversa dal running space come il computation time è diverso dal running time, quindi computation space, come mi ricordo questi nomi? Computation space. Siccome c'è computation fa riferimento a una computazione specifica, running space fa riferimento alla funzione di bound. Ok? Quindi cos'è il lo spazio di di computazione della macchina M sulla stringa W? È definito in maniera molto semplice. Lanciamo la macchina M su W. Questa macchina sposterà la testina sul nastro di eh di lavoro. Il numero di celle viste, nemmeno scritte, basta che la testina ci arriva là. eh il numero di celle viste su workta dalla macchina durante il processamento di W e il suo computation space dm su W. Ok? Molto semplice la definizione. Questa definizione però assume che la macchina M sia deterministica. Lo vedete da come è stata scritta la definizione. Ok? ci dobbiamo inventare una cosa per le macchine non deterministiche. Secondo voi così buttiamola là. Come potremmo definire il computation space di una macchina M non deterministica su una particolare stringa W? Dobbiamo generalizzare questa idea. Prego. Eh, il massimo computation space. [Musica] Esattamente. Se M è non deterministica, definiamo il computation space come il massimo numero di celle visitate dalla macchina su workta in qualsiasi dei suoi branch di computazione. Ok? Perché vi ricordo che una macchina non deterministica ha vari branch di computazione, il suo computation space è il peggio che fa, cioè quant'è lo spazio massimo che la macchina usa in qualsiasi dei suoi rami di computazione. Quello è in caso di macchina M non deterministica, quindi lo mettiamo fra parentesi, se non deterministica si prende il massimo. Ok. Alright. Altra definizione. Una funzione S che mappa interi su interi è una space function. Se S è strettamente positiva è non decreasing. Ok? Quindi abbiamo definito computation space per U della computazione di una macchina su una particolare string. Poi abbiamo dato questo nome alle funzioni intero verso interi. Noi diciamo che una funzione S che mappa interi su interi è una space function. Se S è strettamente positiva è ed è non decreasing è semplicemente un nome. Vi faccio notare che la stessa definizione era stata data per time function, ok? Cioè, stiamo dando due nomi alla stessa cosa, eh, giusto per trovarci un po' con le cose. Ok, definizione di running space, quindi questa è una space function. Running space running space della macchina M. Ok, questa è una un adattamento della definizione del running time di una macchina di Turing. Come lo possiamo definire? Sì, prego. Se è una funzione, è una function, se per tutte le stringhe W parte qualcuno il loro computation space non eccede. Esattamente. E quella è proprio la generalizzazione di running space. Allora, sia sn una space function, il eh il running space di M è SDN Se per ogni stringa w a parte un numero finito di s che il computation space di M su w è bounded da S applicato sulla lunghezza di W. Ok? Quindi è semplicemente un'estensione del concetto di computation time, time function, running time e speculare. Semplicemente ci spostiamo sulla definizione di spazio, ok? Quindi avremo eh computation space, space functions, running space, ok? Esattamente come avevamo fatto per il tempo che avevamo definito le classi temporali, ok? Possiamo definire le classi spaziali, le classi di complessità spaziali. Sì, prego. Una domanda. Decidendoation space abbiamo come numero di celle dist. Ma viste mi sembiamo le scritte o anche? No, no, basta che ci ha spostato la testina, perché la macchina potrebbe fare questo giochino. Sposto e torno indietro per contare. Sposto, magari metto un marcatore su una cella, sposto la testina per contare, ok? Non scrivo niente, quindi non uso spazio, poi torno indietro fino a trovare il marcatore. Eh, ok, in quel modo ho imbrogliato, quindi basta che c ci abbiamo spostato la testina. Ok. Alright. Quindi, esattamente come avevamo definito d time ed n time, possiamo definire D space ed n space. Ok, c'abbiamo spazio. C'abbiamo spazio. Ok. Secondo voi Dpace di SN sia SN una space function, che cosa sarà mai di space? L'insieme di cosa? Ve lo ricordate cos'era? D time, te lo dico io e poi generalizziamo. D time per una certa funzione tdn è l'insieme dei linguaggi riconosciuti da macchine di touring deterministiche in tempo big o di TDN. Ok. Cosa potrà mai essere di Space? insieme dei linguaggi riconosciuti da una macchina diingerministica con Running Space. Esattamente l'insieme dei linguaggi tale per cui esiste una macchina di Touring deterministica, tale che il linguaggio di M e L è il running space m e big o ds S di N. Ok. Definizione abbastanza semplice. S. Cos'è? N. N. Sì, è il mio modo strambo di scrivere N. C'ho una M e una N stranissimo. Vi ho mai fatto vedere come scrivo minimum. Eh, grandioso, eh. Ok, ok. Alri, next one. Ah, n space. N space di s, ok? Dove s n space function. Cosa mai conterrà n space? uguale macchine non determinisch attentamente l'insieme dei linguaggi tale per cui esiste una macchina n non deterministica tale che il linguaggio di N e L è il running Space D n e big o di s n. Ok, quindi definita la complessità spaziale delle macchine, abbiamo definito la complessità le classi di complessità spaziali. Classi di complessità spaziale, esattamente come le classi di complessità temporale, sono insieme di linguaggi. Insieme di linguaggi caratterizzati dal fatto che possono essere decisi da macchine di touring all'interno di un certo un certo quantitativo di risorse computazionali. Nel caso delle classi temporali, la risorsa computazionale è il tempo. Nel caso della classi spaziali, la risorsa computazionale è la memoria. Ok? Ci concentreremo in questa lezione su due classi, due classi spaziali, domani ne vedremo un altro po', più un risultato interessante che le rende particolarmente differenti dalle classi temporali. Allora, la prima classe che definiamo è L, a volte denotata come log space e poi definiremo NL che è deterministic log space. Ok. Allora, secondo voi L come la possiamo definire? Usando D space ed n space. Quindi, innanzitutto, L è deterministica o non deterministica? Deterministica. Deterministica. Quindi facciamo di space. di space di quale funzione? Logaritmo in base 2 di n logaritmo in base 2 di n. Ok? E invece nl n space di logaritmo in base 2 n space di logaritmo in base 2 di n. Ok? Utilizziamo il logaritmo in base 2, ma questo non non è una limitazione perché se dovessimo usare un logaritmo in un'altra base, fare il cambio di base di logaritmo è una costante, quindi non ci cambia niente. A livello asintotico è la stessa cosa. Quindi da adesso in poi quando scrivo logaritmo io intendo logaritmo in base 2. Ok? Secondo voi, qual è la relazione di contenimento fra queste due classi? L contenuto in NL. L è contenuto in ln NL. Sì, L è contenuto in NL. Ok. E poi relazioni con le altre classi le vediamo domani. Oggi ci focalizziamo su questo problema, su queste due classi. Ok? Allora, quello che noi ci poniamo come obiettivo ora è andare a guardare un po' di problemi che stanno in queste classi, ok? cercare di capire se siano distinti o meno, cosa la gente si è inventata per stabilire se fossero distinti o meno. Ve lo anticipo, tanto per cambiare. Noi sappiamo che L sta dentro NL, ma non sappiamo se le due classi sono distinte, ok? È la solita storia di tante classi di complessità. Ok? Secondo voi? Ok? No, ve lo dico io. All'inizio del corso, mentre guardavamo macchine di Touring eccetera, abbiamo a un certo punto giocato con questo linguaggio. Ok, qua uso qua indico linguaggio. Ok. Alrght. Allora, cercherò di questo linguaggio L questo qua abbiamo questo linguaggio qui. Eh, possiamo scrivere con L. Eh, eh sì, alcune persone usano questo, guarda, log space. Non non voglio scu, scrivo log, ok? Log, però generalmente sui testi troverete l oppure log space. Ok? Allora, abbiamo questo linguaggio 0n 1 n con n strettamente maggiore di 0. Ok? Avevamo visto questo linguaggio. Vi ricordate come l'avevamo come l'avevamo riconosciuto? Noi vogliamo ci chiediamo se questo linguaggio sta in lovead space o meno. Ok? Questo è il nostro quesito di ora. Vi ricordate la macchina che avevamo usato? Come riconosceva questo linguaggio? [Musica] Com'è? No, era molto più semplice di quella. Era una macchina deterministica. Facevamo un giochino. La prima la prima versione semplicemente leggeva il primo carattere lo cancellava, poi a destra. Sì, sì, esattamente. Facevamo questo gioco, prendevamo con la testina, andavamo sullo zero, lo marcavamo o lo cancellavamo, andavamo in fondo, eccetera, tornavamo indietro. Pam pam pam. Ok. Assumiamo di fare questo genere di lavoro sulla macchina che abbiamo definito oggi, quella con work tape. Quello che dovremmo fare sostanzialmente sarebbe prendere l'input, ricopiarlo su Wordtech e iniziare a fare il gioco. Ok? Perché noi questo gioco sul nassero di input non lo possiamo fare. Ok? Quanto spazio occupiamo a fare questo lavoretto? lineare. Ok? Quindi questa macchina usa tanto spazio, non ci mostra che questo linguaggio è in log space. Ok? Secondo voi che ci possiamo inventare per riconoscere decidere questo linguaggio in spazio logaritmico? Un attimo. Chiaro l'obiettivo? Noi su nastro di lavoro vogliamo usare solo spazio logaritmico invece che spazio lineare. Come si fa? Pensateci un attimino. Ok. Qualche voce nuova, senò c'ho già la coda in testa delle persone. Prenotate. Ok, prego. Ci scriviamo in qualche modo la quantità che a livello dizione èamente Esattamente. Noi su un nastro di lavoro contiamo, cioè noi ci scriviamo in binario quant'è questo n e questo n quanto spazio occupa? Logaritmico. Ok? Quindi la macchina funziona così. Partiamo dagli zeri e iniziamo a scrivere sul nastro questo conteggio + 1 + 1 + 1 + 1 fin quando arriviamo all'uno. Sul nastro avremo una rappresentazione binaria di n la cui taglia è logaritmica nella quantità di zeri che apparivano sul nastro in input. Sì, prego. Per aumentare il conto non bisogna andare a destra a sinistra con la testina su work tape puoi fare quello che vuoi, invece, ma puoi andare avanti e indietro pure pure su input, l'importante è che non ci scrivi. Ok? Quindi andiamo avanti e li contiamo e sommiamo uno e sommiamo uno e sommiamo uno. Prende un po' di tempo perché dobbiamo fare queste somme binarie. Ok? Comunque mi pare che la complessità ammortizzata di contare è lineare. Non perdiamo così tanto tempo. Scriviamo questa cosa su worktape. Lasciamo uno spazietto. Rifacciamo la stessa cosa con gli uni, risommiamo tutto. Quando abbiamo finito di fare i due conteggi, controlliamo le due stringhe. Se sono uguali rispondiamo di sì. Se sono diverse rispondiamo di no. Quanto spazio abbiamo occupato? logaritmico. Ok? Quindi questo problema appartiene a log space. Appartiene a log space. Quello che vogliamo vedere adesso invece è un interessante problema. Questo C spazio. Ce ne abbiamo a non finire. Un interessante problema che appartiene al nondeterministicace. Ok? È un problema che so per certo che conosciate, non sapete che è un problema di NL. Quello è un problema famosissimo perché l'avrete visto miliardi di volte in 10.000 Zs e però il nostro obiettivo sarà mh eh no, sarà definire un algoritmo non deterministic long space perché di sicuro lo conoscete, però avrete visto ad altissima probabilità algoritmi polinomiali deterministici. Invece quello che noi vorremo vedere oggi è un non deterministic lo space, cioè che nel momento in cui abbiamo una macchina di non deterministica possiamo risparmiare un sacco di spazio. Ok? Questo problema è l'astrusissimo problema della raggiungibilità sul grafo. Ok? Quindi com'è questo linguaggio è il linguaggio delle triple GST in cui G è un grafo diretto. Importante che sia diretto, eh? Sono nodi di V. ed esiste in Gh s a t. Ok, semplice il problema, lo conoscete, abbiamo un grafo diretto. Due nodi ci vengono dati in input. Non è un problema di calcolo, è un problema di decisione. Ci viene semplicemente chiesto ma è vero o no che in G esiste un percorso da S a T? Ok? Noi dobbiamo rispondere sì o no. Come lo risolvete in genere questo problema? BFS o BFS. Esattamente. Cioè, facciamo un'esplorazione del grafo. Cosa possiamo fare? Prendiamo S, che è il nodo di partenza, è la sorgente. T è il target. Analizziamo prima questo algoritmo. Prendiamo S, vediamo chi sono i successori di S. Tutti loro li prendiamo e li infiliamo in un sacco. Ok, prendiamo, no, facciamo così, vediamo i successori di S, se tra loro c'è T, diciamo di sì, sennò li prendiamo e li mettiamo in un sacco. Tiriamo fuori dal sacco un uno di questi vertici, vediamo chi sono i suoi successori. Supponiamo che il grafo non sia ciclico, sennò insomma vanno fatti tutta una serie di controlli. Vediamo se fra quelli c'è T. Se C è T, diciamo di sì, senò prendiamo, li ributtiamo nel sacco e continuiamo così. Ok, procedura deterministica. Quanto tempo prende? No, non mi interessa l'esponente preciso. È polinomiale? Non è polinomiale? È polinomiale. polinomiale. Sarà o quadratico o pubblico, una cosa del genere. Ok. deterministico polinomiale. Quanto spazio usiamo? Polinomiale. Usiamo spazio polinomiale. Usiamo linear space più o meno perché dobbiamo mettere nodi in questo sacco e a un certo punto male che ci va potrebbero comparire tutti. Ok? Quindi lo spazio che usiamo in questo sacco è bello grosso. Questo algoritmo non ci mostra l'appartenenza di reachability a non deterministic space perché usiamo troppo spazio. ci dobbiamo inventare un algoritmo non deterministico che usi solamente, e questo è la parte trucchettosa, che usi solamente spazio logaritmico. Prendetevi un paio di minuti per pensare e poi iniziamo a ragionarci assieme. Diretto vuol dire orientato. Orientato. Sì, sorry ho tradotto da directo. Sì, orientato. Dove l'ho messo? Qua. Cancelliamo. Orientato. Orientato. Sì, alcune parole in italiano non mi ricordo più come si dicono. Che si può fare? Per risparmiare spazio vi do questo indizio, eh, per risparmiare spazio dovete sfruttare il non determinismo, senò non se ne esce. Un attimo. Secondo. Siamo a due. 3. Ok. Siccome preferisco una lezione un po' più collegiale, chiamerò prima quelli che non ho sentito prima. Eh, ok. Era carino che quando ero a Oxford facevo lezione a tre studenti ed era quasi una lezione one to one, quindi si mette il foglio in mezzo, però è da lì che mi viene lo stile interattivo, perché con loro ci siedevamo e facevamo cose Alri, prego. Eh, io mi stavo immaginando di per un guest se esiste una strada al massimo lunga K, dove K è un numero di eh nodi. Mh. Eh, altrimenti Ma quindi lei deve gessare K nodi, lui deve gestare e scrivere sul nastro. Sì, è quello che spazio lineare. Troppo spazio. Buon tentativo. Guess. Guess di cose. Prego. Io farei un gest, però, cioè, poi faccio il primo nodo, faccio un guess, appunto, del primo passaggio ecco e a questo punto sovrassivo dal nuovo nodo. In questo modo, cioè parole tipo ogni singolo passaggio Sì. Ok. Intanto possiamo fare un chest dell'intero percorso. Posso fare un chest? Il passaggio. Sì. E ok. Buono l'impianto. Ok. L'idea che ci dava il nostro collega Gesso, il F è corretta. Il problema è che dobbiamo risparmiare spazio. Come risparmiamo in quel modo. Che cosa facciamo noi? Gessiamo. È strano, eh, perché adesso dobbiamo aggiungere un po' di dettagli, poi scriverò uno pseudocodice, così capiamo come funziona. La questione è che noi non possiamo gessare tutto il patto assieme perché occupa troppo spazio, quindi ne dobbiamo gessare dei pezzi. Che pezzo gessiamo? Noi gessiamo di volta in volta solo il passo successivo, quindi sono in s gesso il nodo di dopo. Ok? Come faccio a stabilire che non sto gessando spazzatura? Verifico che il gess sia relativo a un arco presente nel grafo. Ok? Quindi io gesso uno dei successivi di S. Non è che gess una cosa a caso. Gess uno dei successivi di ss. Una volta che ho questo qua e sto usando al momento poco spazio perché uso l'identificativo del nodo. Oppure come potrete leggere sugli appunti di Calutti posso gessare il pointer alla posizione nel nastro di input di dove compare il nome del nodo. Ok? Cioè è una questione per rappresentare compattamenti. Quindi se abbiamo, che ne so, 16 nodi, per dire, numerati da 0 a 15, a me servono 4 bit per dare nome a quei nodi. Ok? Quindi cosa faccio? sono in s gesso è il suo successivo. Sto solo ghessando il nome identificativo di questo nodo, quindi mi serve spazio logaritmico per rappresentarlo. Dopodiché quando devo gessare il passo successivo, ok? Io prendo, a questo punto sarà in un S prim', prendo un successivo di S'ado in S2. Ok? È chiaro? E dove lo vado a scrivere? Sullo stesso spazio, cioè io lo riuso. Io in memoria mantengo costantemente nodo corrente, nodo successivo, giusto per fare il controllo che ci sia un arco. Dopodiché cambio e vado a riscrivere di volta in volta. Ok? Dobbiamo sfruttare il fatto che riusiamo lo spazio. Questa è la potenza delle classi spaziali. Un secondo. Allora, che cosa succede? Se esisterà, no, un modo di arrivare a destinazione, la macchina la macchina lo trova. Ok? Però noi dobbiamo garantirci che cosa? che questa macchina non lucchi, perché io sto solo chiestando, sto solo chiestando il successivo, eh, io non ho una visione di insieme di dove mi sto spostando nel grafo, sto luppando. In genere negli algoritmi che noi abbiamo visto al corso di algoritmi e strutture dati, come fate ad evitare i loop? Vi tenete una struttura dati che dice io questo nodo l'ho già visto, ma se noi abbiamo una lista del genere nella macchina quanto spazio siiamo? lineare lineare e quindi è una cosa che non si può fare, cioè quindi noi dobbiamo evitare il looping senza tenere traccia di che cosa abbiamo visto. Ok, chiaro? Quindi, che cosa abbiamo? Questa macchina gessa di volta in volta il next step e in questo modo ci evitiamo di di rappresentare tutto il patto in memoria e risparmiamo spazio. Abbiamo un problema però che se la se il grafo è ciclico sta macchina inizia a ciclare non si finisce più. Ok? Quindi dobbiamo essere sicuri che noi andiamo a bloccare i rami di computazione infiniti. Ok? Non possiamo mantenere la lista delle cose liste come si fa generalmente un attimo. Perché non possiamo eh non possiamo occupare tutto quello spazio per memorizzarlo. Ci serve un'altra cosa. Allora, se mi ricordo la sequenza c'era prima lei nulla, poi lei combinatoria. Ho visto un problema simile col taglio del grafo. Mh. simile come problema, eh che intende per taglio del grafo. Prendevo la spesa di limite in area suppeva al grafo. Ci sono forse si potrebbe fare in quel modo, ma c'è un modo ancora più semplice. Prego. E in questo caso mi sa che io mi concentro solo sullo spazio, posso contare, cioè io riciclo quest'anno avanti all'infinito e mi accorgo che non è corretto se ho ehm visitato tutti i nodi del grafo, quindi se il mio conteggio superato. Esattamente. Mettiamo un bound sui nodi visti. Ok? Quindi in memoria noi che cosa teniamo? Nodo corrente, nodo successivo più un contatore. Quanti ne ho visti finora? Se mentre gesso i passi successivi il contatore supera il numero dei nodi del grafo, allora sto luppando e io quel branch di computazione l'ammazzo tutto. Quindi per accettare che devo fare? Il branch accettante, la macchina che fa? Gesso i passi successivi, conto quanti passi faccio e accetto se arrivo a Tliola. Ok? Questo è il trucco. Prego. Due domande. Allora, la prima è ma siccome noi vogliamo mantenerci uno spazio logaritmico, sì, tendenzialmente eh gli algoritmi di ricerca del del B ottimale sono molto più piccoli di una BFS, una BFS. Non possiamo assumere che il guessing eh della macchina in Cuding segua appunto gli algoritmi primari di ricerca del PUAP per avere Ma quegli algoritmi tipo la ricerca la ricerca in breath? Allora, se io mi vado a guardare, facciamo un esempio. Allora, io qui c'ho S e devo arrivare a T che sta qua. Ok? Supponiamo che S c'ha una marea di successivi e questo è attaccato a lui. Quando io metto S e faccio bread first, prendo i suoi successivi e li infilo nel sacco. li ho presi sostanzialmente tutti, quindi ho usato spazio lineare e è andata, però vabbè, in questo esempio è eh ma io devo avere la garanzia che la macchina non usi mai più di più di spazio cioè quindi su qualsiasi sia l' istanza di input, qualsiasi forma abbia il grafo, io devo essere certo di non usare più di spazio logaritmico. Se mi arriva una cosa strampa come quella, al primo salto la macchina si mette nel salto tutti i successivi DS che sono quasi tutti noi. Da qui arriva anche la seconda domanda, ovvero se noi conto che letteralmente è un grafico unilaterale dovè semplicemente una consecuzione di nodi, no? Sì. Ok? Quindi noi lo esploriamo tutto. Quindi noi percorrorendo tutto il grado, cioè si può dire ancora che è logaritmica la questione. La logaritmicità dell'algoritmo dipende dalla quantità di informazione che va scritta sul nastro, sulla quantità di gelle che viene usato sul nastro di lavoro. Adesso descrivo lo pseudocodice di questo algoritmo e vediamo che cosa ci cosa intende per scrivere il nodo, cioè scrivere il nominativo. Sì, quindi, cioè quando lei in un algoritmo, non lo so, programmato Python Java, C++ Sì. Ok. Nel momento in cui abbiamo eh current vertex equals S, ok? Quindi assegniamo S a current vertex, che cosa sta succedendo nella macchina? che c'è un blocco di memoria il cui contenuto binario prende l'identificativo del modo S. Questa è la stessa cosa, solo che invece di scrivere un programma lo scriviamo su. Scusi, una domanda stupida, ma allora seo ha un nome lunghissimo, cioè si eh, però il nome lunghissimo stava in integ perché è tutto rispetto alla data e poi li numeriamo anche Sì, sì. Cioè, se io ho i nomi che si chiamano Pipp, Giovanni, Pluto e Topolino, no? E io mi devo gessare di identificativi. Sto gessando della delle cose che stavano nell'input oppure io li posso semplicemente numerare. Ok. Ok. Grazie. Tutto qua. Allora, facciamo questo e poi ci fermiamo. Alri. Quindi abbiamo rich che prendiamo in input G, S. Funziona così. P è il nodo corrente e prende S. Poi prendiamo il contatore N e gli assegniamo uno che abbiamo visto un nodo. Dopodiché if P è uguale uguale a T, cioè siamo arrivati, allora accettiamo. Dopodiché Pacciamo un guess. di un vertice di G. Ok? Poi facciamo un controllo. If P' appartiene agli archi del grafo, eh no un attimo. Se non appartiene al grafo agli archi, allora rigettiamo. [Musica] Ok. Poi semplicemente mettiamo P' in P, incrementiamo il contatore. Se n è minore o è minore o uguale al numero è minore o uguale sì al alla al numero dei nodi, then go a e diamo la label a là. Altrimenti peggate. Vedete tutto? Sì. Ok. Allora, questo è un algoritmo molto semplice. Partiamo dal nodo S. Inizializziamo il contatore. Se P che il nodo corrente è uguale alla destinazione, accettiamo perché ci siamo arrivati. Altrimenti che siamo in primo un qualsiasi vertice di C. Come vedete, noi non ci curiamo di vedere se qualcosa che è stato già visitato o meno. Se la cosa che abbiamo cessato non fa parte degli altri, rifiutiamo. Dopodiché assegniamo P' a P e possiamo continuare successivamente. Incrementiamo il contatore. Se siamo arrivati, se non stiamo eccedendo la taglia dei nodi, ritorniamo qua e continuiamo la procedura. altrimenti ci fermiamo. Ok? Allora, questo algoritmo è corretto in quanto in quanto nel momento in cui c'è un pat da S a T dentro G, ci sarà una sequenza di gess lunghezza al + n che mi permetterà di arrivare lì e arrivare alla parte in cui ci spostiamo nello stato accettante. Se questo pat G non ci sta, non ci sarà nessuna sequenza di gas di alq n steps che ci permetterà di arrivare t, quindi alla fine rigetteremo su tutti i brgch. Ok? È chiaro come funziona questo algoritmo? Ok. Cosa ci serve da memorizzare? Cosa abbiamo su workta tape? Abbiamo eh abbiamo P che è il nodo corrente, P' che è il nodo successivo e lì noi memorizziamo due identificativi. Se se li contiamo i nodi gli diamo l'identificativo e quello là e prende spazio lì spazio logaritmico. Un altro modo, come troverete sugli appunti di Calauti, è che lì in Patore a dove c'è la descrizione del nodo sul nastro, c'è la 14 input. Quello lo si rappresenta sicuramente in spazio logaritmico. Io uso i nomi perché è più intuitivo. Senò in linea di principio uno lì ci mette il puntatore alla cella sul nastro di input e quella ha sempre taglia logaritmica. Quindi che cosa ho? Devo memorizzare il nodo corrente, il nodo successivo, il contatore, tutti questi. Quindi abbiamo tre elementi, ognuno dei quali prende spazio logaritmico. Quindi questa che cos'è? è una procedura log space da parte di una macchina non deterministica che decide la raggiungibilità in un grafo orientato da cui richability è un problema in nondeterministic space. Chiaro? Ok. E possiamo fare un po' di pausa, una decina di minuti, 10 minuti più o meno. Eh, dov'è? Dov'è? Quand'è? Dove sta? Un tempo ci stava il mouse. Dov'è? Eccolo qua. È a destra. A destra. Spunta, spunta. [Musica] Alri. Right. Right. Ok. Allora, prima quando ragionavamo sul problema sul problema sul nostro fantastico problema di receability vi ho detto se volete risparmiare spazio logaritmico dovete usare dovete usare il nond determinismo perché se non volete usare il non determinismo non ne usciamo. Perché vi dicevo questo? Perché ho una conoscenza che a voi ancora non avevo comunicato che è questa. Non la gente si è chiesta, ma il nondeterminismo per risolvere richility è determinante? Possiamo fare in log space deterministico, non si sa. È una di quelle fantastiche questioni della complessità nella quali algoritmi deterministici log space per richability non ce ne stanno, però nessuno ha mai dimostrato che non sia possibile. Ok. dopo tanti anni riteniamo che non sia fattibile, ma non è che ne siamo certi, lo assumiamo perché, insomma, ci abbiamo sbattuto la testa così a lungo che ormai riteniamo che non sia non sia fattibile. La questione è che nessuno ha mai mostrato formalmente che questa cosa non si possa fare. Allora, il tutto proviene e come si è studiata la gente questa cosa qua? Ha fatto dei ragionamenti simili alla questione P versus NP. che nel momento in cui noi sappiamo che log space sta dentro non deterministic lo log space non sappiamo se queste due classi c'è una relazione di contenimento stretto o meno e le persone se ne sono inventate delle nozioni simili a quelle che furono utilizzate per studiare la questione P versus NP che si sono inventati si sono inventati il concetto dei problemi più difficili della classe NL. Vi ricorda qualcosa? Ok? Come c'abbiamo i problemi NP completi, abbiamo i problemi NL completi che sono i problemi più difficili della classe NL e riceability è uno di quelli, ok? È un problema tosto della classe non deterministic lo space e per queste ragioni riteniamo riteniamo che non possa essere risolvibile il deterministic space. Ok? Allora, quello che noi dobbiamo fare adesso, defini definiamo cos'è un problema non deterministic log space completo è una generalizzazione di cose che abbiamo già visto per NP, quindi lo facciamo assieme così ci esercitiamo a ricordarci i concetti e a estenderli. Allora, ve la ricordate la definizione di NP completezza? un problema, un linguaggio di decisione è sì, un linguaggio di decisione, un linguaggio è NP completo. Se appartiene ad NP e qualsiasi problema appartenente ad NP è riducibile a quelli quali. È riduci in quale modo? Eh, polinomiale. Inopinomiale. Ok, questa era la definizione di NP completezza. Allora, non possiamo usare la stessissima definizione, ne dobbiamo usare una simile. In particolare il problema si annila nella riduzione. Ridurre polinomialmente problemi dentro NL è troppo potente, cioè noi potremmo ridurre problemi difficili a problemi facili se avessimo possibilità di fare riduzioni in tempo polinomiale perché ok vabbò lo vedremo domani, ma vi do l'anteprima. P sta qua, eh, P sta qua sopra, eh, lo vediamo domani sta cosa. Prendetemi sulla parola. P sta lì sopra. Allora, fare riduzioni polinomiali per problemi che stanno qua dentro e sono riduzioni un po' troppo forti. Allora, le persone si sono inventate tutta una pletora di riduzioni, le le cui più semplici sono le riduzioni log space, ok? Dice riduzione polinomiale, riduzione spazio logaritmico. Intuitivamente, secondo voi, una riduzione log space che cosa potrà mai essere? Un attimo, qualcuno di nuovo? Voci nuove. Facciamo il coro e poi andiamo a cantare all'Antoniano, allo Zecchino d'oro. Andiamo, guys. Cosa potrà mai essere una riduzione? Lo space. Io nel frattempo ho registrato i prenotati. Vorrei sentire qualche voce nuova. Nuova. Sì, prego. La funzione calcolabile in spazio logico. Ok. Ok. Quindi ci dobbiamo inventare che cosa significa che una funzione sia calcolabile in spazio logaritmico prima, ok? Perché ci serve una funzione di trasformazione calcolabile in spazio logaritmico. Vi ricordate come avevamo definito le funzioni calcolabili in tempo polinomiale? Erano definite tramite il concetto di di trasduttore. Cos'è un trasduttore? è una macchina di Touring Stramb nastro di output. Ok? Quindi le funzioni sono calcolate da macchine di touring che hanno un nastro particolare sola scrittura nel quale la macchina sputa fuori il proprio output. Ok? Questa era la nozione di trasduttore perché adesso arriva la ragione del perché il nastro di output sia di sola scrittura. Ok? E in particolare ha pure questa caratteristica. La testina va in un solo verso. Perché mai ce la inventiamo così? Perché nel momento in cui sto andando troppo veloce, ok? Nel momento in cui vogliamo imporre un vincolo spaziale a un trasduttore e il trasduttore sul nastro di output può fare quello che gli pare, potrebbe iniziare a usare il nastro di output come foglio di brutta e poi te lo consegnano all'esame e sì, è un po' mischiato, no? Sul sul nastro di output ci va la bella, ci va il risultato. Ok? Quindi un trasduttore così definito in cui il nastro di input lo possiamo solo leggere, il nastro di output può essere solo scritto, diventa sensato che cosa? Imporre il vincolo di spazio sul nastro di lavoro. Ok? Questa è la ragione per cui il nostro di output è sola scrittura, in maniera tale che diventi sensato imporre il vincolo sullo spazio del del del nastro di lavoro. Perché? Perché se dobbiamo produrre un output grosso e noi andiamo pure a contare lo spazio per scrivere, eh ma noi quell'output dovevamo generarlo, no? Quindi non potremmo mai avere un trasduttore long space se dobbiamo scrivere un un output lungo. Quindi noi vogliamo imporre il vincolo di spazio solamente sul nastro di lavoro e per questa ragione noi imponiamo che sul nastro di output possiamo solamente scrivere. Quindi la macchina non può processare, scrive e basta. Cioè, se non è di lettura, vuol dire che il contenuto del nastro di output non viene mai considerato per valutare il next step. Questa è la questione. Ok? Se io posso solo scrivere su quel nastro, cosa ci sta su quel nastro? Non entra mai nella valutazione della funzione di transizione. Quella è la ragione. Ok? Quindi retap. Cos'è un trasduttore long space? è un trasduttore che ha quindi nastro di input di sola lettura, nastro di lavoro work di lettura scrittura, nastro di output di sola scrittura. Il trasuttore lavora in spazio logaritmico se lo spazio utilizzato su un nastro di lavoro è bound da un logaritmo. Ok? e l' ottiene per ok? Un tale trasduttore calcola una certa funzione f se per un qualsiasi input eh che diamo a questo trasduttore su quella stringa W il trasduttore lascerà a fine calcolo sul nastro di output f. Ok? Tutto qua. Possiamo quindi ora definire, quindi è chiaro cos'è un trasduttore in soldoni è un algoritmo che produce un output e impiega spazio algaritmico per farlo sul nastro di lavoro. Ok? tutto qua, non è nulla di particolare. Possiamo quindi definire cos'è una riduzione log space, quindi siano A e B due linguaggi. Esiste una riduzione log space da A a b denotato con A ug l b. Vedete, uso un simbolo diverso. Quando è polinomial time la mettiamo P. Quando è log space mettiamo L. Completate voi la definizione. Quindi A si riduce in log space a B. Sì, per ogni stringa appartenente ad manca un pezzo. Lei sta dicendo per ogni stringa w vale w appartiene ad a come tale w appartiene ad asse solo se e f di w ed f chi è? F è una funzione calcolabile che non abbiamo ancora. Ok. Quindi esiste una funzione, Esiste, bravissimo, esiste una funzione fpa cosa? Stringa. String. String su string. F caratteristiche deve avere? Deve essere calcolabile da un trasduttore calcolabile in spazio logaritmico calcolabile in log space. Ed inoltre per ogni stringa w e se w appartene ad a w appartina ad a se solo se esattamente. Ok, quindi la nozione di riduzione è sostanzialmente mantenuta mantenuta. L'unica cosa è che la funzione di trasformazione che richiediamo è che venga calcolato in log space, sottolineo qua, log space deterministico, eh, cioè non può fare cose strambe la riduzione, deve è una cosa semplicissima. log space deterministico. In realtà non l'abbiamo mai sottolineato, ma moltissime delle riduzioni che abbiamo visto, tipo la riduzione da indipendenzetta a vertex cover, era una riduzione long space. Non ve l'ho detto perché ancora non avevamo il concetto. Quella sì traduce in polinomial time, ma quella era una riduzione che prendeva il grafo e lo sputava in output, quindi spazio di lavoro non ne usava proprio. Quella è una riduzione log space, la prende, la butta. Chiaro? L'unica cosa che faceva calcolava la differenza. n - k faceva era lunga trasformazione, si può dimostrare che eh le sottrazioni, le operazioni, le operazioni aritmetiche si fanno in taglia log space degli operandi, ok? Quindi quella era una trasformazione log space, in realtà ne abbiamo viste tante di trasformazioni log space, ok? Logace deterministiche, ok? E possiamo, una volta che abbiamo la definizione di riduzione log space, possiamo definire i linguaggi NL completi. Un linguaggio L e NL complete. Eh, secondo voi L appartiene a NL e per ogni L'O appartenente a NL si ha una riduzione da N da L' a L. Sì, esattamente. Ok, questo l Lness, sì, questa qui è membership. Membership in NL e questa è NL hardness. Esattamente. Quindi un problema è NL hard, se è almeno difficile quando tutti i problemi della classe NL ed è NL completo se sta anche in NL, quindi è uno dei problemi più complicati della classe NL. Un risultato che si può dimostrare simile a quello che abbiamo visto per P versus NP. Si può mostrare che nel momento in cui un linguaggio NL completo fosse in logpace e non deterministic log space sarebbero la stessa classe. Ok? Quello che dimostriamo ora è che spazio ne abbiamo, ma più o meno. Quello che dimostriamo ora è che tiriamo una bella linea. un problema che rich ability non solo è un problema in NL, come abbiamo visto prima, ma è anche un problema NL art, cioè è un problema tosto per la classe NL. Quindi quello che noi mostriamo ora è che richility è no appartiene, no? Eh, NL complete. Per le riduzioni per le riduzioni log space valgono tutte le proprietà che valevano per eh per le riduzioni polinomiali, quindi la transitività eccetera. E mi ricordo che noi quando facevamo le riduzioni di NPR no prendevamo un problema noto NP argo e lo riducvamo al problema di interesse. Abbiamo problemi noti NL hard? No. Quindi dobbiamo far vedere che qualsiasi linguaggio di NL si riduce a ricciability. Quindi dobbiamo fare una cosa che ha la valenza del teorema di Cook però su NL. Ok? In realtà è più semplice, non è così non è così complicato, però si può fa', dobbiamo un attimo ragionare. Ok, solite domande. Quindi, per mostrare che richability è NL completo, noi dobbiamo far vedere che preso un linguaggio L generico, questo si riduce in log space deterministico a reachability. Ok? Qual è il problema di partenza? qualsiasi L generico, ok? Si riduce log space a reachability. Cos'è un'istanza per L? Stringa. Una stringa. Cos'è un'istanza per richability? Grafo sorgente. È una tripla. Grafo sorgente target. Ok, quindi ci dobbiamo inventare una funzione che mappi una stringa W verso una tripla grafo sorgente targets. Tempo ne abbiamo. Sì, eh. E a questo punto possiamo fare delle considerazioni. Sono considerazioni che avevamo già fatto per il teorema di Cook, quindi vado un attimo più veloce per poi focalizzarci su sulla riduzione in sé, perché questa riduzione produce grafi e non formule buule, quindi dobbiamo un attimo capire dove si colloca. Avevamo osservato che noi anche perché il teorema di Cook, avevamo osservato che L è un linguaggio generico, un linguaggio che non sappiamo che cosa sia. Quindi non sappiamo se sono forme, non sappiamo se sono grafiche, non sappiamo niente. Sappiamo che le istanze di L sono stringhe e noi queste stringhe vanno trasformate in dei grafi tale per cui se W appartiene a L nel grafo G c'è un pat da S a T. Se W non appartiene a L nel grafo G non ci deve stare un pat da S a T. Ok? Bello rognoso sta cosa perché noi d L non sappiamo niente. Come abbiamo superato questa cosa per il teorema? Sul teorema di Cook abbiamo detto di n sappiamo niente. Sappiamo che cosa? Che esiste una macchina non deterministica che in tempo polinomiale decide il linguaggio e quindi qui ci siamo inventati tutto quel gran casino per far vedere che il funzionamento di quella macchina non deterministica poteva essere codificato all'interno di una formula bulearca. Qua dobbiamo fare una cosa simile. Il funzionamento di una macchina NL che decide Linguaggio L va trasformato in un grafico. Questa è una cosa. Ok? In realtà è più semplice di quello che abbiamo visto per il teorema di Cook, infatti spero che in 20 minuti ce la facciamo. Ok? Allora, andiamo per intuizione. Cosa facevamo sul teorema di Cook? Noi avevamo che nella formula buleana dovevamo codificare sostanzialmente le configurazioni della macchina, i passaggi, questo step, quest'altro step, eccetera. Qui facciamo una cosa simile, però dobbiamo sfruttare il fatto che l'istanza di arrivo ha un grafo. Che ci andiamo a ficcare in questo grafo. Vi do l'intuizione prima e poi vediamo come si fa. I nodi del grafo G rappresenteranno, cioè noi andremo a costruire un grafo G i cui nodi saranno le configurazioni della macchina M sulla stringa W. Quindi i nodi di G ci diranno a che punto della computazione la macchina M che decide L è, mentre esegue su B. Quindi i nodi sono le foto. Nei nodi abbiamo le foto. A che punto è la computazione. Quelli sono i nodi. I nodi verranno collegati da archi se uno stato è il legal successor del precedente. Tutto qua. Dopodiché dentro questo grafo avremo la foto dello stato iniziale e la foto del target e potremo andare dallo stato iniziale a quello finale solamente se quello è accettante. Tutto qua. Ok, questa è l'idea che ci sta. Adesso però dobbiamo trovare il modo di farlo. Ok? È chiaro qual è l'obiettivo? Dovremmo generare un grafo che ha tanti nodi quanti sono i possibili stati di funzionamento della macchina M su W. Quei nodi li andiamo a collegare in base alla funzione di transizione. La parte trichi di tutto ciò è che questo vada fatto in in spazio logaritmico deterministico, quindi non è che lo possiamo generare tutto quel grafo e tenerlo su memoria di lavoro perché occuperebbe troppo spazio. Quindi noi tra virgolette dobbiamo generare su un nastro di output, cioè questa funzione futa sul proprio nastro di output, la tripla GST non può mantenere nella propria memoria di lavoro pezzi di grafo intero, occupa troppo spazio. Possiamo tenere cose intermedie e dobbiamo sputare in output qualcosa che noi sappiamo essere corretto già nel momento in cui lo stiamo scrivendo. Questa è la cosa, ok? È la parte difficile perché non possiamo tenere in memoria il grafo, occupa troppo spazio. Allora, facciamo così. Qua metto il coso e poi pagina no. Vi dicevo che a noi servono tanti nodi quanti sono i nodi, quanti sono le possibili foto, abbiamo detto del nastro, eh le foto della computazione, dello stato di computazione della macchina M. Ok? Allora, vi ricordo che questa macchina M è una macchina particolare il cui nastro di input non può essere scritto. Ok? Di conseguenza, la caratterizzazione delle possibili condizioni in cui si trovi la computazione di M su W sono caratterizzate da quali elementi? La il nastro di input rimane sempre lo stesso, quindi non è caratterizzante. Quello che caratterizza è dove sta la testina, quindi la posizione della testina sul nastro di input, ma non il suo contenuto, perché quello rimarrebbe costante in tutte tutte le ID. Quindi non è necessario generare nodi in questo grafo G che prendono in considerazione il fatto che il contenuto del nostro input cambi. No, quello non c'è bisogno. Dobbiamo considerare che la testina sul nastro di input può essere in posizioni diverse, ma non che il contenuto cambi, non è caratterizzante. Ok? Quindi, quali informazioni ci servono per stabilire a che punto del guado la macchina M è? Dove sta la testina sul nastro di input? Il contenuto del nastro di lavoro, perché quello può essere scritto, dove sta la testina sul nastro di lavoro e lo stato corrente di computazione, se siamo in Q4, se siamo in Q11, eccetera. Ok? Quindi questa foto del dello stato corrente della computazione è caratterizzato potrebbe essere scritta su una stringa di questo tipo. Quindi una stringa la cui parte iniziale contiene il contiene il contenuto del nastro di lavoro di M H1 e H2 che sono le posizioni. Sono dei numeri. Dice la testina sul primo nastro alla cella 15, la testina sul secondo nastro sta alla cella 170. Ok? e poi un codice Q per dirci in quale stato siamo, Q3, Q4, Q58, eccetera. Ok? È chiaro? Quindi questa è l'informazione. Se in questa stringa noi scriviamo queste informazioni così come ce le siamo dette, noi siamo in grado di stabilire a che punto del calcolo la macchina M. È chiaro? Questa stringa è una stringa di simboli miei principi. Gli possiamo dare il significato che vogliamo. Può essere il nome dei nodi in G. Noi utilizziamo questa stringa come la label dei nodi di G. Quindi la label dei nodi in G ci dirà cosa c'è su workday, dove sta la testina di primo nastro, dove sta la sina di secondo nastro, in quale stato la macchina. È chiaro? Cioè, noi facciamo sto trucco, questa cosa di Ma sai che sta configurazione io la uso per dare il nome ai nodi di G? Adesso se io prendo inizio a dare simboli a questa stringa, alcune saranno sensate, altre saranno non sensate. Nel momento in cui le generiamo, facciamo un check per vedere se stiamo buttando fuori spazzatura o meno. Sì, prego. Ti scusi, ma la stringa che abbiamo costruito è è la nostra assunzione? Al momento stiamo solo facendo delle considerazioni su quale informazione ci serve per catturare lo stato di avanzamento dei lavori di N. Dobbiamo sapere cosa c'è sul nastro di lavoro, dove stanno le due testine e in quale stato si trovi la luce. Quindi per adesso siamo ancora nella fase di idea. Ideaidea. Fase diidea. Ok. Allora, la macchina M che decide L, la macchina M che decide L è una macchina in quale classe di complessità? NL. Quanto spazio usa? Log space. Quindi questo quanto spazio Logaritmo di n log n è la lunghezza di W. Ok. Quanto occupa? Scriviamo qua. Quanto occupa l'indice sul primo nastro? È un numero. Quindi la 18ª cella. Quanto quanto bit ci servono? logaritmo di sotto e quindi in generale se la lunghezza del nostro input n quanti bit ci servono per indicizzare le celle su input logaritmo logaritmo di n quindi qua abbiamo o di log n questo qua h2 questo qua quasi è log di log log di log perché abbi dobbiamo indicizzare un nastro di taglia logaritmica quindi qua Qua sotto siamo o di log n. Ok. Numero di stati. Quanto spazio ci serve per indigizzare per nominare gli stati? Eh, sicuri logaritmico sul numero di stati, però no. Attenzione, prego. Che sia relativo all'ap, quindi direi No, no, no, sono gli stati della macchina M che sono numero. E questo numero com'è? È fissato. Sì. Il numero di stati della macchina non cambia rispetto all'input, quindi il numero di bit che ci servono per rappresentare Q è una costante. Ok? Quindi, se prendiamo questa stringa rappresentativa, quanto spazio ci serve per scriverla? Log log. Ok? Quindi noi siamo in grado di scrivere questa stringa con l'ad n. Ok? Allora, la nostra idea era ora qual è? È sputare perché noi stiamo definendo la funzione di trasformazione fia b e tira fuori gst. Dobbiamo far vedere ora la funzione fare fuori questa questo grafo. Ok? Allora, il principio che usa è questo. Ricordo che noi sostanzialmente dovremo definire un grafo fatto così. I suoi nodi sono tanti quanti le label, quelle label. Gli archi verranno messi fra nodi che sono uno illegal successor dell'altro secondo la funzione di transizione. Le configurazioni, i nodi relativi a configurazioni accettate verranno agganciate a un nodo extra che chiameremo T. Il nodo S è il nodo relativo alla configurazione iniziale della macchina. Tutto qua. Ok? Questa cosa però va fatto in spazio logaritmico, quindi dobbiamo riciclare roba. Cosa fa questo trasduttore F? per riuscire a generare, perché il grafo che genera è bello grosso, eh, perché noi abbiamo che questo qui è il nome dei nodi, quindi tutti i possibili nodi sono tutte le possibili stringhe che hanno questa forma, no? E quante sono? Sì, noi dobbiamo generare un grafo G i cui nodi hanno i nomi, tutti i possibili nomi che si possono costruire su una stringa che ha questa forma. Quanti sono? Attenzione, attenzione. No, abbiamo una stringa di 5 bit. Quante sono le stringhe di 5 bit? 2^ qu. Abbiamo una stringa di 10 bit. Quante sono le stringhe di 10 bit? Abbiamo una stringa di 12 simboli ternari. Quante sono? 3^ 12 Quindi noi abbiamo Quante sono le stringhe costruibili qua sopra? 2^ log n su n. Sì, sarebbe una costante che è la taglia dell'alfabeto eh per la taglia di questa stringa. Quindi noi avremo che il numero totale dei dei nomi che hanno questa forma, cioè quindi il numero totale che dei nodi che vanno a finire in G è un esponenziale rispetto a questa taglia. Ma questa taglia è logaritmica nella taglia di w, perciò avremo un polinomio nella taglia di W. Ma se quello è un polinomio nella taglia di W, mica può finire tutto nel work tape di F, non abbiamo spazio, eh. Quindi noi questo grafo non è che lo possiamo piazzare tutto là, poi quando siamo sicuri lo soriniamo, no? va costruito pezzo a pezzo e buttato fuori. Allora, facciamo in questo modo. Praticamente la macchina, il trasduttore che fa F, fa questa cosa qua. Si prende quella stringa lì, no, con tutti i campi, come abbiamo visto, eccetera. Vi do l'intuizione di come si fa. Eh, poi penso che sugli appunti di Calauti sia più preciso, però visti le tempistiche cerco di farvi capire. Sostanzialmente la macchina F questo, istanzia una di questa stringa sul nastro di lavoro e fa questo check. Questa stringa è qualcosa di sensato perché per esempio come posizione delle testine e come nome dei stati potrebbero comparire simboli che non c'entrano assolutamente niente perché la la stringa la macchina che fa? Devo generare stringhe di 137 simboli e io ne inizio a generare tutte, però alcune di queste sono senza senso. Quindi parto dalla stringa 00, poi parto da 00 stella, 00 casetta. Ok. Cioè io mi faccio come faccio il conto? Vado avanti. Come li avete visti i contachilometri, quelli quelli digitali, quelli di una volta che giravano. Io li faccio gira tutti, me le faccio tutte le rappresentazioni. Allora, la macchina F che fa F che fa? Si prende questa string, se la mette su work daye, quanto spazio occupa? Logaritmico. Inizia a ciclarsene, quindi guarda la corrente. Ha senso, sì, sputata in output, quello è un nodo. Vado alla successiva. Ha senso, no? Andiamo alla prossima. Guardo la successiva. Ha senso sì, sputate in output e così a poco a poco genero tutti i nodi di G. Not che i nodi di G vengono sputati in output senza essere tutti presenti allo stesso momento sul nastro di lavoro. È questo il trucco, cioè che io non li posso tenere tutti assieme. Quindi io c'ho questa stringa e la ciclo. Vado avanti avanti e sputo fuori. Ta ta ta. Ok, è chiaro? Finito questo giro, prendo una stringa e aggiungo un simbolo, escludo quest'ultimo nodo che lo chiamiamo VDAR. Questo sarà T, che è un nodo differente da tutti gli altri perché perché c'è un simbolo in più. Ok? Quindi è un nodo speciale. Cancelliamo tutto, ripuliamo il worktake, ripartiamo stavolta con due stringhe e io le conto entrambe e però stavolta faccio lo stesso giochino per fare cosa? Per considerare tutte le coppie. Che ci faccio con queste coppie? Simile. La prima è sensata. Sì. La seconda è sensata. Sì, la seconda è un legal successor della prima. Sì, sputo la coppia in output. Quella è un'arma. Quando non verifico questa condizione, vado alla successiva. Vi faccio notare che il grafo non è mai presente completamente su worktape. Cioè, noi stiamo avanzando contatori. Prego. Ma possiamo fare direttamente le coppie di fare prima i figoli? Si potrebbe anche fare. Sì, sì, sì. Si potrebbe anche fare magari. in una rappresentazione nella quale non c'è una rappresentazione esplicita dei nodi, ma abbiamo direttamente gli archi. Quello si può fare, è una scelta tipo sugli appunti di Cala che credo sia fatto così. Ok? Sì, prego. Quindi, allora, prima i nodi. Un secondo, un secondo. Finisco questo e poi perché potremmo non aver tempo, poi in caso mi fa la domanda. Ci siamo, che facciamo? Usiamo cicliamo questa stringa per sputare i nodi. Cicliamo le e poi sputiamo VAR che è un nodo con un nome speciale. Poi cicliamo su tutte le coppie così su quelle sensate mettiamo gli archi in output. Ok? Cosa rimane da fare? Mentre cicliamo queste, dopo che abbiamo fatto il ciclo delle coppie, cancelliamo tutto, rifacciamo il giochino dell'esplorazione per andare a vedere quali sono le configurazioni con uno stato accettante. Quelle lì le agganciamo a Bostar, ok? Perché dobbiamo fare tutto sto gioco. Abbiamo generato G, vanno generate. È chiaro come G viene generato? Perché avremo dentro G ci sono tanti nodi relativi a stati a configurazione accettanti, ma noi il pat deve essere da S verso un nodo specifico, quindi ne mettiamo uno fettizio in maniera tale che il pat è sempre verso il nodo fittizio. Ok? Generiamo il grafo. S è la è il nodo del 3G che è la configurazione iniziale della partenza T e distante. Finito. Per come è costruito questo grafo a un pat da S a T? Se solo se m accetta w e quindi siccome l era un linguaggio generico che si riduce a ritability allora avremo che richability è NL hard. Chiaro? Sì. Prego. Ma dopo appena analizzato le coppie non vado avanti per analizzare 30. No, no, no. Le coppie ci servono perché noi dobbiamo generare gli archi del grafo e gli archi del grafo sono coppie perché io devo devo poter dire vado dal nodo 3 al modo 6, quindi genero le coppie sono sensate. Uno è successo l'altro sì. Allora lo chiaro? Prego. Star, cioè quindi lo star alla fine viene messo in negli stati finali. Star è un nodo specifico di G che riceve come archi entranti da tutti i nodi accettati. E viene generato generando un nodo aggiuntivo con una stringa più lunga in maniera tale che non sia nessuno degli altri che abbiamo la certezza che non sia nessuno degli altri. Ok? Great guys. Qua proprio eh. Allora, fermato