Esercizio 1 Quando siete nati Esercizio l Studiamo

  • Slides: 34
Download presentation
Esercizio 1 Quando siete nati?

Esercizio 1 Quando siete nati?

Esercizio l Studiamo il seguente problema: l l In una classe di (circa) 30

Esercizio l Studiamo il seguente problema: l l In una classe di (circa) 30 studenti, quale è la probabilità che almeno due persone siano nate lo stesso giorno dell’anno? Proviamo 2 approcci l l l Teorico (lo calcoliamo) Sperimentale (lo simuliamo) … e poi confrontiamo

La teoria… l l l Abbastanza facile se uno comincia a ragionare dal punto

La teoria… l l l Abbastanza facile se uno comincia a ragionare dal punto di vista giusto Ho N persone, M giorni l’anno (M=365) Quanti sono TUTTE le possibili combinazioni (insieme universo)? l Esempio semplificato N=2, M=3 l 1, 1 1, 2 1, 3 2, 1 2, 2 2, 3 3, 1 3, 2 3, 3 l l l l Le due persone possono indipendentemente essere nate in uno dei 3 giorni U = Mx. M = MN

l Di queste, quali sono quelle che NON hanno ripetizioni? l Cosa succede: l

l Di queste, quali sono quelle che NON hanno ripetizioni? l Cosa succede: l La prima persona puo` essere nata in un giorno qualsiasi, la seconda in tutti tranne quelli in cui e’ nata la prima, ecc l 3 x 2 = 6 § § § 1, 2 1, 3 2, 1 2, 3 3, 1 3, 2 Generalizzando: Mx(M-1)x(M-2)x(M-3) …. (M-N+1) N pezzi

l Quindi posso scrivere l Queste sono le possibili combinazioni per cui NON si

l Quindi posso scrivere l Queste sono le possibili combinazioni per cui NON si hanno 2 persone nate nello stesso giorno

… a questo punto l l La probabilità cercata è semplicemente la frazione di

… a questo punto l l La probabilità cercata è semplicemente la frazione di possibilità che NON rientrano nel caso precedente P = U-A/U = 1 -A/U

Ok? l l l Siete convinti? Caso N=2, M=3, la formula dà P=1 -(3!)/((1!)(32))

Ok? l l l Siete convinti? Caso N=2, M=3, la formula dà P=1 -(3!)/((1!)(32)) l l =1 -6/9 = 1/3 Perfetto, allora scriviamo un programma che faccia questo l È più interessante di quanto sembri, promesso

Il fattoriale. . . l l Purtroppo, in C++ non c’è predefinita la funzione

Il fattoriale. . . l l Purtroppo, in C++ non c’è predefinita la funzione fattoriale Meglio, ce la facciamo noi l l In modo facile, ciclo for In modo complicato e elegante l ricorsione

fattoriale int fact(int i){ if (i<1) return 1; int result=1; for (int k=i ;

fattoriale int fact(int i){ if (i<1) return 1; int result=1; for (int k=i ; k>0 ; k--) { result*=k; } return result; } Protezione per fattoriale di 0 e numeri negativi Ciclo di moltiplicazione

Potenza int potenza (int i, int j){ int result =1; for (int k=0 ;

Potenza int potenza (int i, int j){ int result =1; for (int k=0 ; k<j ; k++) { result *=i; } return result; }

Programma principale. . . int main(){ int m, n; cout << "Inserisci M "<<endl;

Programma principale. . . int main(){ int m, n; cout << "Inserisci M "<<endl; cin >> m; cout << "Inserisci N "<<endl; cin >> n; float risultato; risultato = 1. - fact(m)/((fact(m-n)*potenza(m, n))); cout <<" La probabilita vale "<<risultato<<endl; }

l Prego … l Scrivete! l TESTATE: l M=3, N=2 dovrebbe dare 0. 3333

l Prego … l Scrivete! l TESTATE: l M=3, N=2 dovrebbe dare 0. 3333 l M=365, N=30 dovrebbe dare … indovinate!

Ha funzionato? l l Immagino di no. . . C/C++: l Il risultato di

Ha funzionato? l l Immagino di no. . . C/C++: l Il risultato di un’operazione è quello della più complessa delle variabili utilizzate nell’operazione (non nell’assegnazione) l float pluto = 1/3; cout <<pluto; l l float pluto = 1. /3; cout <<pluto; l l Stampa 0, perchè 1/3 vale 0. 333 e l’intero più vicino è 0 Stampa 0. 333, perchè nell’operazione c’è un numero in virgola mobile. Soluzione: costringere una delle variabili presenti ad essere a virgola mobile l Basta prendere un intero e moltiplicarlo per 1.

Ha funzionato? l l Provate con M=5, N=6 Il programma che dice ? l

Ha funzionato? l l Provate con M=5, N=6 Il programma che dice ? l l Il mio dice 0. 992 Ma è sbagliato! l l Ovviamente se ho M=5, i primi 5 tentativi possono essere diversi fra loro, ma il sesto no! La risposta dovrebbe essere 1

Dove sta il problema? l Questa volta e’ analitico: l Se N>M, il modo

Dove sta il problema? l Questa volta e’ analitico: l Se N>M, il modo in cui abbiamo calcolato il numero delle possibilita’ senza ripetizione e’ sbagliato l M=5, N=6 l l Il primo puo’ scegliere fra 5 possibilita’, il secondo fra 4 … il sesto fra 0, per cui il valore di A vale zero. Se M<N, la risposta deve essere 1

Ha funzionato? l l Meglio, funziona per M=3 e N=2, M=5 e N=6 Ma

Ha funzionato? l l Meglio, funziona per M=3 e N=2, M=5 e N=6 Ma per M=365, N=30 ? Problema: una variabile intera in un computer non è un intero matematico, ma una serie di 0 e 1 scritta da qualche parte. NON può contenere tutti i numeri fra -∞ e +∞ Macchine a 32 bit (le nostre) l Un intero è generalmente 4 bytes, e cioè 32 bit l 232 vale 4294967296, che è quindi il massimo intero che si può considerare l Anzi no, se si considera che tipicamente un numero puo’ essere positivo o negativo, quindi circa la meta’

C/C++ l Na. N: e’ una sequenza di bit particolare, che vuol dore che

C/C++ l Na. N: e’ una sequenza di bit particolare, che vuol dore che c’e’ stato un errore irrisolvibile nei calcoli matematici l l Na. N: Not a Number Avete idea di quanto sia 365! ? ?

Scopriamo I limiti l l l In C sono definite alcune costanti che ci

Scopriamo I limiti l l l In C sono definite alcune costanti che ci possono essere utili: INT_MAX: il massimo intero che si puo’ rapprensetare, in questo caso con 32 bit UINT_MAX: lo stesso, ma per l l unsigned int pippo; Facciamocelo stampare l M=33, N=2 l ? ?

Risultato l Usando int, posso al massimo calcolare l 11! l Con unsigned int,

Risultato l Usando int, posso al massimo calcolare l 11! l Con unsigned int, 12! l Un po’ deludente….

Passiamo alle variabili in virgola mobile l l Float: il massimo usabile e’ 1038,

Passiamo alle variabili in virgola mobile l l Float: il massimo usabile e’ 1038, cioe’ ~ 34! Double: e’ un float con piu’ bit a disposizione (64 di solito), con un range molto maggiore, ce la fa a reggere l l 170! = 10306 Ultima possibilita’: l long double; 1750! = 104917 (128 bit) Da cui M=365, N=30 da’ 0. 7063 l Ce l’abbiamo fatta, ma non vi sembra un po’ stupido?

Informatica != matematica l Esempio cretino:

Informatica != matematica l Esempio cretino:

Facciamo un po’ di matematica l l Il risultato che noi ci aspettiamo è

Facciamo un po’ di matematica l l Il risultato che noi ci aspettiamo è compreso fra 0 e 1 Perché dobbiamo essere costretti a moltiplicare dividere due numeri di 700 cifre per ottenerlo?

Meglio ancora. . . l 10700 è un numero grande, ma il suo logaritmo

Meglio ancora. . . l 10700 è un numero grande, ma il suo logaritmo è gestibile

Alternativamente… l Il prodotto che da’ A/U lo posso scrivere come prodotto di numeri

Alternativamente… l Il prodotto che da’ A/U lo posso scrivere come prodotto di numeri piccoli: N pezzi

Allora … l Posso semplificare le cose in modo semplice float result=1; for (int

Allora … l Posso semplificare le cose in modo semplice float result=1; for (int k=m-n+1; k<=m ; k++){ result*=1. *k/m; } result = 1. -result; Moltiplico N pezzi non molto diversi da 1

Ultima cosa: la ricorsione. . . l Torniamo indietro, per calcolare il fattoriale avevamo

Ultima cosa: la ricorsione. . . l Torniamo indietro, per calcolare il fattoriale avevamo fatto int fact(int i){ if (i<1) return 1; int result=1; for (int k=i ; k>0 ; k--) { result*=k; } return result; } Perfetto, funziona. . . Ma c’è un altro modo molto pù elegante In C/C++ è possibile che una funzione chiami sé stessa: ricorsione!

Matematicamente. . . l l Il fattoriale di N è N*(N-1)*(N-2). . . *1

Matematicamente. . . l l Il fattoriale di N è N*(N-1)*(N-2). . . *1 Ma lo posso anche scrivere come l l l N!=N*(N-1)! N!=N*(N-1)*(N-2)! Quindi in C++ vorrei poter scrivere fact(i)=i*fact(i-1) MN=M*M(N-1)

l Mi serve però una condizione di stop per non dover continuare in eterno

l Mi serve però una condizione di stop per non dover continuare in eterno l Se i=1, fact(1) vale 1 e non 1*fact(0) int fact(int i){ if (i<=1) return 1; return i*fact(i-1); } 5*4*3*2*1*0*-1*-2*-3. . int potenza (int i, int j){ if (j<=0) return 1; return i*potenza(i, j-1); }

Approccio Sperimentale l Definizione frequentistica di probabilita’: l l Se avessi tante classi (tante->

Approccio Sperimentale l Definizione frequentistica di probabilita’: l l Se avessi tante classi (tante-> infinito), in quante avrei la ripetizione di una data? P = lim (R/U) l l R = numero di volte in cui c’è ripetizione U = numero di volte che ho provato

Però. . . l Perché questo abbia senso, i miei tentativi devono essere l

Però. . . l Perché questo abbia senso, i miei tentativi devono essere l Tanti (faccio il limite. . . ) l l Tanti rispetto a cosa? Devono coprire l’intero range delle possibilità l Elezioni: per fare un exit poll decente non posso chiedere solo agli ultracinquantenni ricchi

Il problema non è banale. . . l Ci sono molti metodi che possano

Il problema non è banale. . . l Ci sono molti metodi che possano garantire un sampling rappresentativo l Il più usato (e anche spesso il più facile da fare) è detto approccio Monte Carlo l l In soldoni: utilizzo un generatore di numeri casuali inmodo tale che tutte le possibili configurazioni abbiano la stessa probabilità di essere “sorteggiate” Ripeto lo studio fino a che non vedo che delle ulteriori prove non cambiano sostanzialmente il risultato l Meno banale di quello che sembra, la prossima (? ) volta vi farò vedere un caso in cui non è possibile

Nel nostro caso l l l Ogni “studente” ha la stessa probabilità a priori

Nel nostro caso l l l Ogni “studente” ha la stessa probabilità a priori di nascere in un qualunque giorno dell’anno P(i)=1/365 Per cui hoi bisogno di un generatore di numeri casuali che mi dia in uscita un numero intero compreso fra 1 e 365, tutti con la stessa probabilità l Per fortuna esiste e non dobbiamo farlo noi

Come generare un numero random in C l Semplice, funzione rand() l l Ritorna

Come generare un numero random in C l Semplice, funzione rand() l l Ritorna un INTERO fra 0 e RAND_MAX Per cui, per avere una distribuzione flat fra 0 e 1 (che poi possiamo usare come meglio ci aggradi), devo fare l double numero_casuale_flat = rand()/RAND_MAX l ? ? l double numero_casuale_flat = rand()/((double)RAND_MAX) double numero_casuale_flat = rand()/(1. *RAND_MAX) l

Strategia l l l Simulo tante classi di N studenti In ogni classe “sparo”

Strategia l l l Simulo tante classi di N studenti In ogni classe “sparo” N numeri casuali fra 0 e 364 o 1 e 365 Controllo se ho dei numeri ripetuti l l l Se sì: incremento R Se no: non incremento R Alla fine, faccio R/Num. Classi e questo asintoticamente dovrebbe raggiungere la probabilità cercata. .