Bläddra i källkod

* Patch from werner Pamler to implement GammaP/GammaQ functions (Bug ID 31534)

git-svn-id: trunk@35594 -
michael 8 år sedan
förälder
incheckning
655542e056
1 ändrade filer med 121 tillägg och 0 borttagningar
  1. 121 0
      packages/numlib/src/spe.pas

+ 121 - 0
packages/numlib/src/spe.pas

@@ -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;