Lezione 1 – 06/03/2019 Abbiamo un sacco di numeri interi e possiamo confrontarli. Possiamo immaginare di avere una bilancia, che pesa due numeri, e possiamo chiedere quale dei due pesa di più. Chiaramente stiamo pensando a un insieme totalmente ordinato, come posso trovare il MASSIMO? Per spezzare il problema e renderlo più piccolo, ho pensato che ho n pesi, allora posso pensare di prendere dei valori precisi per n e testare: - Se n = 3, ho tre pesi A, B, C, con una bilancia a bracci uguali prendo A e B e li confronto, uno dei due sarà più pesante, e quello lo confronto con C. n = 3 mi dà un numero di pesate pari a 2. Se n = 2, basta una sola pesata. Se n = 1, trovare il più pesante è inutile, quindi non faccio pesate. Quindi ci conviene aumentare n. Per n generico cerco di fare un confronto a coppie e “butto via” quello che perde. Quante pesate finisco a fare in questo modo, facendo delle coppie disgiunte? Numero oggetti 1 2 3 Pesate 0 1 2 Arriviamo all’INDUZIONE MATEMATICA, ovvero quando sono nel caso n = 3, io confronto prima una coppia e quindi faccio 1 pesata, e poi mi riconduco al caso precedente n = 2, da cui avrò 1+1 = 2 e corrisponde a quando visto per il caso n = 3. Legge di Church-Turing: tutto ciò che è computabile è ricorsivo. Ora facciamo delle CONGETTURE. Voglio convincermi che il processo termina, come termina e le quantità in gioco. Ora n1 = 7, facendo 3 coppie abbiamo n2 = 4. Il processo consuma oggetti, dato che parto con un numero finito di oggetti, c’è una monovariante, perché ogni volta il numero di oggetti cala, quindi ho un certo controllo. Quindi questo mi dice che il mio processo termina. Riusciamo a stabilire quante sono le pesate che faccio? La congettura è che siano n-1, e mi piace perché sembra vicino al vero. Quindi l’algoritmo termina, l’abbiamo analizzato non solo dal punto di vista che termina, ma anche dal punto di vista delle risorse che impiega. Come posso dimostrarlo in generale? Ho n oggetti, se n>1 posso trasformare l’istanza e darla alla Fatina Ricorsina, e l’unico modo che ho per modificare questa istanza è l’uso della bilancia. Quindi di questi oggetti ne prendo 2, e l’Oracolo mi dice quale pesa di più. Quello che pesa meno è fuori dai giochi, quindi per qualsiasi schema di pesature per risolvere il problema, non metterà più sulla bilancia l’oggetto che pesa meno. No anzi adesso la prende un po’ più lunga e introduce i grafi, e li usa come modelli per rappresentare dei problemi, per semplificare la situazione. Supponiamo di avere 4 oggetti e li vedo come nodi di un grafo e quando faccio una pesata ci metto l’arco. 1 2 3 4 Posso codificare di più in questo grafo? Sì, anche l’esito. Perché se oggetto 2 > oggetto 1, posso mettere una freccia che va da 2 a 1. Voglio sapere quale può essere il numero di archi, perché corrisponde al numero di pesate che io faccio. Cerchiamo di chiederci com’è fatto questo grafo, che quando termina mi dice qual è il massimo. 1 2 3 4 Un grafo fatto come quello sopra può essere uno di quelli che mi trova il massimo? Non contiene cicli grap, sarà un DIGRAFO ACICLICO. A un certo punto le mie pesate mi devono bastare per capire quale sia la biglia più pesante. Se ho un grafo aciclio tutto in avanti, mi direbbe che la biglia più grande è quella su cui arrivano tante frecce, ce la faccio anche con pochi archi, solo con tre ce la faccio a convincermi che c’è un massimo. Passiamo da calcolare la soluzione al problema a certificarla, ovvero a dire che ci posso arrivare. La teoria della complessità ci ha restituito l’importanza dei certificati. 1 2 3 4 Per trovare il “certificato” che il mio grafo mi porta al massimo ma con il numero più basso possibile di archi, considerando la figura sopra, è cambiarlo nel modo seguente: 1 2 3 4 Cioè vale a dire che faccio confronto 1-2 e 3-4 e poi confronto i vincitori. Questo digrafo basta a convincermi che trovo il massimo. È necessario avere 3 archi o c’è un digrafo con meno archi che mi può convincere? Abbiamo un DAG e qui noi vogliamo che ci sia un unico pozzo, e ci deve essere un arco che esce, cioè che ha perso con qualcuno, che può a sua volta aver perso con qualcuno. Finché non arrivi all’unico pozzo, che è l’unico elemento massimo. Se invece il pozzo non è unico, ci son tipo due che non hanno mai perso, non posso applicare la proprietà transitiva. Il minimo numero di archi perché abbia un unico pozzo è quello che devo trovare, è vero che ne devo usare n-1, da ogni nodo ne deve uscire uno. È vero che ogni arco ha due estremi, ma ogni arco esce da al più un nodo. Tutti gli n-1 nodi non pozzi possono avere ciascuno un arco che esce, quindi ne servono davvero almeno n-1. Il digrafo mi rappresenta una generica relazione, cioè è un sottoinsieme delle coppie di un prodotto cartesiano. Una generica relazione è un grafo diretto bipartito, le relazioni sono l’oggetto base. I grafi ci danno modo di lavorare in modo grafico e di visualizzare relazioni. Torniamo al problema di prima (ultimo disegno sopra). Questo DAG non è il primo che mi viene in mente per dire che 4 è il massimo, perché 1 2 3 4, sarebbe più naturale, e troverei sia il più grande sia il più piccolo, questo ordinamento mi permette di trovare anche l’i-esimo. Da qui posso ottenere un argomento più semplice? L’ordinamento in questo modo degli oggetti non ce la si fa con le pesate, un tale algoritmo può non essere così semplice. Per trovare il massimo un metodo un po’ più strutturato è che gli oggetti siano in fila (non necessariamente di peso), confronto i primi due e quello che perde esce di scena. Confronto il next con il vincitore e tengo il vincitore tra i due, e ripeto questo procedimento fino alla fine, questo mi trova il max con n-1 confronti. Io voglio un algoritmo che trovi il massimo su n numeri: ne prendo 2, li confronto, quello che perde lo butto via e chiedo alla fatina ricorsina di trovarmi il massimo di questi altri. La fatina ricorsina usa n-2 confronti più quello che ho fatto io mi torna n-1. Codice python per la ricerca del massimo: def find_max(lista): presunto_max = lista[0] for val in lista[1:] if val > presunto_max presunto_max = val return presunto_max Come faccio a prevedere se e come termina l’algoritmo? Non posso prevederlo perché l’unico modo che una macchina ha per prevedere l’andamento è simularlo. Con questo algoritmo la lista si consuma quindi sicuramente termina, ma quello che io devo dire è che termina e ritorna il massimo. Se l’if non scatta, il presunto_max sta nella lista, se scatta il presunto_max cambia e poi sta nella lista. Se l’if non scatta mai, l’unico modo perché quello che mi ritorna sia nella lista e di impostare come valore iniziale di presunto_max il primo elemento della lista. L’invariante di ciclo in questo algoritmo è data da presunto_max, perché è quello che mi aiuta a chiudere il mio ciclo, cioè ad ogni giro del ciclo trovo il migliore è quello che mi dice se devo continuare a fare confronti.