SSJ API Documentation
Stochastic Simulation in Java
Loading...
Searching...
No Matches
BinomialDist.java
1/*
2 * Class: BinomialDist
3 * Description: binomial 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.*;
28import umontreal.ssj.functions.MathFunction;
29
51 private int n;
52 private double p;
53 private double q;
54 private static final double EPS2 = 100.0 * EPSILON;
55
56 private static class Function implements MathFunction {
57 protected int m;
58 protected int R;
59 protected double mean;
60 protected int f[];
61
62 public Function(int m, double mean, int R, int f[]) {
63 this.m = m;
64 this.mean = mean;
65 this.R = R;
66 this.f = new int[f.length];
67 System.arraycopy(f, 0, this.f, 0, f.length);
68 }
69
70 public double evaluate(double x) {
71 if (x < R)
72 return 1e100;
73
74 double sum = 0.0;
75 for (int j = 0; j < R; j++)
76 sum += f[j] / (x - (double) j);
77
78 return (sum + m * Math.log1p(-mean / x));
79 }
80 }
81
85
90 public static double MAXN = 100000;
91
95
103 public BinomialDist(int n, double p) {
104 setBinomial(n, p);
105 }
106
107 public double prob(int x) {
108 if (n == 0)
109 return 1.0;
110
111 if (x < 0 || x > n)
112 return 0.0;
113
114 if (p == 0.0) {
115 if (x > 0)
116 return 0.0;
117 else
118 return 1.0;
119 }
120
121 if (q == 0.0) {
122 if (x < n)
123 return 0.0;
124 else
125 return 1.0;
126 }
127
128 if (pdf == null)
129 return prob(n, p, q, x);
130
131 if (x > xmax || x < xmin)
132 return prob(n, p, q, x);
133
134 return pdf[x - xmin];
135 }
136
137 public double cdf(int x) {
138 if (n == 0)
139 return 1.0;
140
141 if (x < 0)
142 return 0.0;
143
144 if (x >= n)
145 return 1.0;
146
147 if (p == 0.0)
148 return 1.0;
149
150 if (p == 1.0)
151 return 0.0;
152
153 if (cdf != null) {
154 if (x >= xmax)
155 return 1.0;
156 if (x < xmin) {
157 final int RMAX = 20;
158 int i;
159 double term = prob(x);
160 double Sum = term;
161 final double z = (1.0 - p) / p;
162 i = x;
163 while (i > 0 && i >= x - RMAX) {
164 term *= z * i / (n - i + 1);
165 i--;
166 Sum += term;
167 }
168 return Sum;
169 }
170 if (x <= xmed)
171 return cdf[x - xmin];
172 else
173 // We keep the complementary distribution in the upper part of cdf
174 return 1.0 - cdf[x + 1 - xmin];
175 } else
176 return cdf(n, p, x);
177 }
178
179 public double barF(int x) {
180 if (n == 0)
181 return 1.0;
182
183 if (x < 1)
184 return 1.0;
185
186 if (x > n)
187 return 0.0;
188
189 if (p == 0.0)
190 return 0.0;
191
192 if (p == 1.0)
193 return 1.0;
194
195 if (cdf != null) {
196 if (x > xmax) {
197 // Add IMAX dominant terms to get a few decimals in the tail
198 final double q = 1.0 - p;
199 double z, sum, term;
200 int i;
201 sum = term = prob(x);
202 z = p / q;
203 i = x;
204 final int IMAX = 20;
205 while (i < n && i < x + IMAX) {
206 term = term * z * (n - i) / (i + 1);
207 sum += term;
208 i++;
209 }
210 return sum;
211 // return fdist_Beta (x, n - x + 1, 10, p);
212 }
213
214 if (x <= xmin)
215 return 1.0;
216 if (x > xmed)
217 // We keep the complementary distribution in the upper part of cdf
218 return cdf[x - xmin];
219 else
220 return 1.0 - cdf[x - 1 - xmin];
221 } else
222 return 1.0 - cdf(n, p, x - 1);
223 }
224
225 public int inverseFInt(double u) {
226 if ((cdf == null) || (u <= EPS2))
227 return inverseF(n, p, u);
228 else
229 return super.inverseFInt(u);
230 }
231
232 public double getMean() {
233 return BinomialDist.getMean(n, p);
234 }
235
236 public double getVariance() {
237 return BinomialDist.getVariance(n, p);
238 }
239
240 public double getStandardDeviation() {
241 return BinomialDist.getStandardDeviation(n, p);
242 }
243
248 public static double prob(int n, double p, int x) {
249 return prob(n, p, 1.0 - p, x);
250 }
251
263 public static double prob(int n, double p, double q, int x) {
264 final int SLIM = 50; // To avoid overflow
265 final double MAXEXP = (Num.DBL_MAX_EXP - 1) * Num.LN2;// To avoid overflow
266 final double MINEXP = (Num.DBL_MIN_EXP - 1) * Num.LN2;// To avoid underflow
267 int signe = 1;
268 double Res;
269
270 if (n < 0)
271 throw new IllegalArgumentException("n < 0");
272 if (n == 0)
273 return 1.0;
274 if (x < 0 || x > n)
275 return 0.0;
276
277 // Combination (n, x) are symmetric between x and n-x
278 if (x > n / 2) {
279 x = n - x;
280 Res = p;
281 p = q;
282 q = Res;
283 }
284
285 if (p < 0.0) {
286 p = -p;
287 if ((x & 1) != 0)
288 signe *= -1; // odd x
289 }
290 if (q < 0.0) {
291 q = -q;
292 if (((n - x) & 1) != 0)
293 signe *= -1; // odd n - x
294 }
295
296 if (n <= SLIM) {
297 Res = Math.pow(p, (double) x) * Num.combination(n, x) * Math.pow(q, (double) (n - x));
298 return signe * Res;
299 } else {
300 /*
301 * This could be calculated with more precision as there is some cancellation
302 * because of subtraction of the large LnFactorial: the last few digits can be
303 * lost. But we need the function lgammal in long double precision. Another
304 * possibility would be to use an asymptotic expansion for the binomial
305 * coefficient.
306 */
307
308 Res = x * Math.log(p) + (n - x) * Math.log(q) + Num.lnFactorial(n) - Num.lnFactorial(n - x)
309 - Num.lnFactorial(x);
310 if (Res >= MAXEXP)
311 throw new IllegalArgumentException("term overflow");
312
313 if (Res < MINEXP)
314 return 0.0;
315
316 return signe * Math.exp(Res);
317 }
318 }
319
330 public static double cdf(int n, double p, int x) {
331 final int NLIM1 = 10000;
332 final double VARLIM = 50.0;
333// final double EPS = DiscreteDistributionInt.EPSILON * EPS_EXTRA;
334 double y, z, q = 1.0 - p;
335 double sum, term, termmid;
336 int i, mid;
337 boolean flag = false;
338
339 if (p < 0.0 | p > 1.0)
340 throw new IllegalArgumentException("p not in [0,1]");
341 if (n < 0)
342 throw new IllegalArgumentException("n < 0");
343
344 if (n == 0)
345 return 1.0;
346 if (x < 0)
347 return 0.0;
348 if (x >= n)
349 return 1.0;
350 if (p <= 0.0)
351 return 1.0;
352 if (p >= 1.0)
353 return 0.0; // For any x < n
354
355 if (n < NLIM1) { // Exact Binomial
356 /* Sum RMAX terms to get a few decimals in the lower tail */
357 final int RMAX = 20;
358 mid = (int) ((n + 1) * p);
359 if (mid > x)
360 mid = x;
361 sum = term = termmid = prob(n, p, 1.0 - p, mid);
362
363 z = q / p;
364 i = mid;
365 while (term >= EPSILON || i >= mid - RMAX) {
366 term *= z * i / (n - i + 1);
367 sum += term;
368 i--;
369 if (i == 0)
370 break;
371 }
372
373 z = p / q;
374 term = termmid;
375 for (i = mid; i < x; i++) {
376 term *= z * (n - i) / (i + 1);
377 if (term < EPSILON)
378 break;
379 sum += term;
380 }
381 if (sum >= 1.0)
382 return 1.0;
383 return sum;
384
385 } else {
386 if (p > 0.5 || ((p == 0.5) && (x > n / 2))) {
387 // use F (p, n, x) = 1 - F (q, n, n-x-1)
388 p = q;
389 q = 1.0 - p;
390 flag = true;
391 x = n - x - 1;
392 }
393 if (n * p * q > VARLIM) { // Normal approximation
394 /*
395 * Uses the Camp-Paulson approximation based on the F-distribution. Its maximum
396 * absolute error is smaller than 0.007 / sqrt (npq). Ref: W. Molenaar;
397 * Approximations to the Poisson, Binomial,.... QA273.6 M64, p. 93 (1970)
398 */
399 term = Math.pow((x + 1) * q / ((n - x) * p), 1.0 / 3.0);
400 y = term * (9.0 - 1.0 / (x + 1)) - 9.0 + 1.0 / (n - x);
401 z = 3.0 * Math.sqrt(term * term / (x + 1) + 1.0 / (n - x));
402 y /= z;
403 if (flag)
404 return NormalDist.barF01(y);
405 else
406 return NormalDist.cdf01(y);
407 } else { // Poisson approximation
408 /*
409 * Uses a Bol'shev approximation based on the Poisson distribution. Error is O
410 * (1/n^4) as n -> infinity. Ref: W. Molenaar; Approximations to the Poisson,
411 * Binomial,... QA273.6 M64, p. 107, Table 6.2, Formule lambda_9 (1970).
412 */
413 y = (2.0 * n - x) * p / (2.0 - p);
414 double t = 2.0 * n - x;
415 z = (2.0 * y * y - x * y - x * (double) x - 2.0 * x) / (6 * t * t);
416 z = y / (1.0 - z);
417 if (flag)
418 return PoissonDist.barF(z, x - 1);
419 else
420 return PoissonDist.cdf(z, x);
421 }
422 }
423 }
424
430 public static double barF(int n, double p, int x) {
431 return 1.0 - cdf(n, p, x - 1);
432 }
433
440 public static int inverseF(int n, double p, double u) {
441 if (u < 0.0 || u > 1.0)
442 throw new IllegalArgumentException("u not in [0,1]");
443 if (u <= prob(n, p, 0))
444 return 0;
445 if ((u > 1.0 - prob(n, p, n)) || (u >= 1.0))
446 return n;
447 final int NLIM1 = 10000;
448 int i, mid;
449 final double q = 1.0 - p;
450 double z, sum, term, termmid;
451
452 if (n < NLIM1) { // Exact Binomial
453 i = (int) ((n + 1) * p);
454 if (i > n)
455 i = n;
456 term = prob(n, p, i);
457 while ((term >= u) && (term > Double.MIN_NORMAL)) {
458 i /= 2;
459 term = prob(n, p, i);
460 }
461
462 z = q / p;
463 if (term <= Double.MIN_NORMAL) {
464 i *= 2;
465 term = prob(n, p, i);
466 while (term >= u && (term > Double.MIN_NORMAL)) {
467 term *= z * i / (n - i + 1);
468 i--;
469 }
470 }
471
472 mid = i;
473 term = prob(n, p, i);
474 sum = termmid = term;
475
476 z = q / p;
477 for (i = mid; i > 0; i--) {
478 term *= z * i / (n - i + 1);
479 if (term < EPSILON)
480 break;
481 sum += term;
482 }
483
484 i = mid;
485 term = termmid;
486 if (sum >= u) {
487 while (sum >= u) {
488 z = q / p;
489 sum -= term;
490 term *= z * i / (n - i + 1);
491 i--;
492 }
493 return i + 1;
494 }
495
496 double prev = -1;
497 while ((sum < u) && (sum > prev)) {
498 z = p / q;
499 term *= z * (n - i) / (i + 1);
500 prev = sum;
501 sum += term;
502 i++;
503 }
504 return i;
505
506 } else { // Normal or Poisson approximation
507 for (i = 0; i <= n; i++) {
508 sum = cdf(n, p, i);
509 if (sum >= u)
510 break;
511 }
512 return i;
513 }
514 }
515
535 public static double[] getMLE(int[] x, int m) {
536 if (m <= 1)
537 throw new UnsupportedOperationException(" m < 2");
538
539 int i;
540 int r = 0;
541 double mean = 0.0;
542 for (i = 0; i < m; i++) {
543 mean += x[i];
544 if (x[i] > r)
545 r = x[i];
546 }
547 mean /= (double) m;
548
549 double sum = 0.0;
550 for (i = 0; i < m; i++)
551 sum += (x[i] - mean) * (x[i] - mean);
552 double var = sum / m;
553 if (mean <= var)
554 throw new UnsupportedOperationException("mean <= variance");
555
556 int f[] = new int[r];
557 for (int j = 0; j < r; j++) {
558 f[j] = 0;
559 for (i = 0; i < m; i++)
560 if (x[i] > j)
561 f[j]++;
562 }
563
564 double p = 1.0 - var / mean;
565 double rup = (int) (5 * mean / p);
566 if (rup < 1)
567 rup = 1;
568
569 Function fct = new Function(m, mean, r, f);
570 double parameters[] = new double[2];
571 parameters[0] = (int) RootFinder.brentDekker(r - 1, rup, fct, 1e-5);
572 if (parameters[0] < r)
573 parameters[0] = r;
574 parameters[1] = mean / parameters[0];
575
576 return parameters;
577 }
578
587 public static BinomialDist getInstanceFromMLE(int[] x, int m) {
588 double parameters[] = new double[2];
589 parameters = getMLE(x, m);
590 return new BinomialDist((int) parameters[0], parameters[1]);
591 }
592
604 public static double[] getMLE(int[] x, int m, int n) {
605 if (m <= 0)
606 throw new IllegalArgumentException("m <= 0");
607 if (n <= 0)
608 throw new IllegalArgumentException("n <= 0");
609
610 double parameters[] = new double[1];
611 double mean = 0.0;
612 for (int i = 0; i < m; i++)
613 mean += x[i];
614 mean /= (double) m;
615
616 parameters[0] = mean / (double) n;
617 return parameters;
618 }
619
630 public static BinomialDist getInstanceFromMLE(int[] x, int m, int n) {
631 double parameters[] = new double[1];
632 parameters = getMLE(x, m, n);
633 return new BinomialDist(n, parameters[0]);
634 }
635
642 public static double getMean(int n, double p) {
643 if (n <= 0)
644 throw new IllegalArgumentException("n <= 0");
645 if (p < 0.0 || p > 1.0)
646 throw new IllegalArgumentException("p not in range (0, 1)");
647
648 return (n * p);
649 }
650
658 public static double getVariance(int n, double p) {
659 if (n <= 0)
660 throw new IllegalArgumentException("n <= 0");
661 if (p < 0.0 || p > 1.0)
662 throw new IllegalArgumentException("p not in range (0, 1)");
663
664 return (n * p * (1 - p));
665 }
666
673 public static double getStandardDeviation(int n, double p) {
674 return Math.sqrt(BinomialDist.getVariance(n, p));
675 }
676
677 private void setBinomial(int n, double p) {
678 /*
679 * Compute all probability terms of the binomial distribution; start near the
680 * mean, and calculate probabilities on each side until they become smaller than
681 * EPSILON, then stop there. However, this is more general than the binomial
682 * probability distribu- tion as this will compute the binomial terms when p + q
683 * != 1, and even when p or q are negative. However in this case, the cumulative
684 * terms will be meaningless.
685 */
686
687 if (p < 0.0 || p > 1.0)
688 throw new IllegalArgumentException("p not in range (0, 1)");
689 if (n <= 0)
690 throw new IllegalArgumentException("n <= 0");
691 supportA = 0;
692 supportB = n;
693 double q = 1.0 - p;
694 final double EPS = DiscreteDistributionInt.EPSILON * EPS_EXTRA;
695
696 this.n = n;
697 this.p = p;
698 this.q = q;
699
700 int i, mid;
701 int imin, imax;
702 double z = 0.0;
703 double[] P; // Binomial "probability" terms
704 double[] F; // Binomial cumulative "probabilities"
705
706 // For n > MAXN, we shall not use pre-computed arrays
707 if (n > MAXN) {
708 pdf = null;
709 cdf = null;
710 return;
711 }
712
713 P = new double[1 + n];
714 F = new double[1 + n];
715
716 // the maximum term in absolute value
717 mid = (int) ((n + 1) * Math.abs(p) / (Math.abs(p) + Math.abs(q)));
718 if (mid > n)
719 mid = n;
720 P[mid] = prob(n, p, q, mid);
721
722 if (p != 0.0 || p != -0.0)
723 z = q / p;
724 i = mid;
725
726 while (i > 0 && Math.abs(P[i]) > EPS) {
727 P[i - 1] = P[i] * z * i / (n - i + 1);
728 i--;
729 }
730 imin = i;
731
732 if (q != 0.0 || q != -0.0)
733 z = p / q;
734 i = mid;
735
736 while (i < n && Math.abs(P[i]) > EPS) {
737 P[i + 1] = P[i] * z * (n - i) / (i + 1);
738 i++;
739 }
740 imax = i;
741
742 /*
743 * Here, we assume that we are dealing with a probability distribution. Compute
744 * the cumulative probabilities for F and keep them in the lower part of CDF.
745 */
746 F[imin] = P[imin];
747 i = imin;
748 while (i < n && F[i] < 0.5) {
749 i++;
750 F[i] = F[i - 1] + P[i];
751 }
752
753 // This is the boundary between F (i <= xmed) and 1 - F (i > xmed) in
754 // the array CDF
755 xmed = i;
756
757 // Compute the cumulative probabilities of the complementary
758 // distribution and keep them in the upper part of the array
759 F[imax] = P[imax];
760 i = imax - 1;
761 while (i > xmed) {
762 F[i] = P[i] + F[i + 1];
763 i--;
764 }
765
766 // Reset imin because we lose too much precision for a few terms near
767 // imin when we stop adding terms < epsilon.
768 i = imin;
769 while (i < xmed && F[i] < DiscreteDistributionInt.EPSILON)
770 i++;
771 xmin = imin = i;
772
773 /* Same thing with imax */
774 i = imax;
775 while (i > xmed && F[i] < DiscreteDistributionInt.EPSILON)
776 i--;
777 xmax = imax = i;
778
779 pdf = new double[imax + 1 - imin];
780 cdf = new double[imax + 1 - imin];
781 System.arraycopy(P, imin, pdf, 0, imax + 1 - imin);
782 System.arraycopy(F, imin, cdf, 0, imax + 1 - imin);
783
784 }
785
789 public int getN() {
790 return n;
791 }
792
796 public double getP() {
797 return p;
798 }
799
804 public double[] getParams() {
805 double[] retour = { n, p };
806 return retour;
807 }
808
814 public void setParams(int n, double p) {
815 setBinomial(n, p);
816 }
817
821 public String toString() {
822 return getClass().getSimpleName() + " : n = " + n + ", p = " + p;
823 }
824
825}
static double[] getMLE(int[] x, int m)
Estimates the parameters of the binomial distribution using the maximum likelihood method,...
double cdf(int x)
Returns the distribution function evaluated at (see ( FDistDisc )).
static double getMean(int n, double p)
Computes the mean of the binomial distribution with parameters and .
double getMean()
Returns the mean of the distribution function.
static double cdf(int n, double p, int x)
Computes , the distribution function of a binomial random variable with parameters and ,...
double prob(int x)
Returns , the probability of .
static BinomialDist getInstanceFromMLE(int[] x, int m, int n)
Creates a new instance of a binomial distribution with given (fixed) parameter , and with parameter ...
static double prob(int n, double p, int x)
Computes and returns the binomial probability in eq.
static double barF(int n, double p, int x)
Returns , the complementary distribution function.
static int inverseF(int n, double p, double u)
Computes , the inverse of the binomial distribution.
double[] getParams()
Returns a table that contains the parameters of the current distribution, in regular order: [ ,...
double getVariance()
Returns the variance of the distribution function.
double barF(int x)
Returns , the complementary distribution function.
static double MAXN
The value of the parameter above which the tables are not precomputed by the constructor.
static double prob(int n, double p, double q, int x)
A generalization of the previous method.
static double[] getMLE(int[] x, int m, int n)
Estimates the parameter of the binomial distribution with given (fixed) parameter ,...
void setParams(int n, double p)
Resets the parameters to these new values and recomputes everything as in the constructor.
String toString()
Returns a String containing information about the current distribution.
int inverseFInt(double u)
Returns the inverse distribution function , where.
static double getVariance(int n, double p)
Computes the variance of the binomial distribution with parameters and .
BinomialDist(int n, double p)
Creates an object that contains the binomial terms ( fmass-binomial ), for , and the corresponding cu...
int getN()
Returns the parameter of this object.
static double getStandardDeviation(int n, double p)
Computes the standard deviation of the Binomial distribution with parameters and .
double getStandardDeviation()
Returns the standard deviation of the distribution function.
double getP()
Returns the parameter of this object.
static BinomialDist getInstanceFromMLE(int[] x, int m)
Creates a new instance of a binomial distribution with both parameters and estimated using the maxi...
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 normal distribution (e.g., tjoh95a  (page 80)).
static double cdf01(double x)
Same as cdf(0, 1, x).
static double barF01(double x)
Same as barF(0, 1, x).
Extends the class DiscreteDistributionInt for the Poisson distribution slaw00a  (page 325) with mean.
double cdf(int x)
Returns the distribution function evaluated at (see ( FDistDisc )).
double barF(int x)
Returns , the complementary distribution function.
This class provides various constants and methods to compute numerical quantities such as factorials,...
Definition Num.java:35
static final int DBL_MIN_EXP
Smallest int such that is representable (approximately) as a normalised double.
Definition Num.java:102
static double lnFactorial(int n)
Returns the value of , the natural logarithm of factorial .
Definition Num.java:313
static final int DBL_MAX_EXP
Largest int such that is representable (approximately) as a double.
Definition Num.java:96
static double combination(int n, int s)
Returns the value of , the number of different combinations of objects amongst.
Definition Num.java:243
static final double LN2
The values of .
Definition Num.java:148
This class provides numerical methods to solve non-linear equations.
static double brentDekker(double a, double b, MathFunction f, double tol)
Computes a root of the function in f using the Brent-Dekker method.
This interface should be implemented by classes which represent univariate mathematical functions.