// 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" func lgamma(x float64) float64 { y, _ := math.Lgamma(x) return y } // Beta returns the value of the complete beta function B(a, b). func Beta(a, b float64) float64 { // B(x,y) = Γ(x)Γ(y) / Γ(x+y) return math.Exp(lgamma(a) + lgamma(b) - lgamma(a+b)) } // BetaInc returns the value of the regularized incomplete beta // function Iₓ(a, b). // // This is not to be confused with the "incomplete beta function", // which can be computed as BetaInc(x, a, b)*Beta(a, b). // // If x < 0 or x > 1, returns NaN. func BetaInc(x, a, b float64) float64 { // Based on Numerical Recipes in C, section 6.4. This uses the // continued fraction definition of I: // // (xᵃ*(1-x)ᵇ)/(a*B(a,b)) * (1/(1+(d₁/(1+(d₂/(1+...)))))) // // where B(a,b) is the beta function and // // d_{2m+1} = -(a+m)(a+b+m)x/((a+2m)(a+2m+1)) // d_{2m} = m(b-m)x/((a+2m-1)(a+2m)) if x < 0 || x > 1 { return math.NaN() } bt := 0.0 if 0 < x && x < 1 { // Compute the coefficient before the continued // fraction. bt = math.Exp(lgamma(a+b) - lgamma(a) - lgamma(b) + a*math.Log(x) + b*math.Log(1-x)) } if x < (a+1)/(a+b+2) { // Compute continued fraction directly. return bt * betacf(x, a, b) / a } else { // Compute continued fraction after symmetry transform. return 1 - bt*betacf(1-x, b, a)/b } } // betacf is the continued fraction component of the regularized // incomplete beta function Iₓ(a, b). func betacf(x, a, b 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 } c := 1.0 d := 1 / raiseZero(1-(a+b)*x/(a+1)) h := d for m := 1; m <= maxIterations; m++ { mf := float64(m) // Even step of the recurrence. numer := mf * (b - mf) * x / ((a + 2*mf - 1) * (a + 2*mf)) d = 1 / raiseZero(1+numer*d) c = raiseZero(1 + numer/c) h *= d * c // Odd step of the recurrence. numer = -(a + mf) * (a + b + mf) * x / ((a + 2*mf) * (a + 2*mf + 1)) d = 1 / raiseZero(1+numer*d) c = raiseZero(1 + numer/c) hfac := d * c h *= hfac if math.Abs(hfac-1) < epsilon { return h } } panic("betainc: a or b too big; failed to converge") }