Files
lazarus-ccr/applications/lazstats/source/units/mathunit.pas

291 lines
6.9 KiB
ObjectPascal
Raw Normal View History

unit MathUnit;
{ extract some math functions from functionslib for easier testing }
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils;
function erf(x: Double): Double;
function erfc(x: Double) : Double;
function NormalDist(x: Double): Double;
function Beta(a, b: Double): Extended;
function BetaI(a,b,x: Double): Extended;
function GammaLn(x: double): Extended;
function tDist(x: Double; N: Integer; OneSided: Boolean): Double;
function tDensity(x: Double; N: Integer): Double;
function FDensity(x: Double; DF1, DF2: Integer): Double;
function Chi2Density(x: Double; N: Integer): Double;
implementation
uses
Math;
// Calculates the error function
// /x
// erf(x) = 2/sqrt(pi) * | exp(-t²) dt
// /0
// borrowed from NumLib
function erf(x: Double): Double;
const
xup = 6.25;
SQRT_PI = 1.7724538509055160;
c: array[1..18] of Double = (
1.9449071068178803e0, 4.20186582324414e-2, -1.86866103976769e-2,
5.1281061839107e-3, -1.0683107461726e-3, 1.744737872522e-4,
-2.15642065714e-5, 1.7282657974e-6, -2.00479241e-8,
-1.64782105e-8, 2.0008475e-9, 2.57716e-11,
-3.06343e-11, 1.9158e-12, 3.703e-13,
-5.43e-14, -4.0e-15, 1.2e-15
);
d: array[1..17] of Double = (
1.4831105640848036e0, -3.010710733865950e-1, 6.89948306898316e-2,
-1.39162712647222e-2, 2.4207995224335e-3, -3.658639685849e-4,
4.86209844323e-5, -5.7492565580e-6, 6.113243578e-7,
-5.89910153e-8, 5.2070091e-9, -4.232976e-10,
3.18811e-11, -2.2361e-12, 1.467e-13,
-9.0e-15, 5.0e-16
);
var
t, s, s1, s2, x2: Double;
bovc, bovd, j: Integer;
sgn: Integer;
begin
bovc := SizeOf(c) div SizeOf(Double);
bovd := SizeOf(d) div SizeOf(Double);
t := abs(x);
if t <= 2 then
begin
x2 := sqr(x) - 2;
s1 := d[bovd];
s2 := 0;
j := bovd - 1;
s := x2*s1 - s2 + d[j];
while j > 1 do
begin
s2 := s1;
s1 := s;
j := j-1;
s := x2*s1 - s2 + d[j];
end;
Result := (s - s2) * x / 2;
end else
if t < xup then
begin
x2 := 2 - 20 / (t+3);
s1 := c[bovc];
s2 := 0;
j := bovc - 1;
s := x2*s1 - s2 + c[j];
while j > 1 do
begin
s2 := s1;
s1 := s;
j := j-1;
s := x2*s1 - s2 + c[j];
end;
x2 := ((s-s2) / (2*t)) * exp(-sqr(x)) / SQRT_PI;
if x < 0 then sgn := -1 else sgn := +1;
Result := (1 - x2) * sgn
end
else
if x < 0 then
Result := -1.0
else
Result := +1.0;
end;
{ calculates the complementary error function erfc(x) = 1 - erf(x) }
function erfc(x: Double) : Double;
begin
Result := 1.0 - erf(x);
end;
// Cumulative normal distribution
// x = -INF ... INF --> 0 ... 1
function NormalDist(x: Double): Double;
const
SQRT2 = sqrt(2.0);
begin
if x > 0 then
Result := (erf(x / SQRT2) + 1) * 0.5
else
if x < 0 then
Result := (1.0 - erf(-x / SQRT2)) * 0.5
else
Result := 0;
end;
function Beta(a, b: Double): Extended;
begin
if (a > 0) and (b > 0) then
Result := exp(GammaLn(a) + GammaLn(b) - GammaLn(a+b))
else
raise Exception.Create('Invalid argument for beta function.');
end;
function BetaCF(a, b, x: double): Extended;
const
itmax = 100;
eps = 3.0e-7;
var
tem,qap,qam,qab,em,d: extended;
bz,bpp,bp,bm,az,app: extended;
am,aold,ap: extended;
term1, term2, term3, term4, term5, term6: extended;
m: integer;
BEGIN
am := 1.0;
bm := 1.0;
az := 1.0;
qab := a+b;
qap := a+1.0;
qam := a-1.0;
bz := 1.0 - qab * x / qap;
for m := 1 to itmax do begin
em := m;
tem := em+em;
d := em * (b - m) * x / ((qam + tem) * (a + tem));
ap := az + d * am;
bp := bz + d * bm;
term1 := -(a + em);
term2 := qab + em;
term3 := term1 * term2 * x;
term4 := a + tem;
term5 := qap + tem;
term6 := term4 * term5;
d := term3 / term6;
app := ap + d * az;
bpp := bp + d * bz;
aold := az;
am := ap/bpp;
bm := bp/bpp;
az := app/bpp;
bz := 1.0;
if ((abs(az-aold)) < (eps*abs(az))) then
Break;
end;
{ ShowMessage('WARNING! a or b too big, or itmax too small in betacf');}
Result := az
end;
function BetaI(a,b,x: Double): extended;
var
bt: extended;
term1, term2, term3, term4, term5: extended;
begin
if ((x < 0.0) or (x > 1.0)) then begin
{ ShowMessage('ERROR! Problem in routine BETAI');}
Result := 0.5;
exit;
end;
if ((x <= 0.0) or (x >= 1.0)) then
bt := 0.0
else
begin
term1 := GammaLn(a + b) - GammaLn(a) - GammaLn(b);
term2 := a * ln(x);
term3 := b * ln(1.0 - x);
term4 := term1 + term2 + term3;
bt := exp(term4);
end;
term5 := (a + 1.0) / (a + b + 2.0);
if x < term5 then
Result := bt * betacf(a,b,x) / a
else
Result := 1.0 - bt * betacf(b,a,1.0-x) / b
end;
function GammaLn(x: double): Extended;
var
tmp, ser: double;
cof: array[0..5] of double;
j: integer;
begin
cof[0] := 76.18009173;
cof[1] := -86.50532033;
cof[2] := 24.01409822;
cof[3] := -1.231739516;
cof[4] := 0.00120858003;
cof[5] := -0.00000536382;
x := x - 1.0;
tmp := x + 5.5;
tmp := tmp - (x + 0.5) * ln(tmp);
ser := 1.0;
for j := 0 to 5 do
begin
x := x + 1.0;
ser := ser + cof[j] / x;
end;
Result := -tmp + ln(2.50662827465 * ser);
end;
// Calculates the (cumulative) t distribution function for N degrees of freedom
function tDist(x: Double; N: Integer; OneSided: Boolean): Double;
begin
Result := betai(0.5*N, 0.5, N/(N + sqr(x)));
if OneSided then Result := Result * 0.5;
end;
// Returns the density curve for the t statistic with N degrees of freedom
function tDensity(x: Double; N: Integer): Double;
var
factor: Double;
begin
factor := exp(gammaLn((N+1)/2) - gammaLn(N/2)) / sqrt(N * pi);
Result := factor * Power(1 + sqr(x)/N, (1-n)/2);
end;
// Returns the density curve for the F statistic for DF1 and DF2 degrees of freedom.
function FDensity(x: Double; DF1, DF2: Integer): Double;
var
ratio1, ratio2, ratio3, ratio4: double;
part1, part2, part3, part4, part5, part6, part7, part8, part9: double;
begin
ratio1 := (DF1 + DF2) / 2.0;
ratio2 := (DF1 - 2.0) / 2.0;
ratio3 := DF1 / 2.0;
ratio4 := DF2 / 2.0;
part1 := exp(gammaln(ratio1));
part2 := power(DF1, ratio3);
part3 := power(DF2, ratio4);
part4 := exp(gammaln(ratio3));
part5 := exp(gammaln(ratio4));
part6 := power(x, ratio2);
part7 := power((x*DF1 + DF2), ratio1);
part8 := (part1 * part2 * part3) / (part4 * part5);
if (part7 = 0.0) then
part9 := 0.0
else
part9 := part6 / part7;
Result := part8 * part9;
end;
// Returns the density curve of the chi2 statistic for N degrees of freedom
function Chi2Density(x: Double; N: Integer): Double;
var
factor: Double;
begin
factor := Power(2.0, N * 0.5) * exp(gammaLN(N * 0.5));
Result := power(x, (N-2.0) * 0.5) / (factor * exp(x * 0.5));
end;
end.