/* Re Kolmogorov statistics, here is Birnbaum and Tingey's formula for the distribution of D+, the maximum of all positive deviations between a theoretical distribution function P(x) and an empirical one Sn(x) from n samples. + D = sup [P(x) - S (x)] n -inf < x < inf n [n(1-e)] + - v-1 n-v Pr{D > e} = > C e (e + v/n) (1 - e - v/n) n - n v v=0 [n(1-e)] is the largest integer not exceeding n(1-e). nCv is the number of combinations of n things taken v at a time. */ #include "mconf.h" #ifdef ANSIPROT extern double pow ( double, double ); extern double floor ( double ); extern double lgam ( double ); extern double exp ( double ); extern double sqrt ( double ); extern double log ( double ); extern double fabs ( double ); double smirnov ( int, double ); double kolmogorov ( double ); #else double pow (), floor (), lgam (), exp (), sqrt (), log (), fabs (); double smirnov (), kolmogorov (); #endif extern double MAXLOG; /* Exact Smirnov statistic, for one-sided test. */ double smirnov (n, e) int n; double e; { int v, nn; double evn, omevn, p, t, c, lgamnp1; if (n <= 0 || e < 0.0 || e > 1.0) return (-1.0); nn = floor ((double) n * (1.0 - e)); p = 0.0; if (n < 1013) { c = 1.0; for (v = 0; v <= nn; v++) { evn = e + ((double) v) / n; p += c * pow (evn, (double) (v - 1)) * pow (1.0 - evn, (double) (n - v)); /* Next combinatorial term; worst case error = 4e-15. */ c *= ((double) (n - v)) / (v + 1); } } else { lgamnp1 = lgam ((double) (n + 1)); for (v = 0; v <= nn; v++) { evn = e + ((double) v) / n; omevn = 1.0 - evn; if (fabs (omevn) > 0.0) { t = lgamnp1 - lgam ((double) (v + 1)) - lgam ((double) (n - v + 1)) + (v - 1) * log (evn) + (n - v) * log (omevn); if (t > -MAXLOG) p += exp (t); } } } return (p * e); } /* Kolmogorov's limiting distribution of two-sided test, returns probability that sqrt(n) * max deviation > y, or that max deviation > y/sqrt(n). The approximation is useful for the tail of the distribution when n is large. */ double kolmogorov (y) double y; { double p, t, r, sign, x; x = -2.0 * y * y; sign = 1.0; p = 0.0; r = 1.0; do { t = exp (x * r * r); p += sign * t; if (t == 0.0) break; r += 1.0; sign = -sign; } while ((t / p) > 1.1e-16); return (p + p); } /* Functional inverse of Smirnov distribution finds e such that smirnov(n,e) = p. */ double smirnovi (n, p) int n; double p; { double e, t, dpde; if (p <= 0.0 || p > 1.0) { mtherr ("smirnovi", DOMAIN); return 0.0; } /* Start with approximation p = exp(-2 n e^2). */ e = sqrt (-log (p) / (2.0 * n)); do { /* Use approximate derivative in Newton iteration. */ t = -2.0 * n * e; dpde = 2.0 * t * exp (t * e); if (fabs (dpde) > 0.0) t = (p - smirnov (n, e)) / dpde; else { mtherr ("smirnovi", UNDERFLOW); return 0.0; } e = e + t; if (e >= 1.0 || e <= 0.0) { mtherr ("smirnovi", OVERFLOW); return 0.0; } } while (fabs (t / e) > 1e-10); return (e); } /* Functional inverse of Kolmogorov statistic for two-sided test. Finds y such that kolmogorov(y) = p. If e = smirnovi (n,p), then kolmogi(2 * p) / sqrt(n) should be close to e. */ double kolmogi (p) double p; { double y, t, dpdy; if (p <= 0.0 || p > 1.0) { mtherr ("kolmogi", DOMAIN); return 0.0; } /* Start with approximation p = 2 exp(-2 y^2). */ y = sqrt (-0.5 * log (0.5 * p)); do { /* Use approximate derivative in Newton iteration. */ t = -2.0 * y; dpdy = 4.0 * t * exp (t * y); if (fabs (dpdy) > 0.0) t = (p - kolmogorov (y)) / dpdy; else { mtherr ("kolmogi", UNDERFLOW); return 0.0; } y = y + t; } while (fabs (t / y) > 1e-10); return (y); } #ifdef SALONE /* Type in a number. */ void getnum (s, px) char *s; double *px; { char str[30]; printf (" %s (%.15e) ? ", s, *px); gets (str); if (str[0] == '\0' || str[0] == '\n') return; sscanf (str, "%lf", px); printf ("%.15e\n", *px); } /* Type in values, get answers. */ void main () { int n; double e, p, ps, pk, ek, y; n = 5; e = 0.0; p = 0.1; loop: ps = n; getnum ("n", &ps); n = ps; if (n <= 0) { printf ("? Operator error.\n"); goto loop; } /* getnum ("e", &e); ps = smirnov (n, e); y = sqrt ((double) n) * e; printf ("y = %.4e\n", y); pk = kolmogorov (y); printf ("Smirnov = %.15e, Kolmogorov/2 = %.15e\n", ps, pk / 2.0); */ getnum ("p", &p); e = smirnovi (n, p); printf ("Smirnov e = %.15e\n", e); y = kolmogi (2.0 * p); ek = y / sqrt ((double) n); printf ("Kolmogorov e = %.15e\n", ek); goto loop; } #endif