Geant4 11.1.1
Toolkit for the simulation of the passage of particles through matter
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
RandBinomial.cc
Go to the documentation of this file.
1// -*- C++ -*-
2//
3// -----------------------------------------------------------------------
4// HEP Random
5// --- RandBinomial ---
6// class implementation file
7// -----------------------------------------------------------------------
8
9// =======================================================================
10// John Marraffino - Created: 12th May 1998
11// M Fischler - put and get to/from streams 12/10/04
12// M Fischler - put/get to/from streams uses pairs of ulongs when
13// + storing doubles avoid problems with precision
14// 4/14/05
15//
16// =======================================================================
17
21#include <algorithm> // for min() and max()
22#include <cmath> // for exp()
23#include <iostream>
24#include <vector>
25
26namespace CLHEP {
27
28std::string RandBinomial::name() const {return "RandBinomial";}
29HepRandomEngine & RandBinomial::engine() {return *localEngine;}
30
32}
33
34double RandBinomial::shoot( HepRandomEngine *anEngine, long n,
35 double p ) {
36 return genBinomial( anEngine, n, p );
37}
38
39double RandBinomial::shoot( long n, double p ) {
41 return genBinomial( anEngine, n, p );
42}
43
44double RandBinomial::fire( long n, double p ) {
45 return genBinomial( localEngine.get(), n, p );
46}
47
48void RandBinomial::shootArray( const int size, double* vect,
49 long n, double p )
50{
51 for( double* v = vect; v != vect+size; ++v )
52 *v = shoot(n,p);
53}
54
56 const int size, double* vect,
57 long n, double p )
58{
59 for( double* v = vect; v != vect+size; ++v )
60 *v = shoot(anEngine,n,p);
61}
62
63void RandBinomial::fireArray( const int size, double* vect)
64{
65 for( double* v = vect; v != vect+size; ++v )
66 *v = fire(defaultN,defaultP);
67}
68
69void RandBinomial::fireArray( const int size, double* vect,
70 long n, double p )
71{
72 for( double* v = vect; v != vect+size; ++v )
73 *v = fire(n,p);
74}
75
76/*************************************************************************
77 * *
78 * StirlingCorrection() *
79 * *
80 * Correction term of the Stirling approximation for log(k!) *
81 * (series in 1/k, or table values for small k) *
82 * with long int parameter k *
83 * *
84 *************************************************************************
85 * *
86 * log k! = (k + 1/2)log(k + 1) - (k + 1) + (1/2)log(2Pi) + *
87 * StirlingCorrection(k + 1) *
88 * *
89 * log k! = (k + 1/2)log(k) - k + (1/2)log(2Pi) + *
90 * StirlingCorrection(k) *
91 * *
92 *************************************************************************/
93
94static double StirlingCorrection(long int k)
95{
96 #define C1 8.33333333333333333e-02 // +1/12
97 #define C3 -2.77777777777777778e-03 // -1/360
98 #define C5 7.93650793650793651e-04 // +1/1260
99 #define C7 -5.95238095238095238e-04 // -1/1680
100
101 static const double c[31] = { 0.0,
102 8.106146679532726e-02, 4.134069595540929e-02,
103 2.767792568499834e-02, 2.079067210376509e-02,
104 1.664469118982119e-02, 1.387612882307075e-02,
105 1.189670994589177e-02, 1.041126526197209e-02,
106 9.255462182712733e-03, 8.330563433362871e-03,
107 7.573675487951841e-03, 6.942840107209530e-03,
108 6.408994188004207e-03, 5.951370112758848e-03,
109 5.554733551962801e-03, 5.207655919609640e-03,
110 4.901395948434738e-03, 4.629153749334029e-03,
111 4.385560249232324e-03, 4.166319691996922e-03,
112 3.967954218640860e-03, 3.787618068444430e-03,
113 3.622960224683090e-03, 3.472021382978770e-03,
114 3.333155636728090e-03, 3.204970228055040e-03,
115 3.086278682608780e-03, 2.976063983550410e-03,
116 2.873449362352470e-03, 2.777674929752690e-03,
117 };
118 double r, rr;
119
120 if (k > 30L) {
121 r = 1.0 / (double) k; rr = r * r;
122 return(r*(C1 + rr*(C3 + rr*(C5 + rr*C7))));
123 }
124 else return(c[k]);
125}
126
127double RandBinomial::genBinomial( HepRandomEngine *anEngine, long n, double p )
128{
129/******************************************************************
130 * *
131 * Binomial-Distribution - Acceptance Rejection/Inversion *
132 * *
133 ******************************************************************
134 * *
135 * Acceptance Rejection method combined with Inversion for *
136 * generating Binomial random numbers with parameters *
137 * n (number of trials) and p (probability of success). *
138 * For min(n*p,n*(1-p)) < 10 the Inversion method is applied: *
139 * The random numbers are generated via sequential search, *
140 * starting at the lowest index k=0. The cumulative probabilities *
141 * are avoided by using the technique of chop-down. *
142 * For min(n*p,n*(1-p)) >= 10 Acceptance Rejection is used: *
143 * The algorithm is based on a hat-function which is uniform in *
144 * the centre region and exponential in the tails. *
145 * A triangular immediate acceptance region in the centre speeds *
146 * up the generation of binomial variates. *
147 * If candidate k is near the mode, f(k) is computed recursively *
148 * starting at the mode m. *
149 * The acceptance test by Stirling's formula is modified *
150 * according to W. Hoermann (1992): The generation of binomial *
151 * random variates, to appear in J. Statist. Comput. Simul. *
152 * If p < .5 the algorithm is applied to parameters n, p. *
153 * Otherwise p is replaced by 1-p, and k is replaced by n - k. *
154 * *
155 ******************************************************************
156 * *
157 * FUNCTION: - btpec samples a random number from the binomial *
158 * distribution with parameters n and p and is *
159 * valid for n*min(p,1-p) > 0. *
160 * REFERENCE: - V. Kachitvichyanukul, B.W. Schmeiser (1988): *
161 * Binomial random variate generation, *
162 * Communications of the ACM 31, 216-222. *
163 * SUBPROGRAMS: - StirlingCorrection() *
164 * ... Correction term of the Stirling *
165 * approximation for log(k!) *
166 * (series in 1/k or table values *
167 * for small k) with long int k *
168 * - anEngine ... Pointer to a (0,1)-Uniform *
169 * engine *
170 * *
171 * Implemented by H. Zechner and P. Busswald, September 1992 *
172 ******************************************************************/
173
174#define C1_3 0.33333333333333333
175#define C5_8 0.62500000000000000
176#define C1_6 0.16666666666666667
177#define DMAX_KM 20L
178
179 static CLHEP_THREAD_LOCAL long int n_last = -1L, n_prev = -1L;
180 static CLHEP_THREAD_LOCAL double par,np,p0,q,p_last = -1.0, p_prev = -1.0;
181 static CLHEP_THREAD_LOCAL long b,m,nm;
182 static CLHEP_THREAD_LOCAL double pq, rc, ss, xm, xl, xr, ll, lr, c,
183 p1, p2, p3, p4, ch;
184
185 long bh,i, K, Km, nK;
186 double f, rm, U, V, X, T, E;
187
188 if (n != n_last || p != p_last) // set-up
189 {
190 n_last = n;
191 p_last = p;
192 par=std::min(p,1.0-p);
193 q=1.0-par;
194 np = n*par;
195
196// Check for invalid input values
197
198 if( np <= 0.0 ) return (-1.0);
199
200 rm = np + par;
201 m = (long int) rm; // mode, integer
202 if (np<10)
203 {
204 p0=std::exp(n*std::log(q)); // Chop-down
205 bh=(long int)(np+10.0*std::sqrt(np*q));
206 b=std::min(n,bh);
207 }
208 else
209 {
210 rc = (n + 1.0) * (pq = par / q); // recurr. relat.
211 ss = np * q; // variance
212 i = (long int) (2.195*std::sqrt(ss) - 4.6*q); // i = p1 - 0.5
213 xm = m + 0.5;
214 xl = (double) (m - i); // limit left
215 xr = (double) (m + i + 1L); // limit right
216 f = (rm - xl) / (rm - xl*par); ll = f * (1.0 + 0.5*f);
217 f = (xr - rm) / (xr * q); lr = f * (1.0 + 0.5*f);
218 c = 0.134 + 20.5/(15.3 + (double) m); // parallelogram
219 // height
220 p1 = i + 0.5;
221 p2 = p1 * (1.0 + c + c); // probabilities
222 p3 = p2 + c/ll; // of regions 1-4
223 p4 = p3 + c/lr;
224 }
225 }
226 if( np <= 0.0 ) return (-1.0);
227 if (np<10) //Inversion Chop-down
228 {
229 double pk;
230
231 K=0;
232 pk=p0;
233 U=anEngine->flat();
234 while (U>pk)
235 {
236 ++K;
237 if (K>b)
238 {
239 U=anEngine->flat();
240 K=0;
241 pk=p0;
242 }
243 else
244 {
245 U-=pk;
246 pk=(double)(((n-K+1)*par*pk)/(K*q));
247 }
248 }
249 return ((p>0.5) ? (double)(n-K):(double)K);
250 }
251
252 for (;;)
253 {
254 V = anEngine->flat();
255 if ((U = anEngine->flat() * p4) <= p1) // triangular region
256 {
257 K=(long int) (xm - U + p1*V);
258 return ((p>0.5) ? (double)(n-K):(double)K); // immediate accept
259 }
260 if (U <= p2) // parallelogram
261 {
262 X = xl + (U - p1)/c;
263 if ((V = V*c + 1.0 - std::fabs(xm - X)/p1) >= 1.0) continue;
264 K = (long int) X;
265 }
266 else if (U <= p3) // left tail
267 {
268 if ((X = xl + std::log(V)/ll) < 0.0) continue;
269 K = (long int) X;
270 V *= (U - p2) * ll;
271 }
272 else // right tail
273 {
274 if ((K = (long int) (xr - std::log(V)/lr)) > n) continue;
275 V *= (U - p3) * lr;
276 }
277
278 // acceptance test : two cases, depending on |K - m|
279 if ((Km = std::labs(K - m)) <= DMAX_KM || Km + Km + 2L >= ss)
280 {
281
282 // computation of p(K) via recurrence relationship from the mode
283 f = 1.0; // f(m)
284 if (m < K)
285 {
286 for (i = m; i < K; )
287 {
288 if ((f *= (rc / ++i - pq)) < V) break; // multiply f
289 }
290 }
291 else
292 {
293 for (i = K; i < m; )
294 {
295 if ((V *= (rc / ++i - pq)) > f) break; // multiply V
296 }
297 }
298 if (V <= f) break; // acceptance test
299 }
300 else
301 {
302
303 // lower and upper squeeze tests, based on lower bounds for log p(K)
304 V = std::log(V);
305 T = - Km * Km / (ss + ss);
306 E = (Km / ss) * ((Km * (Km * C1_3 + C5_8) + C1_6) / ss + 0.5);
307 if (V <= T - E) break;
308 if (V <= T + E)
309 {
310 if (n != n_prev || par != p_prev)
311 {
312 n_prev = n;
313 p_prev = par;
314
315 nm = n - m + 1L;
316 ch = xm * std::log((m + 1.0)/(pq * nm)) +
317 StirlingCorrection(m + 1L) + StirlingCorrection(nm);
318 }
319 nK = n - K + 1L;
320
321 // computation of log f(K) via Stirling's formula
322 // final acceptance-rejection test
323 if (V <= ch + (n + 1.0)*std::log((double) nm / (double) nK) +
324 (K + 0.5)*std::log(nK * pq / (K + 1.0)) -
325 StirlingCorrection(K + 1L) - StirlingCorrection(nK)) break;
326 }
327 }
328 }
329 return ((p>0.5) ? (double)(n-K):(double)K);
330}
331
332std::ostream & RandBinomial::put ( std::ostream & os ) const {
333 int pr=os.precision(20);
334 std::vector<unsigned long> t(2);
335 os << " " << name() << "\n";
336 os << "Uvec" << "\n";
337 t = DoubConv::dto2longs(defaultP);
338 os << defaultN << " " << defaultP << " " << t[0] << " " << t[1] << "\n";
339 os.precision(pr);
340 return os;
341}
342
343std::istream & RandBinomial::get ( std::istream & is ) {
344 std::string inName;
345 is >> inName;
346 if (inName != name()) {
347 is.clear(std::ios::badbit | is.rdstate());
348 std::cerr << "Mismatch when expecting to read state of a "
349 << name() << " distribution\n"
350 << "Name found was " << inName
351 << "\nistream is left in the badbit state\n";
352 return is;
353 }
354 if (possibleKeywordInput(is, "Uvec", defaultN)) {
355 std::vector<unsigned long> t(2);
356 is >> defaultN >> defaultP;
357 is >> t[0] >> t[1]; defaultP = DoubConv::longs2double(t);
358 return is;
359 }
360 // is >> defaultN encompassed by possibleKeywordInput
361 is >> defaultP;
362 return is;
363}
364
365
366} // namespace CLHEP
#define C5_8
#define C5
#define C1
#define C1_6
#define C3
#define C1_3
#define DMAX_KM
#define C7
static double longs2double(const std::vector< unsigned long > &v)
Definition: DoubConv.cc:110
static std::vector< unsigned long > dto2longs(double d)
Definition: DoubConv.cc:94
static HepRandomEngine * getTheEngine()
Definition: Random.cc:268
std::string name() const
Definition: RandBinomial.cc:28
HepRandomEngine & engine()
Definition: RandBinomial.cc:29
static double shoot()
void fireArray(const int size, double *vect)
Definition: RandBinomial.cc:63
static void shootArray(const int size, double *vect, long n=1, double p=0.5)
Definition: RandBinomial.cc:48
virtual ~RandBinomial()
Definition: RandBinomial.cc:31
std::ostream & put(std::ostream &os) const
std::istream & get(std::istream &is)
Definition: DoubConv.h:17
bool possibleKeywordInput(IS &is, const std::string &key, T &t)
Definition: RandomEngine.h:166
#define CLHEP_THREAD_LOCAL
Definition: thread_local.h:13