mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-18 17:09:16 +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.
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
**********************************************************************}
|
**********************************************************************}
|
||||||
|
{$mode objfpc}{$H+}
|
||||||
|
{$modeswitch nestedprocvars}
|
||||||
|
|
||||||
unit spe;
|
unit spe;
|
||||||
{$I DIRECT.INC}
|
{$I DIRECT.INC}
|
||||||
|
|
||||||
@ -66,6 +69,15 @@ function spegam(x: ArbFloat): ArbFloat;
|
|||||||
{ Function to calculate the natural logaritm of the Gamma function}
|
{ Function to calculate the natural logaritm of the Gamma function}
|
||||||
function spelga(x: ArbFloat): ArbFloat;
|
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 }
|
{ "Calculates" the maximum of two ArbFloat values }
|
||||||
function spemax(a, b: ArbFloat): ArbFloat;
|
function spemax(a, b: ArbFloat): ArbFloat;
|
||||||
|
|
||||||
@ -1003,6 +1015,115 @@ begin
|
|||||||
RunError(408)
|
RunError(408)
|
||||||
end; {spelga}
|
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;
|
function spepol(x: ArbFloat; var a: ArbFloat; n: ArbInt): ArbFloat;
|
||||||
var pa : ^arfloat0;
|
var pa : ^arfloat0;
|
||||||
i : ArbInt;
|
i : ArbInt;
|
||||||
|
Loading…
Reference in New Issue
Block a user