97 lines
2.1 KiB
Go
97 lines
2.1 KiB
Go
// 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")
|
|
}
|