/** Computes the distribution function. */ public static double cdf(double n, double p, int x) { final double EPSILON = DiscreteDistributionInt.EPSILON; final int LIM1 = 100000; double sum, term, termmode; int i, mode; final double q = 1.0 - p; if (p < 0.0 || p > 1.0) throw new IllegalArgumentException("p not in [0, 1]"); if (n <= 0.0) throw new IllegalArgumentException("n <= 0.0"); if (x < 0) return 0.0; if (p >= 1.0) // In fact, p == 1 return 1.0; if (p <= 0.0) // In fact, p == 0 return 0.0; // Compute the maximum term mode = 1 + (int) Math.floor((n * q - 1.0) / p); if (mode < 0) mode = 0; else if (mode > x) mode = x; if (mode <= LIM1) { sum = term = termmode = prob(n, p, mode); for (i = mode; i > 0; i--) { term *= i / (q * (n + i - 1.0)); if (term < EPSILON) break; sum += term; } term = termmode; for (i = mode; i < x; i++) { term *= q * (n + i) / (i + 1); if (term < EPSILON) break; sum += term; } if (sum <= 1.0) return sum; else return 1.0; } else // return 1.0 - BinomialDist.cdf (x + n, p, n - 1); return BetaDist.cdf(n, x + 1.0, 15, p); }
/** * Sets the parameter <SPAN CLASS="MATH"><I>n</I></SPAN> and <SPAN CLASS="MATH"><I>p</I></SPAN> of * this object. */ public void setParams(double n, double p) { /* * * Compute all probability terms of the negative binomial distribution; * start at the mode, and calculate probabilities on each side until they * become smaller than EPSILON. Set all others to 0. */ supportA = 0; int i, mode, Nmax; int imin, imax; double sum; double[] P; // Negative Binomial mass probabilities double[] F; // Negative Binomial cumulative if (p < 0.0 || p > 1.0) throw new IllegalArgumentException("p not in [0, 1]"); if (n <= 0.0) throw new IllegalArgumentException("n <= 0"); this.n = n; this.p = p; // Compute the mode (at the maximum term) mode = 1 + (int) Math.floor((n * (1.0 - p) - 1.0) / p); /* * For mode > MAXN, we shall not use pre-computed arrays. mode < 0 should be impossible, unless overflow of long occur, in which case mode will be = LONG_MIN. */ if (mode < 0.0 || mode > MAXN) { pdf = null; cdf = null; return; } /* * In theory, the negative binomial distribution has an infinite range. But for i > Nmax, probabilities should be extremely small. Nmax = Mean + 16 * Standard deviation. */ Nmax = (int) (n * (1.0 - p) / p + 16 * Math.sqrt(n * (1.0 - p) / (p * p))); if (Nmax < 32) Nmax = 32; P = new double[1 + Nmax]; double epsilon = EPSILON / prob(n, p, mode); // We shall normalize by explicitly summing all terms >= epsilon sum = P[mode] = 1.0; // Start from the maximum and compute terms > epsilon on each side. i = mode; while (i > 0 && P[i] >= epsilon) { P[i - 1] = P[i] * i / ((1.0 - p) * (n + i - 1)); i--; sum += P[i]; } imin = i; i = mode; while (P[i] >= epsilon) { P[i + 1] = P[i] * (1.0 - p) * (n + i) / (i + 1); i++; sum += P[i]; if (i == Nmax - 1) { Nmax *= 2; double[] nT = new double[1 + Nmax]; System.arraycopy(P, 0, nT, 0, P.length); P = nT; } } imax = i; // Renormalize the sum of probabilities to 1 for (i = imin; i <= imax; i++) P[i] /= sum; // Compute the cumulative probabilities for F and keep them in the // lower part of CDF. F = new double[1 + Nmax]; F[imin] = P[imin]; i = imin; while (i < imax && F[i] < 0.5) { i++; F[i] = F[i - 1] + P[i]; } // This is the boundary between F (i <= xmed) and 1 - F (i > xmed) in // the array CDF xmed = i; // Compute the cumulative probabilities of the complementary // distribution 1 - F and keep them in the upper part of the array F[imax] = P[imax]; i = imax - 1; do { F[i] = P[i] + F[i + 1]; i--; } while (i > xmed); xmin = imin; xmax = imax; pdf = new double[imax + 1 - imin]; cdf = new double[imax + 1 - imin]; System.arraycopy(P, imin, pdf, 0, imax + 1 - imin); System.arraycopy(F, imin, cdf, 0, imax + 1 - imin); }
/** Computes the inverse function without precomputing tables. */ public static int inverseF(double n, double p, double u) { if (u < 0.0 || u > 1.0) throw new IllegalArgumentException("u is not in [0,1]"); if (p < 0.0 || p > 1.0) throw new IllegalArgumentException("p not in [0, 1]"); if (n <= 0.0) throw new IllegalArgumentException("n <= 0"); if (p >= 1.0) // In fact, p == 1 return 0; if (p <= 0.0) // In fact, p == 0 return 0; if (u <= prob(n, p, 0)) return 0; if (u >= 1.0) return Integer.MAX_VALUE; double sum, term, termmode; final double q = 1.0 - p; // Compute the maximum term int mode = 1 + (int) Math.floor((n * q - 1.0) / p); if (mode < 0) mode = 0; int i = mode; term = prob(n, p, i); while ((term >= u) && (term > Double.MIN_NORMAL)) { i /= 2; term = prob(n, p, i); } if (term <= Double.MIN_NORMAL) { i *= 2; term = prob(n, p, i); while (term >= u && (term > Double.MIN_NORMAL)) { term *= i / (q * (n + i - 1.0)); i--; } } mode = i; sum = termmode = prob(n, p, i); for (i = mode; i > 0; i--) { term *= i / (q * (n + i - 1.0)); if (term < EPSILON) break; sum += term; } term = termmode; i = mode; double prev = -1; if (sum < u) { // The CDF at the mode is less than u, so we add term to get >= u. while ((sum < u) && (sum > prev)) { term *= q * (n + i) / (i + 1); prev = sum; sum += term; i++; } } else { // The computed CDF is too big so we substract from it. sum -= term; while (sum >= u) { term *= i / (q * (n + i - 1.0)); i--; sum -= term; } } return i; }