// one-argument functions /// <summary> /// Computes the natural logarithm of the Gamma function. /// </summary> /// <param name="x">The argument, which must be positive.</param> /// <returns>The log Gamma function ln(Γ(x)).</returns> /// <remarks> /// <para>Because Γ(x) grows rapidly for increasing positive x, it is often necessary to /// work with its logarithm in order to avoid overflow. This function returns accurate /// values of ln(Γ(x)) even for values of x which would cause Γ(x) to overflow.</para> /// </remarks> /// <exception cref="ArgumentOutOfRangeException"><paramref name="x"/> is negative.</exception> /// <seealso cref="Gamma(double)" /> public static double LogGamma(double x) { if (x < 0.0) { throw new ArgumentOutOfRangeException(nameof(x)); } else if (x < 16.0) { // For small arguments, use the Lanczos approximation. return(Lanczos.LogGamma(x)); } else if (x < Double.PositiveInfinity) { // For large arguments, the asymptotic series is even faster than the Lanczos approximation. return(Stirling.LogGamma(x)); } else if (x == Double.PositiveInfinity) { // Precisely at infinity x * Math.Log(x) - x => NaN, so special-case it. return(Double.PositiveInfinity); } else { return(Double.NaN); } }
/// <summary> /// Compute the complex log Gamma function. /// </summary> /// <param name="z">The complex argument, which must have a non-negative real part.</param> /// <returns>The complex value ln(Γ(z)).</returns> /// <exception cref="ArgumentOutOfRangeException">The real part of <paramref name="z"/> is negative.</exception> /// <seealso cref="AdvancedMath.LogGamma" /> public static Complex LogGamma(Complex z) { if (z.Re < 0.0) { throw new ArgumentOutOfRangeException("z"); } else if (ComplexMath.Abs(z) < 16.0) { return(Lanczos.LogGamma(z)); } else { return(LogGamma_Stirling(z)); } }
// one-argument functions /// <summary> /// Computes the natural logrithm of the Gamma function. /// </summary> /// <param name="x">The argument, which must be positive.</param> /// <returns>The log Gamma function ln(Γ(x)).</returns> /// <remarks> /// <para>Because Γ(x) grows rapidly for increasing positive x, it is often necessary to /// work with its logarithm in order to avoid overflow. This function returns accurate /// values of ln(Γ(x)) even for values of x which would cause Γ(x) to overflow.</para> /// </remarks> /// <exception cref="ArgumentOutOfRangeException"><paramref name="x"/> is negative or zero.</exception> /// <seealso cref="Gamma(double)" /> public static double LogGamma(double x) { if (x <= 0.0) { throw new ArgumentOutOfRangeException("x"); } else if (x < 16.0) { // For small arguments, use the Lanczos approximation. return(Lanczos.LogGamma(x)); } else { // For large arguments, the asymptotic series is even faster than the Lanczos approximation. return(Stirling.LogGamma(x)); //return (LogGamma_Stirling(x)); } }
// one-argument functions /// <summary> /// Computes the natural logarithm of the Gamma function. /// </summary> /// <param name="x">The argument, which must be positive.</param> /// <returns>The log Gamma function ln(Γ(x)).</returns> /// <remarks> /// <para>Because Γ(x) grows rapidly for increasing positive x, it is often necessary to /// work with its logarithm in order to avoid overflow. This function returns accurate /// values of ln(Γ(x)) even for values of x which would cause Γ(x) to overflow.</para> /// </remarks> /// <exception cref="ArgumentOutOfRangeException"><paramref name="x"/> is negative.</exception> /// <seealso cref="Gamma(double)" /> public static double LogGamma(double x) { if (x < 0.0) { throw new ArgumentOutOfRangeException(nameof(x)); } else if (x < 0.5) { // For small arguments, use the Lanczos approximation. return(Lanczos.LogGamma(x)); } else if (x < 1.5) { // Use the series expansion near 1. return(GammaSeries.LogGammaOnePlus(x - 1.0)); } else if (x < 2.5) { // The series expansion can be adapted near 2, too. return(GammaSeries.LogGammaTwoPlus(x - 2.0)); } else if (x < 16.0) { // In between, we still use Lanczos. return(Lanczos.LogGamma(x)); } else if (x < Double.PositiveInfinity) { // For large arguments, the asymptotic series is even faster than the Lanczos approximation. return(Stirling.LogGamma(x)); } else if (x == Double.PositiveInfinity) { // Precisely at infinity x * Math.Log(x) - x => NaN, so special-case it. return(Double.PositiveInfinity); } else { return(Double.NaN); } }
/// <summary> /// Compute the complex log Gamma function. /// </summary> /// <param name="z">The complex argument.</param> /// <returns>The principal complex value y for which exp(y) = Γ(z).</returns> /// <seealso cref="AdvancedMath.LogGamma" /> /// <seealso href="http://mathworld.wolfram.com/LogGammaFunction.html"/> public static Complex LogGamma(Complex z) { if (z.Im == 0.0 && z.Re < 0.0) { // Handle the pure negative case explicitly. double re = Math.Log(Math.PI / Math.Abs(MoreMath.SinPi(z.Re))) - AdvancedMath.LogGamma(1.0 - z.Re); double im = Math.PI * Math.Floor(z.Re); return(new Complex(re, im)); } else if (z.Re > 16.0 || Math.Abs(z.Im) > 16.0) { // According to https://dlmf.nist.gov/5.11, the Stirling asymptoic series is valid everywhere // except on the negative real axis. So at first I tried to use it for |z.Im| > 0, |z| > 16. But in practice, // it exhibits false convergence close to the negative real axis, e.g. z = -16 + i. So I have // moved to requiring |z| large and reasonably far from the negative real axis. return(Stirling.LogGamma(z)); } else if (z.Re >= 0.125) { return(Lanczos.LogGamma(z)); } else { // For the remaining z < 0, we need to use the reflection formula. // For large z.Im, SinPi(z) \propto e^{\pi |z.Im|} overflows even though its log does not. // It's possible to do some algebra to get around that problem, but it's not necessary // because for z.Im that big we would have used the Stirling series. Complex f = ComplexMath.Log(Math.PI / ComplexMath.SinPi(z)); Complex g = Lanczos.LogGamma(1.0 - z); // The reflection formula doesn't stay on the principal branch, so we need to add a multiple of 2 \pi i // to fix it up. See Hare, "Computing the Principal Branch of Log Gamma" for how to do this. // https://pdfs.semanticscholar.org/1c9d/8865836a312836500126cb47c3cbbed3043e.pdf Complex h = new Complex(0.0, 2.0 * Math.PI * Math.Floor(0.5 * (z.Re + 0.5))); if (z.Im < 0.0) { h = -h; } return(f - g + h); } }