Accord.Statistics.Distributions.Univariate.KolmogorovSmirnovDistribution.PelzGood C# (CSharp) Method

PelzGood() public static method

Pelz-Good algorithm for computing lower-tail areas of the Kolmogorov-Smirnov distribution.

As stated in Simard's paper, Pelz and Good (1976) generalized Kolmogorov's approximation to an asymptotic series in 1/sqrt(n).

References: Wolfgang Pelz and I. J. Good, "Approximating the Lower Tail-Areas of the Kolmogorov-Smirnov One-Sample Statistic", Journal of the Royal Statistical Society, Series B. Vol. 38, No. 2 (1976), pp. 152-156

public static PelzGood ( double n, double x ) : double
n double
x double
return double
        public static double PelzGood(double n, double x)
        {
            const int maxTerms = 20;
            const double eps = 1.0e-10;

            const double PI2 = Math.PI * Math.PI;
            const double PI4 = PI2 * PI2;

            double sqrtN = Math.Sqrt(n);

            double z = sqrtN * x;
            double z2 = z * z;
            double z3 = z2 * z;
            double z4 = z2 * z2;
            double z6 = z4 * z2;
            double z7 = z4 * z3;
            double z8 = z4 * z4;
            double z10 = z8 * z2;

            double pz = -PI2 / (2 * z2);
            double term;


            double k0 = 0; // Evaluate K0(z)
            for (int k = 0; k <= maxTerms; k++)
            {
                double h = (k + 0.5);
                k0 += term = Math.Exp(h * h * pz);
                if (term <= eps * k0) break;
            }


            double k1 = 0; // Evaluate K1(z)
            for (int k = 0; k <= maxTerms; k++)
            {
                double hh = (k + 0.5) * (k + 0.5);
                k1 += term = (PI2 * hh - z2) * Math.Exp(hh * pz);
                if (Math.Abs(term) <= eps * Math.Abs(k1)) break;
            }


            double k2a = 0; // Evaluate 1st part of K2(z)
            for (int k = 0; k <= maxTerms; k++)
            {
                double hh = (k + 0.5) * (k + 0.5);
                k2a += term = (6 * z6 + 2 * z4
                            + PI2 * (2 * z4 - 5 * z2) * hh
                            + PI4 * (1 - 2 * z2) * hh * hh) * Math.Exp(hh * pz);
                if (Math.Abs(term) <= eps * Math.Abs(k2a)) break;
            }

            double k2b = 0; // Evaluate 2nd part of K2(z)
            for (int k = 1; k <= maxTerms; k++)
            {
                double kk = k * k;
                k2b += term = PI2 * kk * Math.Exp(kk * pz);
                if (term <= eps * k2b) break;
            }


            double k3a = 0; // Evaluate 1st part of K3(z)
            for (int k = 0; k <= maxTerms; k++)
            {
                double hh = (k + 0.5) * (k + 0.5);
                k3a += term = (-30 * z6 - 90 * z8
                    + PI2 * (135 * z4 - 96 * z6) * hh
                    + PI4 * (212 * z4 - 60 * z2) * hh * hh
                    + PI2 * PI4 * hh * hh * hh * (5 - 30 * z2)) * Math.Exp(hh * pz);
                if (Math.Abs(term) <= eps * Math.Abs(k3a)) break;
            }

            double k3b = 0; // Evaluate 2nd part of K3(z)
            for (int k = 1; k <= maxTerms; k++)
            {
                double kk = k * k;
                k3b += term = (3 * PI2 * kk * z2 - PI4 * kk * kk) * Math.Exp(kk * pz);
                if (Math.Abs(term) <= eps * Math.Abs(k3b)) break;
            }


            // Evaluate the P[sqrt(N) * Dn <= z | H0]
            double sum = k0 * (Constants.Sqrt2PI / z)
                       + k1 * (Constants.SqrtHalfPI / (sqrtN * 3.0 * z4))
                       + k2a * (Constants.SqrtHalfPI / (n * 36.0 * z7))
                       - k2b * (Constants.SqrtHalfPI / (n * 18.0 * z3))
                       + k3a * (Constants.SqrtHalfPI / (n * sqrtN * 3240.0 * z10))
                       + k3b * (Constants.SqrtHalfPI / (n * sqrtN * 108.0 * z6));

            return sum;
        }