mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-09 22:48:57 +02:00
* Patch from werner Pamler to implement GammaP/GammaQ functions (Bug ID 31534)
git-svn-id: trunk@35594 -
This commit is contained in:
parent
0a9031c76f
commit
655542e056
@ -20,6 +20,9 @@
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
**********************************************************************}
|
||||
{$mode objfpc}{$H+}
|
||||
{$modeswitch nestedprocvars}
|
||||
|
||||
unit spe;
|
||||
{$I DIRECT.INC}
|
||||
|
||||
@ -66,6 +69,15 @@ function spegam(x: ArbFloat): ArbFloat;
|
||||
{ Function to calculate the natural logaritm of the Gamma function}
|
||||
function spelga(x: ArbFloat): ArbFloat;
|
||||
|
||||
{ Function to calculate the lower incomplete Gamma function
|
||||
int(t,0,x,exp(-t)t^(s-1)) / spegam(s) (s > 0) }
|
||||
function gammap(s, x: ArbFloat): ArbFloat;
|
||||
|
||||
{ Function to calculate the upper incomplete Gamma function
|
||||
int(t,x,inf,exp(-t)t^(s-1)) / spegam(s) (s > 0)
|
||||
gammaq(s,x) = 1 - gammap(s,x) }
|
||||
function gammaq(s, x: ArbFloat): ArbFloat;
|
||||
|
||||
{ "Calculates" the maximum of two ArbFloat values }
|
||||
function spemax(a, b: ArbFloat): ArbFloat;
|
||||
|
||||
@ -1003,6 +1015,115 @@ begin
|
||||
RunError(408)
|
||||
end; {spelga}
|
||||
|
||||
{ Implements power series expansion for lower incomplete gamma function
|
||||
according to
|
||||
https://en.wikipedia.org/wiki/Incomplete_gamma_function#Evaluation_formulae
|
||||
gamma(s, x) = sum {k = 0 to inf, x^s exp(-x) x^k / (s (s+1) ... (s+k) ) }
|
||||
Converges rapidly for x < s + 1 }
|
||||
function gammaser(s, x: ArbFloat): ArbFloat;
|
||||
const
|
||||
MAX_IT = 100;
|
||||
EPS = 1E-7;
|
||||
var
|
||||
delta: Arbfloat;
|
||||
sum: ArbFloat;
|
||||
k: Integer;
|
||||
lngamma: ArbFloat;
|
||||
begin
|
||||
delta := 1 / s;
|
||||
sum := delta;
|
||||
for k := 1 to MAX_IT do begin
|
||||
delta := delta * x / (s + k);
|
||||
sum := sum + delta;
|
||||
if delta < EPS then break;
|
||||
end;
|
||||
lngamma := spelga(s); // log of complete gamma(s)
|
||||
Result := exp(s * ln(x) - x + ln(sum) - lngamma);
|
||||
end;
|
||||
|
||||
type
|
||||
TCFFunc = function(n: Integer): ArbFloat is nested;
|
||||
|
||||
{ Calculates the continued fraction a0 + (b1 / (a1 + b2 / (a2 + b3 / (a3 + b4 /...))))
|
||||
Ref.: https://rosettacode.org/wiki/Continued_fraction#C}
|
||||
function CalcCF(funca, funcb: TCfFunc; NumIt: Integer): ArbFloat;
|
||||
var
|
||||
r: ArbFloat;
|
||||
i: Integer;
|
||||
begin
|
||||
r := 0;
|
||||
for i := NumIt downTo 1 do
|
||||
r := funcb(i) / (funca(i) + r);
|
||||
Result := funca(0) + r;
|
||||
end;
|
||||
|
||||
{ calculates the upper incomplete gamma function using its continued
|
||||
fraction expansion
|
||||
https://en.wikipedia.org/wiki/Incomplete_gamma_function#Connection_with_Kummer.27s_confluent_hypergeometric_function }
|
||||
function gammacf(s, x: ArbFloat): ArbFloat;
|
||||
|
||||
function funca(i: Integer): ArbFloat;
|
||||
begin
|
||||
if i = 0 then
|
||||
Result := 0
|
||||
else
|
||||
if odd(i) then
|
||||
Result := x
|
||||
else
|
||||
Result := 1;
|
||||
end;
|
||||
|
||||
function funcb(i: Integer): ArbFloat;
|
||||
begin
|
||||
if i = 1 then
|
||||
Result := 1
|
||||
else
|
||||
if odd(i) then
|
||||
Result := (i-1) div 2
|
||||
else
|
||||
Result := i div 2 - s;
|
||||
end;
|
||||
|
||||
const
|
||||
MAX_IT = 100;
|
||||
EPS = 1E-7;
|
||||
var
|
||||
gamma, prevgamma, lngamma: ArbFloat;
|
||||
i: Integer;
|
||||
begin
|
||||
prevgamma := giant;
|
||||
i := 0;
|
||||
while i < MAX_IT do begin
|
||||
gamma := CalcCF(@funca, @funcb, i);
|
||||
if (abs(gamma - prevgamma) < EPS) then
|
||||
break;
|
||||
prevgamma := gamma;
|
||||
inc(i);
|
||||
end;
|
||||
lngamma := spelga(s); // logarithm of complete gamma(s)
|
||||
Result := exp(-x + s*ln(x) - lngamma) * gamma;
|
||||
end;
|
||||
|
||||
function gammap(s, x: ArbFloat): ArbFloat;
|
||||
begin
|
||||
if (x < 0.0) or (s <= 0.0) then
|
||||
RunError(401); // Invalid argument of gammap
|
||||
if x < s + 1 then
|
||||
Result := gammaser(s, x) // Use series expansion
|
||||
else
|
||||
Result := 1.0 - gammacf(s, x); // Use continued fraction
|
||||
end;
|
||||
|
||||
function gammaq(s, x: ArbFloat): ArbFloat;
|
||||
begin
|
||||
if (x < 0.0) or (s <= 0.0) then
|
||||
RunError(401); // Invalid argument of gammaq
|
||||
if x < s + 1 then
|
||||
Result := 1.0 - gammaser(s, x) // Use series expansion
|
||||
else
|
||||
Result := gammacf(s, x); // Use continued fraction
|
||||
end;
|
||||
|
||||
function spepol(x: ArbFloat; var a: ArbFloat; n: ArbInt): ArbFloat;
|
||||
var pa : ^arfloat0;
|
||||
i : ArbInt;
|
||||
|
Loading…
Reference in New Issue
Block a user