SSJ API Documentation
Stochastic Simulation in Java
Loading...
Searching...
No Matches
PoissonDist.java
1/*
2 * Class: PoissonDist
3 * Description: Poisson distribution
4 * Environment: Java
5 * Software: SSJ
6 * Copyright (C) 2001 Pierre L'Ecuyer and Universite de Montreal
7 * Organization: DIRO, Universite de Montreal
8 * @author
9 * @since
10 *
11 *
12 * Licensed under the Apache License, Version 2.0 (the "License");
13 * you may not use this file except in compliance with the License.
14 * You may obtain a copy of the License at
15 *
16 * http://www.apache.org/licenses/LICENSE-2.0
17 *
18 * Unless required by applicable law or agreed to in writing, software
19 * distributed under the License is distributed on an "AS IS" BASIS,
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 * See the License for the specific language governing permissions and
22 * limitations under the License.
23 *
24 */
25package umontreal.ssj.probdist;
26
27import umontreal.ssj.util.*;
28
57
58 private double lambda;
59
63
68 public static double MAXLAMBDA = 100000;
69
73
79 public PoissonDist(double lambda) {
80 setLambda(lambda);
81 }
82
83 public double prob(int x) {
84 if (x < 0)
85 return 0.0;
86 if (pdf == null)
87 return prob(lambda, x);
88 if (x > xmax || x < xmin)
89 return prob(lambda, x);
90 return pdf[x - xmin];
91 }
92
93 public double cdf(int x) {
94 double Sum = 0.0;
95 // int j;
96
97 if (x < 0)
98 return 0.0;
99 if (lambda == 0.0)
100 return 1.0;
101
102 /*
103 * For large lambda, we use the Chi2 distribution according to the exact
104 * relation, with 2x + 2 degrees of freedom
105 *
106 * cdf (lambda, x) = 1 - chiSquare (2x + 2, 2*lambda)
107 *
108 * which equals also 1 - gamma (x + 1, lambda)
109 */
110 if (cdf == null)
111 return GammaDist.barF(x + 1.0, 15, lambda);
112
113 if (x >= xmax)
114 return 1.0;
115
116 if (x < xmin) {
117 // Sum a few terms to get a few decimals far in the lower tail. One
118 // could also call GammaDist.barF instead.
119 final int RMAX = 20;
120 int i;
121 double term = prob(lambda, x);
122 Sum = term;
123 i = x;
124 while (i > 0 && i >= x - RMAX) {
125 term = term * i / lambda;
126 i--;
127 Sum += term;
128 }
129 return Sum;
130 }
131
132 if (x <= xmed)
133 return cdf[x - xmin];
134 else
135 // We keep the complementary distribution in the upper part of cdf
136 return 1.0 - cdf[x + 1 - xmin];
137 }
138
139 public double barF(int x) {
140 /*
141 * poisson (lambda, x) = 1 - cdf (lambda, x - 1)
142 */
143
144 if (x <= 0)
145 return 1.0;
146
147 /*
148 * For large lambda, we use the Chi2 distribution according to the exact
149 * relation, with 2x + 2 degrees of freedom
150 *
151 * cdf (lambda, x) = 1 - ChiSquare.cdf (2x + 2, 2*lambda) cdf (lambda, x) = 1 -
152 * GammaDist.cdf (x + 1, lambda)
153 */
154
155 if (cdf == null)
156 return GammaDist.cdf((double) x, 15, lambda);
157
158 if (x > xmax)
159// return GammaDist.cdf ((double)x, 15, lambda);
160 return PoissonDist.barF(lambda, x);
161 if (x <= xmin)
162 return 1.0;
163 if (x > xmed)
164 // We keep the complementary distribution in the upper part of cdf
165 return cdf[x - xmin];
166 else
167 return 1.0 - cdf[x - 1 - xmin];
168 }
169
170 public int inverseFInt(double u) {
171 if ((cdf == null) || (u <= EPSILON))
172 return inverseF(lambda, u);
173 return super.inverseFInt(u);
174 }
175
176 public double getMean() {
177 return PoissonDist.getMean(lambda);
178 }
179
180 public double getVariance() {
181 return PoissonDist.getVariance(lambda);
182 }
183
184 public double getStandardDeviation() {
185 return PoissonDist.getStandardDeviation(lambda);
186 }
187
197 public static double prob(double lambda, int x) {
198 if (x < 0)
199 return 0.0;
200
201 if (lambda >= 100.0) {
202 if ((double) x >= 10.0 * lambda)
203 return 0.0;
204 } else if (lambda >= 3.0) {
205 if ((double) x >= 100.0 * lambda)
206 return 0.0;
207 } else {
208 if ((double) x >= 200.0 * Math.max(1.0, lambda))
209 return 0.0;
210 }
211
212 final double LAMBDALIM = 20.0;
213 double Res;
214 if (lambda < LAMBDALIM && x <= 100)
215 Res = Math.exp(-lambda) * Math.pow(lambda, x) / Num.factorial(x);
216 else {
217 double y = x * Math.log(lambda) - Num.lnGamma(x + 1.0) - lambda;
218 Res = Math.exp(y);
219 }
220 return Res;
221 }
222
234 public static double cdf(double lambda, int x) {
235 /*
236 * On our machine, computing a value using gamma is faster than the naive
237 * computation for lambdalim > 200.0, slower for lambdalim < 200.0
238 */
239 if (lambda < 0.0)
240 throw new IllegalArgumentException("lambda < 0");
241 if (lambda == 0.0)
242 return 1.0;
243 if (x < 0)
244 return 0.0;
245
246 if (lambda >= 100.0) {
247 if ((double) x >= 10.0 * lambda)
248 return 1.0;
249 } else {
250 if ((double) x >= 100.0 * Math.max(1.0, lambda))
251 return 1.0;
252 }
253
254 /*
255 * If lambda > LAMBDALIM, use the Chi2 distribution according to the exact
256 * relation, with 2x + 2 degrees of freedom
257 *
258 * poisson (lambda, x) = 1 - chiSquare (2x + 2, 2*lambda)
259 *
260 * which also equals 1 - gamma (x + 1, lambda)
261 */
262
263 final double LAMBDALIM = 200.0;
264 if (lambda > LAMBDALIM)
265 return GammaDist.barF(x + 1.0, 15, lambda);
266
267 if (x >= lambda)
268 return 1 - PoissonDist.barF(lambda, x + 1);
269
270 // Naive computation: sum all prob. from i = x
271 double sum = 1;
272 double term = 1;
273 for (int j = 1; j <= x; j++) {
274 term *= lambda / j;
275 sum += term;
276 }
277 return sum * Math.exp(-lambda);
278 }
279
285 public static double barF(double lambda, int x) {
286 if (lambda < 0)
287 throw new IllegalArgumentException("lambda < 0");
288 if (x <= 0)
289 return 1.0;
290
291 if (lambda >= 100.0) {
292 if ((double) x >= 10.0 * lambda)
293 return 0.0;
294 } else {
295 if ((double) x >= 100 + 100.0 * Math.max(1.0, lambda))
296 return 0.0;
297 }
298
299 /*
300 * If lambda > LAMBDALIM, we use the Chi2 distribution according to the exact
301 * relation, with 2x + 2 degrees of freedom
302 *
303 * cdf (lambda, x) = 1 - ChiSquare.cdf (2x + 2, 2*lambda)
304 *
305 * which also equals 1 - GammaDist.cdf (x + 1, lambda)
306 */
307
308 final double LAMBDALIM = 200.0;
309 if (lambda > LAMBDALIM)
310 return GammaDist.cdf((double) x, 15, lambda);
311
312 if (x <= lambda)
313 return 1.0 - PoissonDist.cdf(lambda, x - 1);
314
315 // Naive computation: sum all prob. from i = x to i = oo
316 double term, sum;
317 final int IMAX = 20;
318
319 // Sum at least IMAX prob. terms from i = s to i = oo
320 sum = term = PoissonDist.prob(lambda, x);
321 int i = x + 1;
322 while (term > EPSILON || i <= x + IMAX) {
323 term *= lambda / i;
324 sum += term;
325 i++;
326 }
327 return sum;
328 }
329
334 public static int inverseF(double lambda, double u) {
335 if (u < 0.0 || u > 1.0)
336 throw new IllegalArgumentException("u is not in range [0,1]");
337 if (lambda < 0.0)
338 throw new IllegalArgumentException("lambda < 0");
339 if (u >= 1.0)
340 return Integer.MAX_VALUE;
341 if (u <= prob(lambda, 0))
342 return 0;
343 int i;
344
345 final double LAMBDALIM = 700.0;
346 if (lambda < LAMBDALIM) {
347 double sumprev = -1.0;
348 double term = Math.exp(-lambda);
349 double sum = term;
350 i = 0;
351 while (sum < u && sum > sumprev) {
352 i++;
353 term *= lambda / i;
354 sumprev = sum;
355 sum += term;
356 }
357 return i;
358
359 } else {
360 i = (int) lambda;
361 double term = PoissonDist.prob(lambda, i);
362 while ((term >= u) && (term > Double.MIN_NORMAL)) {
363 i /= 2;
364 term = PoissonDist.prob(lambda, i);
365 }
366 if (term <= Double.MIN_NORMAL) {
367 i *= 2;
368 term = PoissonDist.prob(lambda, i);
369 while (term >= u && (term > Double.MIN_NORMAL)) {
370 term *= i / lambda;
371 i--;
372 }
373 }
374 int mid = i;
375 double sum = term;
376 double termid = term;
377
378 while (term >= EPSILON * u && i > 0) {
379 term *= i / lambda;
380 sum += term;
381 i--;
382 }
383
384 term = termid;
385 i = mid;
386 double prev = -1;
387 if (sum < u) {
388 while ((sum < u) && (sum > prev)) {
389 i++;
390 term *= lambda / i;
391 prev = sum;
392 sum += term;
393 }
394 } else {
395 // The computed CDF is too big so we substract from it.
396 sum -= term;
397 while (sum >= u) {
398 term *= i / lambda;
399 i--;
400 sum -= term;
401 }
402 }
403 }
404 return i;
405 }
406
419 public static double[] getMLE(int[] x, int n) {
420 if (n <= 0)
421 throw new IllegalArgumentException("n <= 0");
422
423 double parameters[];
424 parameters = new double[1];
425 double sum = 0.0;
426 for (int i = 0; i < n; i++) {
427 sum += x[i];
428 }
429
430 parameters[0] = (double) sum / (double) n;
431 return parameters;
432 }
433
442 public static PoissonDist getInstanceFromMLE(int[] x, int n) {
443 double parameters[] = getMLE(x, n);
444 return new PoissonDist(parameters[0]);
445 }
446
453 public static double getMean(double lambda) {
454 if (lambda < 0.0)
455 throw new IllegalArgumentException("lambda < 0");
456
457 return lambda;
458 }
459
467 public static double getVariance(double lambda) {
468 if (lambda < 0.0)
469 throw new IllegalArgumentException("lambda < 0");
470
471 return lambda;
472 }
473
480 public static double getStandardDeviation(double lambda) {
481 if (lambda < 0.0)
482 throw new IllegalArgumentException("lambda < 0");
483
484 return Math.sqrt(lambda);
485 }
486
490 public double getLambda() {
491 return lambda;
492 }
493
497 public void setLambda(double lambda) {
498 supportA = 0;
499 if (lambda < 0.0)
500 throw new IllegalArgumentException("lambda < 0");
501 this.lambda = lambda;
502
503 // For lambda > MAXLAMBDAPOISSON, we do not use pre-computed arrays
504 if (lambda > MAXLAMBDA) {
505 pdf = null;
506 cdf = null;
507 return;
508 }
509
510 double epsilon;
511 int i, mid, Nmax;
512 int imin, imax;
513 double sum;
514 double[] P; // Poisson probability terms
515 double[] F; // Poisson cumulative probabilities
516
517 // In theory, the Poisson distribution has an infinite range. But
518 // for i > Nmax, probabilities should be extremely small.
519 Nmax = (int) (lambda + 16 * (2 + Math.sqrt(lambda)));
520 P = new double[1 + Nmax];
521
522 mid = (int) lambda;
523 epsilon = EPSILON * EPS_EXTRA / prob(lambda, mid);
524 // For large lambda, mass will lose a few digits of precision
525 // We shall normalize by explicitly summing all terms >= epsilon
526 sum = P[mid] = 1.0;
527
528 // Start from the maximum and compute terms > epsilon on each side.
529 i = mid;
530 while (i > 0 && P[i] > epsilon) {
531 P[i - 1] = P[i] * i / lambda;
532 i--;
533 sum += P[i];
534 }
535 xmin = imin = i;
536
537 i = mid;
538 while (P[i] > epsilon) {
539 P[i + 1] = P[i] * lambda / (i + 1);
540 i++;
541 sum += P[i];
542 if (i >= Nmax - 1) {
543 Nmax *= 2;
544 double[] nT = new double[1 + Nmax];
545 System.arraycopy(P, 0, nT, 0, P.length);
546 P = nT;
547 }
548 }
549 xmax = imax = i;
550 F = new double[1 + Nmax];
551
552 // Renormalize the sum of probabilities to 1
553 for (i = imin; i <= imax; i++)
554 P[i] /= sum;
555
556 // Compute the cumulative probabilities until F >= 0.5, and keep them in
557 // the lower part of array, i.e. F[s] contains all P[i] for i <= s
558 F[imin] = P[imin];
559 i = imin;
560 while (i < imax && F[i] < 0.5) {
561 i++;
562 F[i] = P[i] + F[i - 1];
563 }
564 // This is the boundary between F and 1 - F in the CDF
565 xmed = i;
566
567 // Compute the cumulative probabilities of the complementary distribution
568 // and keep them in the upper part of the array. i.e. F[s] contains all
569 // P[i] for i >= s
570 F[imax] = P[imax];
571 i = imax - 1;
572 do {
573 F[i] = P[i] + F[i + 1];
574 i--;
575 } while (i > xmed);
576
577 /*
578 * Reset imin because we lose too much precision for a few terms near imin when
579 * we stop adding terms < epsilon.
580 */
581 i = imin;
582 while (i < xmed && F[i] < EPSILON)
583 i++;
584 xmin = imin = i;
585
586 /* Same thing with imax */
587 i = imax;
588 while (i > xmed && F[i] < EPSILON)
589 i--;
590 xmax = imax = i;
591
592 pdf = new double[imax + 1 - imin];
593 cdf = new double[imax + 1 - imin];
594 System.arraycopy(P, imin, pdf, 0, imax - imin + 1);
595 System.arraycopy(F, imin, cdf, 0, imax - imin + 1);
596 }
597
601 public double[] getParams() {
602 double[] retour = { lambda };
603 return retour;
604 }
605
609 public String toString() {
610 return getClass().getSimpleName() + ": lambda = " + lambda;
611 }
612
613}
Classes implementing discrete distributions over the integers should inherit from this class.
static double EPSILON
Environment variable that determines what probability terms can be considered as negligible when buil...
Extends the class ContinuousDistribution for the gamma distribution tjoh95a  (page 337) with shape pa...
double cdf(double x)
Returns the distribution function .
double barF(double x)
Returns the complementary distribution function.
double cdf(int x)
Returns the distribution function evaluated at (see ( FDistDisc )).
double getStandardDeviation()
Returns the standard deviation of the distribution function.
static double[] getMLE(int[] x, int n)
Estimates the parameter of the Poisson distribution using the maximum likelihood method,...
double barF(int x)
Returns , the complementary distribution function.
static double prob(double lambda, int x)
Computes and returns the Poisson probability for lambda, as defined in ( fmass-Poisson ).
static PoissonDist getInstanceFromMLE(int[] x, int n)
Creates a new instance of a Poisson distribution with parameter.
void setLambda(double lambda)
Sets the associated with this object.
static double barF(double lambda, int x)
Computes and returns the value of the complementary Poisson distribution function,...
static double getStandardDeviation(double lambda)
Computes and returns the standard deviation of the Poisson distribution with parameter .
static double MAXLAMBDA
The value of the parameter above which the tables are not precomputed by the constructor.
double getVariance()
Returns the variance of the distribution function.
static int inverseF(double lambda, double u)
Performs a linear search to get the inverse function without precomputed tables.
static double getVariance(double lambda)
Computes and returns the variance of the Poisson distribution with parameter .
static double cdf(double lambda, int x)
Computes and returns the value of the Poisson distribution function.
double getLambda()
Returns the associated with this object.
double getMean()
Returns the mean of the distribution function.
double prob(int x)
Returns , the probability of .
double[] getParams()
Return a table containing the parameter of the current distribution.
int inverseFInt(double u)
Returns the inverse distribution function , where.
static double getMean(double lambda)
Computes and returns the mean of the Poisson distribution with parameter .
PoissonDist(double lambda)
Creates an object that contains the probability and distribution functions, for the Poisson distribut...
String toString()
Returns a String containing information about the current distribution.
This class provides various constants and methods to compute numerical quantities such as factorials,...
Definition Num.java:35
static double lnGamma(double x)
Returns the natural logarithm of the gamma function evaluated at x.
Definition Num.java:417
static double factorial(int n)
Returns the value of .
Definition Num.java:296