source: Sophya/trunk/SophyaLib/BaseTools/srandgen.c@ 3364

Last change on this file since 3364 was 3364, checked in by cmv, 18 years ago

add PoissRandLimit pour tirage sur distrib poisson avec protection, cmv 29/10/2007

File size: 16.6 KB
Line 
1#include "machdefs.h"
2#include <stdlib.h>
3#include <stdio.h>
4#include <string.h>
5#include <math.h>
6#include <sys/time.h>
7#include "srandgen.h"
8#include "smathconst.h"
9
10static double GAU_RANGE=6.;
11
12/*
13++
14 Module Tirages aleatoires (C)
15 Lib LibsUtil
16 include srandgen.h
17--
18*/
19
20/*
21++
22 frand01()
23 tirage aleatoire entre 0 et 1, retourne float
24 drand01()
25 tirage aleatoire entre 0 et 1, retourne double
26 rand01()
27 c'est le defaut: drand01()
28 frandpm1()
29 tirage aleatoire entre -1 et 1, retourne float
30 drandpm1()
31 tirage aleatoire entre -1 et 1, retourne double
32 ranfpm1()
33 c'est le defaut: drandpm1()
34--
35*/
36
37/*! \ingroup BaseTools
38 \fn frand01
39 Returns a random number (float) with flat distribution between 0 ... 1
40*/
41/*! \ingroup BaseTools
42 \fn drand01
43 Returns a random number (double) with flat distribution between 0 ... 1
44*/
45/*! \ingroup BaseTools
46 \fn frandpm1()
47 Returns a random number (float) with flat distribution between -1 ... 1
48*/
49/*! \ingroup BaseTools
50 \fn drandpm1()
51 Returns a random number (double) with flat distribution between -1 ... 1
52*/
53
54/*=========================================================================*/
55/*
56++
57 void Ini_Ranf_Quick(long seed_val, int lp)
58 Initialisation rapide du generateur (drand48) par un entier
59 de 32 bits de type long (cf srand48).
60--
61*/
62
63/*! \ingroup BaseTools
64 Fast initialisation of the random number generator \c (drand48)
65 using a \c long type value (cf \c srand48 )
66*/
67void Ini_Ranf_Quick(long seed_val, int lp)
68{
69if(lp) printf("Ini_Ranf_Quick: %d\n",(int) seed_val);
70srand48(seed_val);
71return;
72}
73
74/*=========================================================================*/
75/*
76++
77 void Ini_Ranf(unsigned short seed_16v[3], int lp)
78 Initialisation complete du generateur (drand48) par
79 48 bits (cf seed48).
80--
81*/
82/*! \ingroup BaseTools
83 complete initialisation of the random number generator \c (drand48)
84 using a \c using 48 bits (cf \c seed48 )
85*/
86void Ini_Ranf(unsigned short seed_16v[3], int lp)
87{
88if(lp) printf("Ini_Ranf: %d %d %d\n"
89 ,seed_16v[0],seed_16v[1],seed_16v[2]);
90seed48(seed_16v);
91return;
92}
93
94/*=========================================================================*/
95/*
96++
97 void Get_Ranf(unsigned short seed_16v[3], int lp)
98 Recuperation de l'etat du generateur (drand48) sur
99 de 48 bits (cf seed48).
100--
101*/
102/*! \ingroup BaseTools
103 Returns the status (48 bits) of the random number generator \c (drand48)
104 (cf \c seed48 )
105*/
106void Get_Ranf(unsigned short seed_16v[3], int lp)
107{
108unsigned short seed[3] = {0,0,0};
109unsigned short *p;
110p = seed48(seed);
111memcpy(seed_16v,p,3*sizeof(unsigned short));
112if(lp) printf("Get_Ranf: %d %d %d\n"
113 ,seed_16v[0],seed_16v[1],seed_16v[2]);
114/* on re-initialise a ce qui etait avant */
115seed48(seed_16v);
116return;
117}
118
119/*=========================================================================*/
120/*
121++
122 void Auto_Ini_Ranf(int lp)
123 Initialisation automatique (pseudo) aleatoire du generateur.
124 L'initialiseur est donne par le nombre de millisecondes
125 ecoulees depuis le dernier jour multiple de 23 du nombre de jours
126 depuis le 0 heure le 1er Janvier 1970 UTC (cf gettimeofday).
127 Pour retomber sur la meme initialisation
128 il faut generer deux aleatoires a moins de 1/1000 seconde
129 ou generer le deuxieme aleatoire 23 jours apres le premier
130 a la meme heure (a 1/1000 de seconde pres). La fonction
131 d'initialisation utilisee est Ini_Ranf_Quick(long).
132--
133*/
134/*! \ingroup BaseTools
135 Automatic initialisation using the present time
136*/
137void Auto_Ini_Ranf(int lp)
138{
139struct timeval now;
140long nj,nj23,seed=0;
141double buf;
142
143gettimeofday (&now,0);
144
145/* dans 32 bits signes on met environ 23 jours a 1/1000 de seconde pres! */
146/* Nombre de jours depuis l'origine */
147nj = (long) now.tv_sec / 86400;
148/* Nombre de jours depuis le dernier jour multiple de 23 jours */
149nj23 = nj % 23;
150/* nombre de secondes depuis le dernier jour multiple de 23 jours */
151buf = (double) (nj23*86400 + (now.tv_sec-nj*86400));
152/* nombre de milliemes de secondes depuis ... */
153buf = buf*1000. + now.tv_usec/1000.;
154seed = (long) buf;
155
156if(lp) {
157 printf("Auto_Ini_Ranf: date %ld s %ld 10^-6 sec seed=%ld:\n"
158 ,(long)now.tv_sec,(long)now.tv_usec,seed);
159 if(lp>1) printf("... njours=%ld nj23=%ld buf=%.20g\n",nj,nj23,buf);
160}
161Ini_Ranf_Quick(seed,lp);
162return;
163}
164
165/*=========================================================================*/
166/*
167++
168 void SetGauRange(double range)
169 Generation de distribution gaussienne:
170 Changement de l'initialisation de l'excursion du tirage
171 en nombre de sigmas (obsolete).
172--
173*/
174void SetGauRange(double range)
175{
176GAU_RANGE = range;
177}
178
179/*=========================================================================*/
180/*
181++
182 float NorRand(void)
183 Generation aleatoire gaussienne normee centree
184--
185*/
186/*! \ingroup BaseTools
187 Normal (Gaussian) random number generator (Mean=0., Sigma=1.)
188*/
189float NorRand(void)
190{
191double x,A,B;
192
193LAB10:
194A = drand01();
195if ( A == 0. ) goto LAB10;
196B = drand01();
197x = sqrt(-2.*log(A))*cos(DeuxPi*B);
198return( (float) x );
199}
200
201/*=========================================================================*/
202/*
203++
204 float NorRand1(void)
205 Generation aleatoire gaussienne normee centree
206 la distribution est limitee entre +/- GAU_RANGE (obsolete).
207--
208*/
209float NorRand1(void)
210{
211double b, x, y, gauss;
212
213b = 1./sqrt(2.*M_PI);
214LAB10:
215x = GAU_RANGE*drandpm1();
216y = drand01()*b;
217gauss = b*exp(-x*x/2.);
218if ( gauss-y < 0. ) goto LAB10 ;
219return( (float) x );
220}
221
222/*=========================================================================*/
223/*
224++
225 double GauRnd(double am, double s)
226 Generation aleatoire gaussienne de centre "am" et de sigma "s".
227--
228*/
229/*! \ingroup BaseTools
230 Normal (Gaussian) random number generator with the specified mean
231 (\c am ) and sigma (\c s )
232*/
233double GauRnd(double am, double s)
234{
235double x,A,B;
236
237LAB10:
238A = drand01();
239if ( A == 0. ) goto LAB10;
240B = drand01();
241x = am + s * sqrt(-2.*log(A))*cos(DeuxPi*B);
242return(x);
243}
244
245/*! \ingroup BaseTools
246 \brief Poisson random number generator.
247
248 Return an integer value (>=0) corresponding a Poisson distribution with mean \b mu.
249 \warning NOT the most efficient way of generating a large series of numbers
250 with the SAME mean
251*/
252int PoissRand(double mu)
253{
254 double pp,ppi,x;
255 int n;
256 ppi = pp = exp(-mu);
257 x = drand01();
258 n = 0;
259 while (x > ppi) {
260 n++;
261 pp = mu*pp/(double)n;
262 ppi += pp;
263 }
264 return n;
265}
266
267/*! \ingroup BaseTools
268 \brief Poisson random number generator with approximation for large mean.
269
270 Return an integer value (>=0) corresponding a Poisson distribution with mean \b mu.
271 If \b mu is greater or equal than \b mumax,
272 the large number gaussian approximation is applied.
273 \b mumax=10 is a good value.
274*/
275unsigned long PoissRandLimit(double mu,double mumax)
276// p(n) = (mu^n / n!) exp(-mu) for n = 0, 1, 2, ...
277{
278 double pp,ppi,x;
279 unsigned long n;
280
281 if(mu>=mumax) {
282 pp = sqrt(mu);
283 while( (x=pp*NorRand()) < -mu );
284 return (unsigned long)(mu+x+0.5);
285 }
286
287 ppi = pp = exp(-mu);
288 x = drand01();
289 n = 0;
290 while (x > ppi) {
291 n++;
292 pp = mu*pp/(double)n;
293 ppi += pp;
294 }
295 return n;
296}
297
298/*=========================================================================*/
299/*
300++
301 double GauRnd1(double am, double s)
302 Generation aleatoire gaussienne de centre "am" et de
303 sigma "s" la distribution est limitee entre am +/- GAU_RANGE (obsolete).
304--
305*/
306/*! \ingroup BaseTools
307 \brief OBSOLETE (gaussian random number generator)
308*/
309double GauRnd1(double am, double s)
310{
311double s2, b, x, y, gauss;
312
313s2 = 2.*s*s;
314b = 1./sqrt(2.*M_PI*s);
315LAB10:
316x = am + GAU_RANGE*s*drandpm1();
317y = drand01()*b;
318gauss = b*exp(-(x-am)*(x-am)/s2);
319if ( gauss-y < 0. ) goto LAB10 ;
320return(x);
321}
322
323/*==========================================================================*/
324/*
325++
326 void NormGau(double *x,double *y,double mx,double my,double sa,double sb,double teta);
327 Tirage de 2 nombres aleatoires x et y distribues sur une gaussienne 2D
328 de centre (x=mx,y=my), de sigmas grand axe et petit axe (sa,sb)
329 et dont le grand axe fait un angle teta (radian) avec l'axe des x.
330--
331*/
332/*
333++
334| - La densite de probabilite (normalisee a 1) sur laquelle on tire est:
335| N*exp[-0.5*{ (A/sa)**2+(C/sc)**2 }], N=1/(2Pi*sa*sc)
336| ou A et B sont les coordonnees selon le grand axe et le petit axe
337| et teta = angle(x,A), le resultat subit ensuite une rotation d'angle teta.
338| - La matrice des covariances C des variables A,B est:
339| | sa^2 0 |
340| | | et det(C) = (1-ro^2)*sa^2*sb^2
341| | 0 sb^2 |
342| - La distribution x,y resultante est:
343| N*exp[-0.5*{[(dx/sx)^2-2*ro/(sx*sy)*dx*dy+(dy/sy)^2]/(1-ro^2)}]
344| ou N est donne dans NormCo et sx,sy,ro sont calcules a partir
345| de sa,sc,teta (voir fonctions paramga ou gaparam). La matrice des
346| covariances des variables x,y est donnee dans la fonction NormCo.
347--
348*/
349void NormGau(double *x,double *y
350 ,double mx,double my,double sa,double sb,double teta)
351{
352double c,s,X,Y;
353
354LAB10:
355 s = drand01();
356 if ( s == 0. ) goto LAB10;
357s = sqrt(-2.*log(s));
358c = DeuxPi * drand01();
359
360X = sa*s*cos(c);
361Y = sb*s*sin(c);
362
363c = cos(teta); s = sin(teta);
364*x = mx + c*X - s*Y;
365*y = my + s*X + c*Y;
366}
367
368/*==========================================================================*/
369/*
370++
371 int NormCo(double *x,double *y,double mx,double my,double sx,double sy,double ro)
372 Tirage de 2 nombres aleatoires x et y distribues sur une gaussienne 2D
373 de centre (mx,my), de coefficient de correlation rho (ro) et telle que
374 les sigmas finals des variables x et y soient sx,sy (ce sont
375 les valeurs des distributions marginales des variables aleatoires x et y
376 c'est a dire les sigmas des projections x et y de l'histogramme 2D
377 de la gaussienne). Retourne 0 si ok.
378--
379*/
380/*
381++
382| - La densite de probabilite (normalisee a 1) sur laquelle on tire est:
383| N*exp[-0.5*{[(dx/sx)^2-2*ro/(sx*sy)*dx*dy+(dy/sy)^2]/(1-ro^2)}]
384| avec dx = x-mx, dy = y-my et N = 1/[2Pi*sx*sy*sqrt(1-ro^2)]
385| - Dans ce cas la distribution marginale est (ex en X):
386| 1/(sqrt(2Pi)*sx) * exp[-0.5*{dx^2/sx^2}]
387| - La matrice des covariances C des variables x,y est:
388| | sx^2 ro*sx*sy |
389| | | et det(C) = (1-ro^2)*sx^2*sy^2
390| | ro*sx*sy sy^2 |
391| - La matrice inverse C^(-1) est:
392| | 1/sx^2 -ro/(sx*sy) |
393| | | * 1/(1-ro^2)
394| | -ro/(sx*sy) 1/sy^2 |
395--
396*/
397/*
398++
399| - Remarque:
400| le sigma que l'on obtient quand on fait une coupe de la gaussienne 2D
401| en y=0 (ou x=0) est: SX0(y=0) = sx*sqrt(1-ro^2) different de sx
402| SY0(x=0) = sy*sqrt(1-ro^2) different de sy
403| La distribution qui correspond a des sigmas SX0,SY0
404| pour les coupes en y=0,x=0 de la gaussienne 2D serait:
405| N*exp[-0.5*{ (dx/SX0)^2-2*ro/(SX0*SY0)*dx*dy+(dy/SY0)^2 }]
406| avec N = sqrt(1-ro^2)/(2Pi*SX0*SY0) et les variances
407| des variables x,y sont toujours
408| sx=SX0/sqrt(1-ro^2), sy=SY0/sqrt(1-ro^2)
409--
410*/
411int NormCo(double *x,double *y
412 ,double mx,double my,double sx,double sy,double ro)
413{
414double a,b,sa;
415if( ro <= -1. || ro >= 1. ) return(1);
416LAB10:
417 b = drand01();
418 if ( b == 0. ) goto LAB10;
419b = sqrt(-2.*log(b));
420a = DeuxPi * drand01();
421sa = sin(a);
422
423*x = mx + sx*b*(sqrt(1.-ro*ro)*cos(a)+ro*sa);
424*y = my + sy*b*sa;
425
426return(0);
427}
428
429
430/*!
431 \ingroup BaseTools
432 \class SOPHYA::RandomGenerator
433 \brief Random number generator
434
435 This is a class with static methods, providing an alternative interface
436 to random number generations functions declared in srandgen.h.
437
438 \sa frand01 drand01 frandpm1 drandpm1
439 \sa GauRnd PoissRand
440 \sa Ini_Ranf_Quick Ini_Ranf Get_Ranf Auto_Ini_Ranf
441*/
442
443/*==========================================================================*/
444/*
445++
446 Titre Exemple d'utilisation des aleatoires avec initialisation.
447--
448*/
449/*
450++
451| #include "srandgen.h"
452|
453| void main() {
454| long i,ini=123456789;
455| unsigned short seed[3];
456|
457| printf(" 1./ ==> test nitialisation par un long\n");
458| Ini_Ranf_Quick(ini,1);
459| for(i=0;i<10;i++) printf("%d -> %f\n",i,ranf01());
460--
461*/
462/*
463++
464|
465| printf("\n 2./ ==> test initialisation par tableau de 3 unsigned short\n");
466| Ini_Ranf_Quick(ini,1);
467| for(i=0;i<5;i++) printf("%d -> %f\n",i,ranf01());
468| Get_Ranf(seed,1);
469| for(i=5;i<10;i++) printf("%d -> %f\n",i,ranf01());
470| Ini_Ranf(seed,1);
471| for(i=5;i<10;i++) printf("%d -> %f\n",i,ranf01());
472| Get_Ranf(seed,1);
473--
474*/
475/*
476++
477|
478| printf("\n 3./ ==> test initialisation automatique\n");
479| Auto_Ini_Ranf(2);
480| for(i=0;i<5;i++) printf("%d -> %f\n",i,ranf01());
481| i=0; while(i<10000000) i++;
482| Auto_Ini_Ranf(2);
483| for(i=0;i<5;i++) printf("%d -> %f\n",i,ranf01());
484| i=0; while(i<10000000) i++;
485| Auto_Ini_Ranf(2);
486| for(i=0;i<5;i++) printf("%d -> %f\n",i,ranf01());
487| }
488--
489*/
490/*
491++
492| 1./ ==> test initialisation par un long
493| Ini_Ranf_Quick: 123456789
494| 0 -> 0.052468
495| 1 -> 0.025444
496| 2 -> 0.099272
497| 3 -> 0.436130
498| 4 -> 0.327740
499| 5 -> 0.821202
500| 6 -> 0.560493
501| 7 -> 0.018157
502| 8 -> 0.872758
503| 9 -> 0.652496
504--
505*/
506/*
507++
508|
509| 2./ ==> test initialisation par tableau de 3 unsigned short
510| Ini_Ranf_Quick: 123456789
511| 0 -> 0.052468
512| 1 -> 0.025444
513| 2 -> 0.099272
514| 3 -> 0.436130
515| 4 -> 0.327740
516--
517*/
518/*
519++
520| Get_Ranf: 36117 51106 21478
521| 5 -> 0.821202
522| 6 -> 0.560493
523| 7 -> 0.018157
524| 8 -> 0.872758
525| 9 -> 0.652496
526--
527*/
528/*
529++
530| Ini_Ranf: 36117 51106 21478
531| 5 -> 0.821202
532| 6 -> 0.560493
533| 7 -> 0.018157
534| 8 -> 0.872758
535| 9 -> 0.652496
536| Get_Ranf: 16576 62373 42761
537--
538*/
539/*
540++
541|
542| 3./ ==> test initialisation automatique
543| Auto_Ini_Ranf: date 887117206 s 868138 10^-6 sec seed=826006868:
544| ... njours=10267 nj23=9 buf=826006868.13800001
545| Ini_Ranf_Quick: 826006868
546| 0 -> 0.798860
547| 1 -> 0.342478
548| 2 -> 0.401300
549| 3 -> 0.442912
550| 4 -> 0.170912
551--
552*/
553/*
554++
555| Auto_Ini_Ranf: date 887117207 s 188779 10^-6 sec seed=826007188:
556| ... njours=10267 nj23=9 buf=826007188.77900004
557| Ini_Ranf_Quick: 826007188
558| 0 -> 0.455599
559| 1 -> 0.811427
560| 2 -> 0.703880
561| 3 -> 0.409569
562| 4 -> 0.390399
563--
564*/
565/*
566++
567| Auto_Ini_Ranf: date 887117207 s 489750 10^-6 sec seed=826007489:
568| ... njours=10267 nj23=9 buf=826007489.75
569| Ini_Ranf_Quick: 826007489
570| 0 -> 0.567094
571| 1 -> 0.893156
572| 2 -> 0.975995
573| 3 -> 0.531331
574| 4 -> 0.834354
575--
576*/
577/*==========================================================================*/
578
579/*==========================================================================*/
580/*
581++
582 Module Tirages aleatoires selon une fonction (C)
583 Lib LibsUtil
584 include srandgen.h
585--
586*/
587/*
588++
589 TIREALEA *init_tirage_alea(int nbin,double xmin,double xmax,double (*fonc) (double))
590 Initialise la structure qui va permettre le tirage aleatoire
591 d'un nombre compris entre xmin et xmax selon la
592 distribution fonc (histo de nbin bins)
593--
594*/
595TIREALEA *init_tirage_alea(int nbin,double xmin,double xmax,double (*fonc) (double))
596{
597int sof,i;
598double x;
599struct tirage_alea *t;
600
601if ( xmax-xmin<0.) return(NULL);
602
603if(nbin<=3) nbin=50;
604
605sof = sizeof(struct tirage_alea);
606if( (t = (struct tirage_alea*)malloc(sof) ) == NULL ) {
607 printf("impossible d'allouer *tirage_alea par malloc \n");
608 return(NULL);
609}
610
611t->Nbin=nbin; t->Min=xmin; t->Max=xmax; t->Lbin=(xmax-xmin) /nbin;
612
613sof = nbin * sizeof(double);
614if( (t->Tab = (double*)malloc(sof) ) == NULL ) {
615 printf("impossible d'allouer *tirage_alea.Tab par malloc \n");
616 return(NULL);
617}
618
619x = xmin + .5*t->Lbin;
620t->Tab[0] = fonc(x);
621for(i=1;i<nbin;i++) {
622 x = xmin + (i+.5)*t->Lbin;
623 t->Tab[i] = t->Tab[i-1] + fonc(x);
624}
625
626for(i=0;i<nbin-1;i++) t->Tab[i] /= t->Tab[nbin-1];
627t->Tab[nbin-1] = 1.;
628
629return(t);
630}
631
632/*==========================================================================*/
633/*
634++
635 double tirage_alea( TIREALEA *alea )
636 tirage aleatoire d'un nombre compris entre xmin et xmax
637 selon la fonction fonc (cf init_tirage_alea).
638--
639*/
640double tirage_alea( TIREALEA *alea )
641{
642int i,ibin = -1;
643double z,t1,t2,x1,x2,t;
644
645z=drand01();
646/* protections z<=0 ou z>=1 */
647if( z <= 0. ) return ( alea->Min );
648if( z >= 1. ) return ( alea->Max );
649/* cas z <= tab[0] */
650if(z <= alea->Tab[0]) {
651 t = alea->Min + (alea->Lbin/2.)/alea->Tab[0] * z;
652 return (t);
653}
654
655/* recherche du premier bin plus grand que z */
656for(i=0;i<alea->Nbin;i++) {
657 ibin=i;
658 if ( z < alea->Tab[i] ) break;
659}
660
661/* extrapolation pour trouver la valeur du tirage aleatoire */
662if( ibin == alea->Nbin-1 ) ibin--;
663t1=alea->Tab[ibin];
664x1 = alea->Min + (ibin+0.5) * alea->Lbin;
665t2=alea->Tab[ibin+1];
666x2 = x1 + alea->Lbin;
667t = x1 + (x2-x1)/(t2-t1) *(z-t1);
668if ( t < alea->Min ) t = alea->Min;
669if ( t > alea->Max ) t = alea->Max;
670return(t);
671}
672
673/*==========================================================================*/
674/*
675++
676 int end_tirage_alea( TIREALEA *alea )
677 De-allocation de la structure qui a permis le tirage aleatoire.
678--
679*/
680int end_tirage_alea( TIREALEA *alea )
681{
682if ( alea != NULL ) { free(alea); return(0);}
683 else return(-1);
684}
Note: See TracBrowser for help on using the repository browser.