// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package mathx import "math" // GammaInc returns the value of the incomplete gamma function (also // known as the regularized gamma function): // // P(a, x) = 1 / Γ(a) * ∫₀ˣ exp(-t) t**(a-1) dt func GammaInc(a, x float64) float64 { // Based on Numerical Recipes in C, section 6.2. if a <= 0 || x < 0 || math.IsNaN(a) || math.IsNaN(x) { return math.NaN() } if x < a+1 { // Use the series representation, which converges more // rapidly in this range. return gammaIncSeries(a, x) } else { // Use the continued fraction representation. return 1 - gammaIncCF(a, x) } } // GammaIncComp returns the complement of the incomplete gamma // function 1 - GammaInc(a, x). This is more numerically stable for // values near 0. func GammaIncComp(a, x float64) float64 { if a <= 0 || x < 0 || math.IsNaN(a) || math.IsNaN(x) { return math.NaN() } if x < a+1 { return 1 - gammaIncSeries(a, x) } else { return gammaIncCF(a, x) } } func gammaIncSeries(a, x float64) float64 { const maxIterations = 200 const epsilon = 3e-14 if x == 0 { return 0 } ap := a del := 1 / a sum := del for n := 0; n < maxIterations; n++ { ap++ del *= x / ap sum += del if math.Abs(del) < math.Abs(sum)*epsilon { return sum * math.Exp(-x+a*math.Log(x)-lgamma(a)) } } panic("a too large; failed to converge") } func gammaIncCF(a, x float64) float64 { const maxIterations = 200 const epsilon = 3e-14 raiseZero := func(z float64) float64 { if math.Abs(z) < math.SmallestNonzeroFloat64 { return math.SmallestNonzeroFloat64 } return z } b := x + 1 - a c := math.MaxFloat64 d := 1 / b h := d for i := 1; i <= maxIterations; i++ { an := -float64(i) * (float64(i) - a) b += 2 d = raiseZero(an*d + b) c = raiseZero(b + an/c) d = 1 / d del := d * c h *= del if math.Abs(del-1) < epsilon { return math.Exp(-x+a*math.Log(x)-lgamma(a)) * h } } panic("a too large; failed to converge") }