qngmeter/RandTest/KolmogorovSmirnov.cpp

250 lines
5.1 KiB
C++
Raw Permalink Normal View History

2024-03-02 17:58:28 +02:00
//#include "StdAfx.h"
#include "KolmogorovSmirnov.h"
#include "memory.h"
#include "stdlib.h"
#include "math.h"
#include <float.h>
CKolmogorovSmirnov::CKolmogorovSmirnov()
{
}
///////////////////////////////////////////////////////////////////////////////
// double CKolmogorovSmirnov::ZtoP
//
// Returns: cumulative normal distribution value based on input z-score
// Accuracy better than 1% for zscore<=<3D>7.5; better than 0.05% for zscore=<3D>4
double CKolmogorovSmirnov::ZtoP( double zscore )
{
double retval;
// calculation variables
double w;
double y;
double t;
double num;
double denom;
// calculation constants
double c[8];
c[1] = 2.506628275; c[2] = 0.31938153; c[3] = -0.356563782; c[4] = 1.781477937;
c[5] = -1.821255978; c[6] = 1.330274429; c[7] = 0.2316419;
w = (zscore>=0)? 1 : -1;
y = 1./( 1. + (c[7]*w*zscore) );
t = 1. + (c[7]*w*zscore);
num = w * ( 0.5 - c[2] + ((c[6] + (c[5]*t) + (c[4]*t*t) + (c[3]*t*t*t))/(t*t*t*t)) );
denom = c[1] * pow( 10, pow(0.5*zscore, 2) );
retval = 0.5 + (num/denom);
return retval;
}
///////////////////////////////////////////////////////////////////////////////
// double CKolmogorovSmirnov::PtoZ
//
// Returns: z-score based on input cumulative normal distribution value
// Accuracy better than 0.1% for zscore<=<3D>7.5; 6 digits for zscore=<3D>6
double CKolmogorovSmirnov::PtoZ( double p )
{
double retval = -8.2;
if (p <= DBL_EPSILON)
return retval;
// calculation variables
double pp;
double y;
double num;
double denom;
// calculation constants
double P[5];
P[0] = -0.322232431088; P[1] = -1.0; P[2] = -0.342242088547;
P[3] = -0.0204231210245; P[4] = -0.453642210148e-4;
double q[5];
q[0] = 0.099348462606; q[1] = 0.588581570495; q[2] = 0.531103462366;
q[3] = 0.10353775285; q[4] = 0.38560700634e-2;
pp = (p<0.5)? p : (1.-p);
y = sqrt( log(1./(pp*pp)) );
num = y*(y*(y*(y*P[4]+P[3]) + P[2]) + P[1]) + P[0];
denom = y*(y*(y*(y*q[4]+q[3]) + q[2]) + q[1]) + q[0];
retval = y + (num/denom);
retval = (p<0.5)? -retval : retval;
return retval;
}
void CKolmogorovSmirnov::KSUP( double* pKn_pos, double* pKn_neg, double* U, unsigned long n )
{
unsigned long i; // universal index
double val; // calc value
double* Us = new double[n];
double Kn_pos;
double Kn_neg;
double* cump1 = new double[n+2];
double* cump2 = new double[n+2];
double* kn = new double[n+2];
double max;
double min;
memcpy( (void*)Us, (void*)U, n * sizeof(double) );
qsort( Us, n, sizeof(double), KSUPcompare );
cump1[1] = Us[0];
cump2[1] = 0.;
for ( i=1; i<=n; i++ )
{
cump1[i+1] = Us[i-1];
val = (double)i/n;
cump2[i+1] = val;
}
min = cump2[2] - cump1[2];
max = min;
for ( i=2; i<=(n+1); i++ )
{
kn[i-1] = cump2[i] - cump1[i];
if ( kn[i-1] > max )
{
max = kn[i-1];
}
if ( kn[i-1] < min )
{
min = kn[i-1];
}
}
Kn_pos = sqrt((double)n) * (double)max;
Kn_neg = (-sqrt((double)n)) * ((double)min - (1/(double)n));
*pKn_pos = KSProb( n, Kn_pos );
*pKn_neg = KSProb( n, Kn_neg );
if (Us!=NULL)
{
delete Us;
Us = NULL;
}
if (kn!=NULL)
{
delete kn;
kn = NULL;
}
if (cump1!=NULL)
{
delete cump1;
cump1 = NULL;
}
if (cump2!=NULL)
{
delete cump2;
cump2 = NULL;
}
}
double CKolmogorovSmirnov::KSProb( unsigned long n, double Kn )
{
double retval;
double e;
double las;
unsigned long j;
double cof[7];
cof[1] = 76.18009172947146; cof[2] = -86.50532032941677; cof[3] = 24.01409824083091;
cof[4] = -1.231739572450155; cof[5] = 1.208650973866179e-3; cof[6] = -5.395239384953e-6;
if ( Kn<=0 )
{
retval = 0;
}
else if ( Kn>=sqrt((double)n) )
{
retval = 1;
}
else
{
e = Kn / sqrt((double)n);
las = floor( (double)n - ((double)n*e) );
retval = 0;
for ( j=0; j<=las; j++ )
{
double ee = exp(lnbin(n, j));
double p1 = pow((e+(double)j/(double)n),((double)j-1.));
double p2 = pow((1.0-e-(double)j/(double)n),(double)(n-j));
retval += ee * p1 * p2;
}
retval = 1.-e*retval;
}
return retval;
}
double CKolmogorovSmirnov::lnbin( double n, double k )
{
double retval = 0;
if (k==0)
retval = 0;
else
{
retval = lnf(n) - lnf(k) - lnf(n-k);
}
return retval;
}
double CKolmogorovSmirnov::lnf( double xx )
{
double retval = 0;
double x1 = xx + 1.;
double cof[7];
cof[1] = 76.18009172947146; cof[2] = -86.50532032941677; cof[3] = 24.01409824083091;
cof[4] = -1.231739572450155; cof[5] = 1.208650973866179e-3; cof[6] = -5.395239384953e-6;
if (x1<=1.)
retval = 0;
else
{
double x = x1;
double y = x1;
double tmp = x + 5.5 - (x+.5)*log(x+5.5);
double ser = 1.000000000190015;
for (int j=0; j<=5; j++)
{
y = y+1.;
ser += cof[j+1]/y;
}
retval = log(2.506628274631 * ser/x) - tmp;
}
return retval;
}
int CKolmogorovSmirnov::KSUPcompare( const void* elem1, const void* elem2 )
{
if ((*(double*)elem1)==(*(double*)elem2))
return 0;
if ((*(double*)elem1)>(*(double*)elem2))
return 1;
else
return -1;
}