/************************************************************************** * file pruec * **************************************************************************/ #include extern double drand(unsigned long *seed); /****************************************************************** * * * Poisson Distribution - Ratio of Uniforms/Inversion * * * ****************************************************************** * * * For parameter my < 5.5 the Inversion method is applied: * * The random numbers are generated via sequential search, * * starting at the lowest index k=0. The cumulative probabilities * * are avoided by using the technique of chop-down. * * For my >= 5.5 Ratio of Uniforms is employed: * * A table mountain hat function h(x) with optimal scale * * parameter s for fixed location parameter a = mu+1/2 is used. * * * ****************************************************************** * * * FUNCTION: - pruec samples a random number from the Poisson * * distribution with parameter my > 0. * * REFERENCE: - E. Stadlober (1989): Sampling from Poisson, * * binomial and hypergeometric distributions: * * ratio of uniforms as a simple and fast * * alternative, Bericht 303, Math. Stat. Sektion, * * Forschungsgesellschaft Joanneum, Graz. * * SUBPROGRAMS: - drand(seed) ... (0,1)-Uniform generator with * * unsigned long integer *seed. * * * * Implemented by R.Kremer 1990, revised by P.Busswald, July 1992 * ******************************************************************/ long pruec(unsigned long *seed, double my) { #define C0 9.18938533204672742e-01 /* constants for */ #define C1 8.33333333333333333e-02 /* Stirling's */ #define C3 -2.77777777777777778e-03 /* approximation */ #define C5 7.93650793650793651e-04 /* of ln(k!) */ #define C7 -5.95238095238095238e-04 static long m,b; static double logfak[30L] = { 0.00000000000000000, 0.00000000000000000, 0.69314718055994531, 1.79175946922805500, 3.17805383034794562, 4.78749174278204599, 6.57925121201010100, 8.52516136106541430, 10.60460290274525023, 12.80182748008146961, 15.10441257307551530, 17.50230784587388584, 19.98721449566188615, 22.55216385312342289, 25.19122118273868150, 27.89927138384089157, 30.67186010608067280, 33.50507345013688888, 36.39544520803305358, 39.33988418719949404, 42.33561646075348503, 45.38013889847690803, 48.47118135183522388, 51.60667556776437357, 54.78472939811231919, 58.00360522298051994, 61.26170176100200198, 64.55753862700633106, 67.88974313718153498, 71.25703896716800901 }; /* ln(k!) for k=0,1,2,...,29 */ static double my_prev = -1.0,p0,a,h,g,q; long k; double u,x,lf,ry,ry2,yk,ym,fk,fm,r; if (my_prev != my) /* Set-up */ { my_prev = my; if (my<5.5) { p0=exp(-my); /* Set-up for Inversion */ b=(long int)(my+10.0*(sqrt(my)+1.0)); /* safety-bound */ } else { a = my + 0.5; /* Set-up for Ratio of Uniforms */ r = sqrt(a + a); b = floor(a + 7.0 * (r + 1.5)); /* safety-bound */ m = my; g = log(my); ym = m; yk = floor(a - r); x = (a - yk - 1.0) / (a - yk); if (my * x * x > yk + 1) yk++; if (yk > 29) /* computing ln(k!) with */ { /* Stirling's Approximation */ ry= 1.0/yk; ry2=ry*ry; fk = (yk + 0.5)*log(yk)-yk+C0+ry*(C1+ry2*(C3+ry2*(C5+ry2*C7))); } else { k = yk; /* ln(k!) out of table logfak */ fk = logfak[k]; /* applied for k<=29 */ } if (ym > 29) /* ln(m!) analogous to ln(k!) */ { ry= 1.0/ym; ry2=ry*ry; fm = (ym + 0.5)*log(ym)-ym+C0+ry*(C1+ry2*(C3+ry2*(C5+ry2*C7))); } else fm = logfak[m]; q = ym * g - fm; h = (a - yk) * exp(0.5 * ((yk - ym) * g + fm - fk) + logfak[2L]); } } if (my<5.5) /* Inversion/Chop-down */ { double pk; pk=p0; k=0; u=drand(seed); while (u>pk) { ++k; if (k>b) { u=drand(seed); k=0; pk=p0; } else { u-=pk; pk*=my/k; } } return(k); } for(;;) /* Ratio of Uniforms */ { do { u = drand(seed); x = a + h * (drand(seed) - 0.5) / u; } while ((x < 0) || (x >= b)); /* check, if x is valid candidate */ k = (long int)(x); yk = k; if (k > 29) { ry=1.0/yk; /* lf - ln(f) */ lf = g * yk - (0.5 + yk) * log(yk) + yk - C0 - C1*ry - q; /* lower bound */ if (lf >= u * (4.0 - u) - 3.0) break; /* quick acceptance */ ry2=ry*ry; lf -=ry*ry2*(C3+ry2*(C5+ry2*C7)); /* more exact ln(f) */ } else { lf = yk * g - logfak[k] - q; /* exact ln(f) using table */ if (lf >= u * (4.0 - u) - 3.0) break; /* to compute ln(k!) */ } if (u * (u - lf) <= 1.0) /* u*(u-lf)>1 - quick rejection */ if (2.0 * log(u) <= lf) break; /* final acceptance */ } return(k); }