Esercizio Costruiamo un generatore di numeri random, per esempio
Transcript
Esercizio Costruiamo un generatore di numeri random, per esempio
Esercizio Costruiamo un generatore di numeri random, per esempio secondo una gaussiana. Per generare una generatore di numeri random distribuiti secondo una gaussiana usiamo un metodo semplice e sufficientemente preciso: il metodo BoxMuller. See Numerical Receipies in Fortran,C o C++ chapter 7.2 F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 265 Esercizio Di solito in quasi tutti i linguaggi di programmazione abbiamo a disposizione un generatore di numeri random secondo una distribuzione piatta, ovvero con una distribuzione di probabilità uniforme di generare un numero tra x e x+dx: ⎧dx 0 < x < 1 p ( x)dx = ⎨ ⎩0 per ogni altro valore Essendo la probabilità normalizzata: ∞ ∫ p( x)dx = 1 −∞ F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 266 Esercizio Nel caso del C e del C++ abbiamo un generatore che genera numeri random, fino a RAND_MAX (macro di preprocessore): rand() Quindi, se si desidera una variabile uniforme nell’intervallo [0,1[, di solito si definisce: #define DRAND(PAR) rand()/(double)RAND_MAX E’ anche possibile definire il seme di partenza usando: sran( int ) F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 267 Esercizio Supponiamo di sapere generare in modo uniforme x, e consideriamo una funzione: y (x) la sua distribuzione di probabilità sara: p ( y )dy ovvero: p ( y )dy = p ( x)dx dx p( y ) = p( x) dy F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 268 Esercizio Quindi se la probabilità di una distribuzione arbitraria è una funzione positiva, invertibile e normalizzata ad 1, avremo: p( y) = f ( y) ∞ ∫ f ( y) = 1 −∞ dx = f ( y) dy La soluzione dell’ultima eq. differenziale: y y x = F ( y ) = ∫ f ( y )dy = ∫ p ( y )dy 0 0 F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 269 Esercizio Quindi la trasformazione che da una distribuzione uniforme ci porta ad una distribuita secondo f ( y ) è: −1 y ( x) = F ( x) −1 essendo F la funzione inversa di F . Tutto dipende dall’invertibilità dell’integrale di f ( y ) e dalla sua calcolabilità (numerica o analitica) F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 270 Esercizio C’è una semplice interpretazione geometrica. F ( y ) altri non è che l’area sotto la curva di probabilità a sinistra di y . Quindi devo: Scegliere x random Trovare il valore y per cui l’integrale della curva di probabilità vale x F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 271 Esercizio Nel caso del metodo Box-Muller, partiamo da: 2 y − 1 p ( y ) dy = e 2 dy 2π E consideriamo l’estensione del metodo precedente in più dimensioni: p ( y 1 , y 2 , K ) dy 1 dy 2 K = ∂ (x1 , x 2 , K ) dy 1 dy 2 K p ( x1 , x 2 , K ) ∂ ( y1 , y 2 , K ) F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 272 Esercizio E consideriamo: y1 = − 2 ln x 1 cos( 2 π x 2 ) y2 = − 2 ln x 1 sin( 2 π x 2 ) Queste le possiamo invertire: ⎡ 1 x 1 = exp ⎢ − ( y 12 + ⎣ 2 ⎛ y2 1 x2 = arctan ⎜⎜ 2π ⎝ y1 ⎤ y )⎥ ⎦ ⎞ ⎟⎟ ⎠ 2 2 F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 273 Esercizio Lo Jacobiano diventa quindi: ∂ x1 ∂ ( x1 , x 2 ) ∂ y1 = ∂x2 ∂ ( y1 , y 2 ) ∂ y1 ⎡ −⎢ ⎢⎣ 1 e 2π y 12 − 2 ⎤⎡ ⎥⎢ ⎥⎦ ⎢⎣ ∂ x1 ∂y2 = ∂x2 ∂y2 1 e 2π y 22 − 2 ⎤ ⎥ ⎥⎦ F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 274 Esercizio Possiamo generare x1 e x 2 indipendentemente per ottenere le due y 1 e y 2 Se consideriamo le variabili trigonometriche: R 2 ≡ v12 + v 22 → x1 angle ( v 1 , v 2 ) → 2 π x 2 Possiamo scrivere: ( − 2 ln x (v y1 = − 2 ln x 1 v 1 / y2 = / 1 2 ) R ) R2 2 F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 275 Esercizio In breve: Generiamo v 1 e v 2 nel quadrato di lati [-1,1] Controlliamo che siano nella circonferenza unitaria Calcoliamo y 1 e y 2 con la trasformazione di BoxMuller: ( − 2 ln x (v y1 = − 2 ln x 1 v 1 / y2 = / 1 2 ) R ) R2 2 Ritorniamo un valore e salviamo l’altro per il passaggio successivo. F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 276 Esercizio double v1,v2,rsq,fac,g; switch(_flag){ case 0: // Generate two new random number rsq=2.; while( (rsq>=1.) || (rsq==0.) ){ v1=2.*DRAND()-1.; v2=2.*DRAND()-1.; rsq=pow(v1,2.)+pow(v2,2.); } fac=sqrt(-2.*log(rsq)/rsq); _g1=v1*fac; _g2=v2*fac; _flag=1; g=_g1; break; case 1: g=_g2; _flag=0; break; default: std::cout << " gaussrnd::Grnd flag error ! _flag= " << _flag << std::endl; break; } return g; F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 277 Esercizio #ifndef GAUSSRND_H #define GAUSSRND_H #include <iostream> #include <cstdlib> #include <cmath> #define DRAND(PAR) rand()/(double)RAND_MAX #define SRAND(PAR) srand(PAR) #define RANGEN // To generate a normal distributed Gaussian rand number // distribution using the Box-Muller method. See Numerical Receipies // in Fortran, chapter 7.2 class gaussrnd{ private: int _flag; int _seed; double _g1,_g2; public: // Init the rand48 random number generator gaussrnd(const int &seed=0 ): _flag(0) {NewSeq(seed);} // Inizialize a new random sequence void NewSeq(const int &seed) {_seed=seed; SRAND(_seed);} double Grnd(); void Print(); }; #endif //GAUSSRND_H F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 278 Esercizio #include "gaussrnd.h" double gaussrnd::Grnd(){ double v1,v2,rsq,fac,g; switch(_flag){ } case 0: // Generate two new random number rsq=2.; while( (rsq>=1.) || (rsq==0.) ){ v1=2.*DRAND()-1.; v2=2.*DRAND()-1.; rsq=pow(v1,2.)+pow(v2,2.); } fac=sqrt(-2.*log(rsq)/rsq); _g1=v1*fac; _g2=v2*fac; _flag=1; g=_g1; break; case 1: g=_g2; _flag=0; break; default: std::cout << " gaussrnd::Grnd flag error ! _flag= " << _flag << std::endl; break; } return g; void gaussrnd::Print() { std::cout << " gaussrnd::Print max random number: " << RAND_MAX << std::endl << " Seed : " << _seed << std::endl; } F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 279 Esercizio Adesso che abbiamo il nostro generatore random, come facciamo a controllarne il risultato, i.e. a vedere la distribuzione ? Costruiamo un istogramma !! F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 280 Esercizio Immaginiamo una semplice classe con un massimo, un minimo ed un numero di divisioni: struct simple{ double xmin_,xmax_; int bin_; }; e pensiamo ad un metodo: int simple::Bin( double x ) { return (x>=max_) ? (bin_+1): int( ((x-min_)/((max_-min_)/bin_)); } Questo metodo, dato un valore x, ritorna: bin_+1, se x è maggiore o uguale di max_ un intero tra 1 e bin_, se x è maggiore di min_ e minore di max_ Un numero minore uguale a 0 se x è minore o uguale a min_ F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 281 Esercizio Immaginiamo una semplice classe con un massimo, un minimo ed un numero di divisioni: struct simple{ double xmin_,xmax_; int bin_; }; e pensiamo ad un metodo: int simple::Bin( double x ) { return (x>=max_) ? (bin_+1): int( ((x-min_)/((max_-min_)/bin_)); } Questo metodo, dato un valore x, ritorna: bin_+1, se x è maggiore o uguale di max_ un intero tra 1 e bin_, se x è maggiore di min_ e minore di max_ Un numero minore uguale a 0 se x è minore o uguale a min_ F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 282 Esercizio Quindi se scrivo: int Max(const int a, const int b) const { return ( a<b) ? b: a; } Posso riscrivere Bin: int simple::Bin( double x ) { return Max( (x>=max_) ? (bin_+1): int( ((x-min_)/((max_-min_)/bin_)), 0 ); } Adesso Bin(x), dato un valore x, ritorna: bin_+1, se x è maggiore o uguale di max_ un intero tra 1 e bin_, se x è maggiore di min_ e minore di max_ Un numero uguale a 0 se x è minore o uguale a min_ F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 283 Esercizio Quindi questo oggetto dato un range: ]min_, max_[, ed un numero di bin: bin_, ritorna il numero di bin di un valore qualsiasi: x Se gli aggiungo un titolo, posso utilizzarlo come asse dell’istogramma: class axis { public: int Bin(double x) ; private: int Max(const int a, const int b) const ; double max_; double min_; int bin_; string title_; }; F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 284 Esercizio Completandola con setters, getters e Print(): class axis { public: int Bin(double x) ; double GetMax() const { return max_; } double GetMin() const { return min_; } int GetBin() const { return bin_; } const char * GetTitle() const { return title_.c_str(); } void SetMax( double x ) { max_ = x; } void SetMin( double x ) { min_ = x; } void SetBin( int x ) { bin_ = x; } void SetTitle( const char *x ) { title_=x; } void SetTitle( const string & x ) { SetTitle(x.c_str() ) ; } void Print() const; private: int Max(const int a, const int b) const ; double max_; double min_; int bin_; string title_; }; F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 285 Esercizio Potrebbe tornare comodo il metodo inverso di Bin(): #define BINERROR -999999. #define UNDERFLOW -1111111. #define OVERFLOW 111111. double axis::GetBinMin( const int i ) const { if(!i) return UNDERFLOW; if(i==bin_+1) return OVERFLOW; return (i<=bin_) ? double(min_+(i-1)*(((max_min_)/bin_))) : BINERROR; } Questo metodo dato un intero i ritorna: -1111111., se i è pari a 0, UNDERFLOW; 1111111., se i è pari a bin_+1, OVERFLOW; -999999., se i è maggiore di bin_+1, BINERROR; un valore compreso tra min_ e max_ se i e compreso tra 1 e bin_ Quindi calcolare l’estremo superiore: double GetBinMax( const int i ) const { return GetBinMin(i+1); } F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 286 Esercizio Quindi la class definition finale diventa: #ifndef AXIS_H #define AXIS_H #include <iostream> #define BINERROR -999999. #define UNDERFLOW -1111111. #define OVERFLOW 111111. using std::string; using std::cout; using std::endl; class axis { public: int Bin(double x) ; double GetMax() const { return max_; } double GetMin() const { return min_; } int GetBin() const { return bin_; } deouble GetBinMin) const int I ) const; double GetBinMax( const int i ) const { return GetBinMin(i+1); } const char * GetTitle() const { return title_.c_str(); } const char * GetTitle() const { return title_.c_str(); } void SetMax( double x ) { max_ = x; } void SetMin( double x ) { min_ = x; } void SetBin( int x ) { bin_ = x; } void SetTitle( const char *x ) { title_=x; } void SetTitle( const string & x ) { SetTitle(x.c_str() ) ; } void Print() const; private: int Max(const int a, const int b) const ; double max_; double min_; int bin_; string title_; axis.h }; # endif //AXIS_H F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 287 Esercizi Quindi un istogramma potrebbe essere fatto : typedef vector <int> vint; class hist { Crea un vector di interi public: hist(double xmax=1., double xmin=0., int bin=100, const char *t="none", const char *at="none"): xaxis_(xmax,xmin,bin,at), count_(bin+2), entries_(0), title_(t) {}; bool Fill(double x){ int i=xaxis_.Bin(x); if(i<=count_.size()) { count_[i]++; entries_++; return true; } return false; } Sfrutta Bin(int) di axis per calcolare il numero di bin che sarà l’i-esimo oggetto contenuto nel vector private: vint count_; axis xaxis_; int entries_; string title_; }; F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 288 Esercizi Notare l’utilizzo del costruttore di vector in cui viene specificato il numero di elementi iniziali da creare: hist(double xmax=1., double xmin=0., int bin=100, const char *t="none", const char *at="none"): xaxis_(xmax,xmin,bin,at), count_(bin+2), entries_(0), title_(t) {}; count_(bin+2) è equivalente a: vector <int, (bin+2) > // crea un vector che contiene inizialmente (bin+2) oggetti di tipo int F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 289 Esercizio Al solito con getters e setters avremo: // Getters int GetEntries() const { return entries_; } int GetNbin() const { return xaxis_.GetBin(); } double GetXmax() const { return xaxis_.GetMax(); } double GetXmin() const { return xaxis_.GetMin(); } const char * GetTitle() const { return title_.c_str(); } const char * GetATitle() const { return xaxis_.GetTitle(); } #ifndef HIST_H #ifndef HIST_H #define HIST_H #include <vector> #include <string> #include "axis.h" using std::vector; using std::string; typedef vector <int> vint; class hist { public: hist(double xmax=1., double xmin=0., int bin=100, const char *t="none", const char *at="none"): xaxis_(xmax,xmin,bin,at), count_(bin+2), entries_(0), title_(t) {}; bool Fill(double x){ int i=xaxis_.Bin(x); if(i<=count_.size()) { count_[i]++; entries_++; return true; } return false; } // Setters void SetNbin( const int i ) { xaxis_.SetBin(i); } void SetXmax( const double i ) { xaxis_.SetMax(i); } void SetXmin( const double i ) { xaxis_.SetMin(i); } void SetTitle( const char *c ) { title_=c; } void SetATitle( const char *c) { xaxis_.SetTitle(c); } private: vint count_; axis xaxis_; int entries_; string title_; }; #endif // HIST_H hist.h F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 290 Esercizio Rimane il problema di stampare il contenuto: void Print( ostream &o = cout) const { o << " Histo : " << title_ << endl; o << " Underflow : " << *count_.begin() << ", Overflow : " << *count_.end() << endl; for( int i=0; i<(count_.size()); PrintBin(i++,o) ); } void PrintBin( const int I, ostream &o) const { o << " Bin : " << i << ", " << xaxis_.GetBinMin(i) << ", " << count_[i] << endl; } Notare la generalizzazione dell’output stream. In questo modo sarà possibile usare un file-stream invece di cout e salvare l’output su file F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 291 Esercizio In questo modo viene stampato il contenuto di ogni bin, ma possiamo pensare ad un modo per ottenere un “grafico” ? Se abbiamo a disposizione MAXCHAR colonne su un terminale ASCII, potremmo pensare di scrivere una stringa lunga L dove: MAXCHAR:L=max_bin_content:bin_content Quindi una volta calcolato il massimo contenuto nei bin, potremo calcolare il fattore di scala: MAXCHAR/max_bin_content E calcolare: L=bin_content*(MAXCHAR/max_bin_content) F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 292 Esercizio Per calcolare il massimo conteggio nel bin, possiamo usare un algoritmo della STL: max_element E’ dichiarato nell’header: algorithm E la sua dichiarazione recita: template <class ForwardIterator> ForwardIterator max_element ( ForwardIterator first, ForwardIterator last ); template <class ForwardIterator, class Compare> ForwardIterator max_element ( ForwardIterator first, ForwardIterator last, Compare comp ); Quindi dati due iteratori, controlla il valore di tutti gli oggetti a cui gli iteratori puntano e ritorna l’iteratore che punta all’oggetto con il contenuto maggiore secondo la classe di comparazione comp F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 293 Esercizio Per cui, nella nostra classe hist potremmo scrivere: int maxele=*(max_element( count_.begin()+1, count_.end()-1)); Una volta calcolato il massimo contenuto nel bin, possiamo calcolare Il fattore di scala e stampare una linea che contiene un numero di caratteri proporzionale al contenuto del bin. Per questo sfruttiamo le proprietà delle stringstream ed il creatore non di default delle string F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 294 Esercizio void Print( ostream & o=cout,) const { int maxele=*(max_element( count_.begin()+1, count_.end()-1)); o << " Histo : " << title_ << endl; o << " Max value : " << maxele << endl; Calcolo o << " Underflow : " << *count_.begin() << ", Overflow : " << *count_.end() << endl; for( int i=0; i<(count_.size()); PrintBin(i++,o) ); for( int i=1; i<(count_.size()-1); PrintBin(i++, o, MAXCHAR/maxele ) ); } il massimo // scale, must be equal to MAXCHAR / MAXELE void PrintBin( const int i, ostream &o=cout, double scale=0 ) const { if(!scale) { o << " Bin : " << i << ", " << xaxis_.GetBinMin(i) << ", " << count_[i] << endl; Calcolo L } else { int j=(int)(count_[i]*scale); Scrivo nella stringa come se stringstream s; s<< setw(6) << xaxis_.GetBinMin(i)<<"|"; fosse uno stream il valore del s<< left << string(j,'-') << "|"; bin o << s.str() << endl; } } Creo una string di L caratteri ‘-’ e la scrivo nella stringstream Modificatori di I/O. setw(int):fissa la largezza di campo 295 F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo left: allinea a sinistra Esercizio Aggiungendo delle opzioni per selezionare il PLOT o il print del BIN content, la class definition diventa: #ifndef HIST_H #define HIST_H #include <vector> #include <string> #include <sstream> #include <algorithm> #include <iostream> #include <iomanip> #include "axis.h" // Getters int GetEntries() const { return entries_; } int GetNbin() const { return xaxis_.GetBin(); } double GetXmax() const { return xaxis_.GetMax(); } double GetXmin() const { return xaxis_.GetMin(); } const char * GetTitle() const { return title_.c_str(); } const char * GetATitle() const { return xaxis_.GetTitle(); } // Setters void SetNbin( const int i ) { xaxis_.SetBin(i); } void SetXmax( const double i ) { xaxis_.SetMax(i); } void SetXmin( const double i ) { xaxis_.SetMin(i); } void SetTitle( const char *c ) { title_=c; } void SetATitle( const char *c) { xaxis_.SetTitle(c); } using std::vector; using std::string; using std::stringstream; using std::right; using std::fixed; using std::endl; using std::cout; using std::left; using std::setw; using std::ostream; void Print( ostream & o=cout, opt op=PLOT ) const; // scale, must be equal to MAXCHAR / MAXELE void PrintBin( const int i, ostream &o=cout, double scale=0. ) const; #define MAXCHAR 60. typedef vector <int> vint; class hist { public: enum opt {ALL,BIN,PLOT}; hist(double xmax=1., double xmin=0., int bin=100, const char *t="none", const char *at="none"): xaxis_(xmax,xmin,bin,at), count_(bin+2), entries_(0), title_(t) {}; private: vint count_; axis xaxis_; int entries_; string title_; }; #endif // HIST_H hist.h bool Fill(double x); F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 296 Esercizio #include "hist.h" void hist::Print( ostream & o, opt op) const { int maxele=*(max_element( count_.begin()+1, count_.end()-1)); o << " Histo : " << title_ << endl; o << " Max value : " << maxele << endl; o << " Underflow : " << *count_.begin() << ", Overflow : " << *count_.end() << endl; switch(op) { case BIN: for( int i=0; i<(count_.size()); PrintBin(i++,o) ); break; case PLOT: for( int i=1; i<(count_.size()-1); PrintBin(i++, o, MAXCHAR/maxele ) ); break; case ALL: for( int i=0; i<(count_.size()); PrintBin(i++,o) ); for( int i=1; i<(count_.size()-1); PrintBin(i++, o, MAXCHAR/maxele ) ); break; } } // scale, must be equal to MAXCHAR / MAXELE void hist::PrintBin( const int i, ostream &o, double scale ) const { if(!scale) { o << " Bin : " << i << ", " << xaxis_.GetBinMin(i) << ", " << count_[i] << endl; } else { int j=(int)(count_[i]*scale); stringstream s; s<< setw(6) << xaxis_.GetBinMin(i)<<"|"; s<< left << string(j,'-') << "|"; o << s.str() << endl; } } bool hist::Fill(double x){ int i=xaxis_.Bin(x); if(i<=count_.size()) { count_[i]++; entries_++; return true; } return false; } hist.cpp F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 297 Esercizio A questo punto non ci resta che usare queste classi #include "hist.h" #include "gaussrnd.h" #include <fstream> using std::ofstream; int main() { hist t(3.,-3., 150, "grandom", "count") ; gaussrnd g(12345); for(int j=0; j<500000; j++, t.Fill(g.Grnd())); ofstream f("test_gauss.dat"); t.Print(); t.Print(f); return 0; } test_hist.cpp F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 298 Esercizio Non dimentichiamo il Makefile: CC=g++ test_hist: hist.o axis.o test_hist.o gaussrnd.o F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 299 Esercizi Provate a: Correggere la classe axis. Che succede se calcolo Bin(min_) ? Viene ritornato il numero di bin corretto ? Provare ad “estendere” l’utilizzo delle classi axis e hist, anche ai double utilizzando il template Provare a scrivere un generatore di numeri random distribuiti secondo una esponenziale −y (hint: y ( x) =− ln( x), p( y )dy = e dy ) Provate a scrivere una classe base rndgen, da cui fare derivare le classi: gaussrnd, exprnd Provate a riscrivere il main in modo che utilizzi solo la classe base dei generatori random e che possiate scegliere il generatore via cin F.S. Cafagna, Linguaggi di programmazione avanzati: C++ , XXIII ciclo 300