/// <summary> /// Implements the Exponential Series for integer n /// <para>Sum = ((-z)^(n-1)/(n-1)!) * (ψ(n)-Log(z)) - Σ( (k==n-1) ? 0 : ((-1)^k * z^k)/((k-n+1) * k!)) k={0,∞}</para> /// </summary> /// <param name="n"></param> /// <param name="z"></param> /// <returns></returns> /// <see href="http://functions.wolfram.com/GammaBetaErf/ExpIntegralE/06/01/04/01/02/0005/"/> public static double En_Series(int n, double z) { Debug.Assert(n > 0); const double DefaultTolerance = 2 * DoubleLimits.MachineEpsilon; // the sign of the summation is negative // so negate these double sum = (n > 1) ? -1.0 / (1.0 - n) : 0; double term = -1.0; int k = 1; for (; k < n - 1; k++) { term *= -z / k; if (term == 0) { break; } sum += term / (k - n + 1); } sum += Math.Pow(-z, n - 1) * (Math2.Digamma(n) - Math.Log(z)) / Math2.Factorial(n - 1); if (term == 0) { return(sum); } // skip n-1 term term *= -z / k; for (int i = 0; i < Policies.MaxSeriesIterations; i++) { double prevSum = sum; k++; term *= -z / k; double delta = term / (k - n + 1); sum += delta; if (Math.Abs(delta) <= Math.Abs(prevSum) * DefaultTolerance) { return(sum); } } Policies.ReportConvergenceError("Series did not converge in {0} iterations", Policies.MaxSeriesIterations); return(double.NaN); }
public static (double Result, int Iterations) RefineQ_SmallA_LargeZ(double z, double p, double q, double aGuess) { Debug.Assert(z >= 0, "Requires z >= 0"); Debug.Assert(aGuess >= 0 && aGuess <= double.MaxValue, "Requires finite aGuess >= 0"); Debug.Assert(q <= 0.5, "Requires q <= 0.5"); Debug.Assert(aGuess < z + 1); // f = Log(z^a/Gamma(a))-Log(q); // f/f' = (Log(z^a/Gamma(a))-Log(q))/ (Log(z) - Polygamma(0, a)) // // http://functions.wolfram.com/GammaBetaErf/GammaRegularized/06/02/02/0001/ // For z->Infinity, Q(a,z) ~= z^(a-1)*e^-z/Gamma(a) * (1 - (1-a)/z ...) // Find a quick estimate of a // where Log[q] = Log[z^(a-1)*e^-z/Gamma(a)] to get into the ballpark double lz = Math.Log(z); double lq = Math.Log(q); int i = 0; for (; i < 1; i++) { const double eps = 1.0 / 2; double f = (aGuess - 1) * lz - z - Math2.Lgamma(aGuess) - lq; if (f == 0) { break; } var delta = f / (lz - Math2.Digamma(aGuess)); double aLast = aGuess; aGuess -= delta; if (Math.Abs(delta) <= eps * Math.Abs(aLast)) { break; } } return(aGuess, i); }