/* *
 * Title:        

* Description:

* Copyright: Copyright (c)

* Company:

* @author * @version 1.0 */ package umontreal.iro.lecuyer.probdist; import umontreal.iro.lecuyer.util.*; import umontreal.iro.lecuyer.functions.MathFunction; /** * Extends the class {@link ContinuousDistribution} for the * Kolmogorov-Smirnov distribution with parameter n. * Given an empirical distribution Fn with n independent observations and * a continuous distribution F(x), the two-sided statistic is defined as *

*

*
* Dn = sup-∞ <= x <= ∞| Fn(x) - F(x)|  = {Dn+, Dn-}, *

* where Dn+ and Dn- are the + and - statistics as defined in * equations and on page [*] of this guide. * This class implements a high precision version of the * distribution * P[Dn <= x]; it is a Java translation of the C program * written in. According to its authors, it should give * 13 decimal digits of precision. It is extremely slow * for large values of n. * */ public class KolmogorovSmirnovDist extends ContinuousDistribution { protected int n; protected static final int NLIM = 20; // For the Durbin matrix algorithm private static final double NORM = 1.0e140; private static final double INORM = 1.0e-140; private static final int LOGNORM = 140; private static class Function implements MathFunction { protected int n; protected double u; public Function (int n, double u) { this.n = n; this.u = u; } public double evaluate (double x) { return u - cdf(n,x); } } /** * Constructs a distribution with parameter n. * Restriction: n >= 1. * */ public KolmogorovSmirnovDist (int n) { setN (n); } public double density (double x) { return density (n, x); } public double cdf (double x) { return cdf (n, x); } public double barF (double x) { return barF (n, x); } public double inverseF (double u) { return inverseF (n, u); } private static double dclem (int n, double x, double EPS) { return (cdf(n, x + EPS) - cdf(n, x - EPS)) / (2.0 * EPS); } protected static double densConnue (int n, double x) { if ((x >= 1.0) || (x <= 0.5 / n)) return 0.0; if (n == 1) return 2.0; if (x <= 1.0 / n) { double w; double t = 2.0 * x - 1.0 / n; if (n <= NLIM) { w = 2.0 * n * Num.factorial (n); w *= Math.pow (t, (double) (n - 1)); return w; } w = Num.lnFactorial (n) + (n-1) * Math.log (t); return 2*n*Math.exp (w); } if (x >= 1.0 - 1.0 / n) return 2.0 * n * Math.pow (1.0 - x, (double) (n - 1)); return -1.0; } /** * Computes the density for the distribution with * parameter n. * */ public static double density (int n, double x) { double Res = densConnue(n,x); if (Res != -1.0) return Res; double EPS = 1.0 / 200.0; final double D1 = dclem(n, x, EPS); final double D2 = dclem(n, x, 2.0 * EPS); Res = D1 + (D1 - D2) / 3.0; if (Res <= 0.0) return 0.0; return Res; } /*========================================================================= The following implements the Durbin matrix algorithm and was programmed in C by G. Marsaglia, Wai Wan Tsang and Jingbo Wong in C. I have translated their program in Java. Only small modifications have been made in their program; the most important is to prevent the return of NAN or infinite values in some regions. (Richard Simard) =========================================================================*/ /* The C program to compute Kolmogorov's distribution K(n,d) = Prob(D_n < d), where D_n = max(x_1-0/n,x_2-1/n...,x_n-(n-1)/n,1/n-x_1,2/n-x_2,...,n/n-x_n) with x_1 NORM) renormalize (B, m, pB); if (n % 2 == 0) { for (i = 0; i < m * m; i++) V[i] = B[i]; eV[0] = pB[0]; } else { mMultiply (A, B, V, m); eV[0] = eA + pB[0]; } if (V[(m / 2) * m + (m / 2)] > NORM) renormalize (V, m, eV); } protected static double DurbinMatrix (int n, double d) { int k, m, i, j, g, eH; double h, s; double[] H; double[] Q; int[] pQ; //Omit next two lines if you require >7 digit accuracy in the right tail if (false) { s = d * d * n; if (s > 7.24 || (s > 3.76 && n > 99)) return 1 - 2 * Math.exp (-(2.000071 + .331 / Math.sqrt (n) + 1.409 / n) * s); } k = (int) (n * d) + 1; m = 2 * k - 1; h = k - n * d; H = new double[m * m]; Q = new double[m * m]; pQ = new int[1]; for (i = 0; i < m; i++) for (j = 0; j < m; j++) if (i - j + 1 < 0) H[i * m + j] = 0; else H[i * m + j] = 1; for (i = 0; i < m; i++) { H[i * m] -= Math.pow (h, (double)(i + 1)); H[(m - 1) * m + i] -= Math.pow (h, (double)(m - i)); } H[(m - 1) * m] += (2 * h - 1 > 0 ? Math.pow (2 * h - 1, (double) m) : 0); for (i = 0; i < m; i++) for (j = 0; j < m; j++) if (i - j + 1 > 0) for (g = 1; g <= i - j + 1; g++) H[i * m + j] /= g; eH = 0; mPower (H, eH, Q, pQ, m, n); s = Q[(k - 1) * m + k - 1]; for (i = 1; i <= n; i++) { s = s * (double) i / n; if (s < INORM) { s *= NORM; pQ[0] -= LOGNORM; } } s *= Math.pow (10., (double) pQ[0]); return s; } protected static double cdfConnu (int n, double x) { // For nx^2 > 18, barF(n, x) is smaller than 5e-16 if ((n * x * x >= 18.0) || (x >= 1.0)) return 1.0; if (x <= 0.5 / n) return 0.0; if (n == 1) return 2.0 * x - 1.0; if (x <= 1.0 / n) { double w; double t = 2.0 * x - 1.0 / n; if (n <= NLIM) { w = Num.factorial (n); return w * Math.pow (t, (double) n); } w = Num.lnFactorial (n) + n * Math.log (t); return Math.exp (w); } if (x >= 1.0 - 1.0 / n) { return 1.0 - 2.0 * Math.pow (1.0 - x, (double) n); } return -1.0; } /** * Computes the distribution function F(x) with parameter n * using Durbin's matrix formula. It is a translation of the * C program in; * according to its authors, it returns 13 decimal digits * of precision. It is extremely slow for large n. * */ public static double cdf (int n, double x) { double Res = cdfConnu(n,x); if (Res != -1.0) return Res; return DurbinMatrix (n, x); } protected static double barFConnu (int n, double x) { if ((n * x * x >= 370.0) || (x >= 1.0)) return 0.0; if ((n * x * x <= 0.0274) || (x <= 0.5 / n)) return 1.0; if (n == 1) return 2.0 - 2.0 * x; if (x <= 1.0 / n) { double w; final double t = 2.0 * x - 1.0 / n; if (n <= NLIM) { w = Num.factorial (n); return 1.0 - w * Math.pow (t, (double) n); } w = Num.lnFactorial (n) + n * Math.log (t); return 1.0 - Math.exp (w); } if (x >= 1.0 - 1.0 / n) { return 2.0 * Math.pow (1.0 - x, (double) n); } return -1.0; } /** * Computes the complementary distribution function bar(F)(x) * with parameter n. Simply returns 1 - cdf(n,x). It is not precise in * the upper tail. * */ public static double barF (int n, double x) { double h = barFConnu(n, x); if (h >= 0.0) return h; h = 1.0 - cdf(n, x); if (h >= 0.0) return h; return 0.0; } protected static double inverseConnue (int n, double u) { if (n <= 0) throw new IllegalArgumentException ("n <= 0"); if (u < 0.0 || u > 1.0) throw new IllegalArgumentException ("u must be in [0,1]"); if (u == 1.0) return 1.0; if (u == 0.0) return 0.5/n; if (n == 1) return (u + 1.0) / 2.0; final double NLNN = n*Math.log (n); final double LNU = Math.log(u) - Num.lnFactorial (n); if (LNU <= -NLNN){ double t = 1.0/n*(LNU); return 0.5 * (Math.exp(t) + 1.0/n); } if (u >= 1.0 - 2.0 / Math.exp (NLNN)) return 1.0 - Math.pow((1.0-u)/2.0, 1.0/n); return -1.0; } /** * Computes the inverse * x = F-1(u) of the * distribution F(x) with parameter n. * */ public static double inverseF (int n, double u) { double Res = inverseConnue(n,u); if (Res != -1.0) return Res; Function f = new Function (n,u); return RootFinder.brentDekker (0.5/n, 1.0, f, 1e-10); } /** * Returns the parameter n of this object. * */ public int getN() { return n; } /** * Sets the parameter n of this object. * */ public void setN (int n) { if (n <= 0) throw new IllegalArgumentException ("n <= 0"); this.n = n; supportA = 0.5 / n; supportB = 1.0; } /** * Returns an array containing the parameter n of this object. * * */ public double[] getParams () { double[] retour = {n}; return retour; } public String toString () { return getClass().getSimpleName() + " : n = " + n; } }

Ads help maintain this website.