mirror of
https://github.com/gonum/gonum.git
synced 2025-10-10 17:40:30 +08:00
249 lines
3.9 KiB
Go
249 lines
3.9 KiB
Go
// Copyright ©2016 The Gonum Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
/*
|
|
* Cephes Math Library Release 2.4: March,1996
|
|
* Copyright 1984, 1996 by Stephen L. Moshier
|
|
*/
|
|
|
|
package cephes
|
|
|
|
import "math"
|
|
|
|
// Incbi computes the inverse of the regularized incomplete beta integral.
|
|
func Incbi(aa, bb, yy0 float64) float64 {
|
|
var a, b, y0, d, y, x, x0, x1, lgm, yp, di, dithresh, yl, yh, xt float64
|
|
var i, rflg, dir, nflg int
|
|
|
|
i = 0
|
|
if yy0 <= 0 {
|
|
return (0.0)
|
|
}
|
|
if yy0 >= 1.0 {
|
|
return (1.0)
|
|
}
|
|
x0 = 0.0
|
|
yl = 0.0
|
|
x1 = 1.0
|
|
yh = 1.0
|
|
nflg = 0
|
|
|
|
if aa <= 1.0 || bb <= 1.0 {
|
|
dithresh = 1.0e-6
|
|
rflg = 0
|
|
a = aa
|
|
b = bb
|
|
y0 = yy0
|
|
x = a / (a + b)
|
|
y = Incbet(a, b, x)
|
|
goto ihalve
|
|
} else {
|
|
dithresh = 1.0e-4
|
|
}
|
|
// Approximation to inverse function
|
|
yp = -Ndtri(yy0)
|
|
|
|
if yy0 > 0.5 {
|
|
rflg = 1
|
|
a = bb
|
|
b = aa
|
|
y0 = 1.0 - yy0
|
|
yp = -yp
|
|
} else {
|
|
rflg = 0
|
|
a = aa
|
|
b = bb
|
|
y0 = yy0
|
|
}
|
|
|
|
lgm = (yp*yp - 3.0) / 6.0
|
|
x = 2.0 / (1.0/(2.0*a-1.0) + 1.0/(2.0*b-1.0))
|
|
d = yp*math.Sqrt(x+lgm)/x - (1.0/(2.0*b-1.0)-1.0/(2.0*a-1.0))*(lgm+5.0/6.0-2.0/(3.0*x))
|
|
d = 2.0 * d
|
|
if d < minLog {
|
|
// mtherr("incbi", UNDERFLOW)
|
|
x = 0
|
|
goto done
|
|
}
|
|
x = a / (a + b*math.Exp(d))
|
|
y = Incbet(a, b, x)
|
|
yp = (y - y0) / y0
|
|
if math.Abs(yp) < 0.2 {
|
|
goto newt
|
|
}
|
|
|
|
/* Resort to interval halving if not close enough. */
|
|
ihalve:
|
|
|
|
dir = 0
|
|
di = 0.5
|
|
for i = 0; i < 100; i++ {
|
|
if i != 0 {
|
|
x = x0 + di*(x1-x0)
|
|
if x == 1.0 {
|
|
x = 1.0 - machEp
|
|
}
|
|
if x == 0.0 {
|
|
di = 0.5
|
|
x = x0 + di*(x1-x0)
|
|
if x == 0.0 {
|
|
// mtherr("incbi", UNDERFLOW)
|
|
goto done
|
|
}
|
|
}
|
|
y = Incbet(a, b, x)
|
|
yp = (x1 - x0) / (x1 + x0)
|
|
if math.Abs(yp) < dithresh {
|
|
goto newt
|
|
}
|
|
yp = (y - y0) / y0
|
|
if math.Abs(yp) < dithresh {
|
|
goto newt
|
|
}
|
|
}
|
|
if y < y0 {
|
|
x0 = x
|
|
yl = y
|
|
if dir < 0 {
|
|
dir = 0
|
|
di = 0.5
|
|
} else if dir > 3 {
|
|
di = 1.0 - (1.0-di)*(1.0-di)
|
|
} else if dir > 1 {
|
|
di = 0.5*di + 0.5
|
|
} else {
|
|
di = (y0 - y) / (yh - yl)
|
|
}
|
|
dir += 1
|
|
if x0 > 0.75 {
|
|
if rflg == 1 {
|
|
rflg = 0
|
|
a = aa
|
|
b = bb
|
|
y0 = yy0
|
|
} else {
|
|
rflg = 1
|
|
a = bb
|
|
b = aa
|
|
y0 = 1.0 - yy0
|
|
}
|
|
x = 1.0 - x
|
|
y = Incbet(a, b, x)
|
|
x0 = 0.0
|
|
yl = 0.0
|
|
x1 = 1.0
|
|
yh = 1.0
|
|
goto ihalve
|
|
}
|
|
} else {
|
|
x1 = x
|
|
if rflg == 1 && x1 < machEp {
|
|
x = 0.0
|
|
goto done
|
|
}
|
|
yh = y
|
|
if dir > 0 {
|
|
dir = 0
|
|
di = 0.5
|
|
} else if dir < -3 {
|
|
di = di * di
|
|
} else if dir < -1 {
|
|
di = 0.5 * di
|
|
} else {
|
|
di = (y - y0) / (yh - yl)
|
|
}
|
|
dir -= 1
|
|
}
|
|
}
|
|
// mtherr("incbi", PLOSS)
|
|
if x0 >= 1.0 {
|
|
x = 1.0 - machEp
|
|
goto done
|
|
}
|
|
if x <= 0.0 {
|
|
// mtherr("incbi", UNDERFLOW)
|
|
x = 0.0
|
|
goto done
|
|
}
|
|
|
|
newt:
|
|
if nflg > 0 {
|
|
goto done
|
|
}
|
|
nflg = 1
|
|
lgm = lgam(a+b) - lgam(a) - lgam(b)
|
|
|
|
for i = 0; i < 8; i++ {
|
|
/* Compute the function at this point. */
|
|
if i != 0 {
|
|
y = Incbet(a, b, x)
|
|
}
|
|
if y < yl {
|
|
x = x0
|
|
y = yl
|
|
} else if y > yh {
|
|
x = x1
|
|
y = yh
|
|
} else if y < y0 {
|
|
x0 = x
|
|
yl = y
|
|
} else {
|
|
x1 = x
|
|
yh = y
|
|
}
|
|
if x == 1.0 || x == 0.0 {
|
|
break
|
|
}
|
|
/* Compute the derivative of the function at this point. */
|
|
d = (a-1.0)*math.Log(x) + (b-1.0)*math.Log(1.0-x) + lgm
|
|
if d < minLog {
|
|
goto done
|
|
}
|
|
if d > maxLog {
|
|
break
|
|
}
|
|
d = math.Exp(d)
|
|
/* Compute the step to the next approximation of x. */
|
|
d = (y - y0) / d
|
|
xt = x - d
|
|
if xt <= x0 {
|
|
y = (x - x0) / (x1 - x0)
|
|
xt = x0 + 0.5*y*(x-x0)
|
|
if xt <= 0.0 {
|
|
break
|
|
}
|
|
}
|
|
if xt >= x1 {
|
|
y = (x1 - x) / (x1 - x0)
|
|
xt = x1 - 0.5*y*(x1-x)
|
|
if xt >= 1.0 {
|
|
break
|
|
}
|
|
}
|
|
x = xt
|
|
if math.Abs(d/x) < 128.0*machEp {
|
|
goto done
|
|
}
|
|
}
|
|
/* Did not converge. */
|
|
dithresh = 256.0 * machEp
|
|
goto ihalve
|
|
|
|
done:
|
|
|
|
if rflg > 0 {
|
|
if x <= machEp {
|
|
x = 1.0 - machEp
|
|
} else {
|
|
x = 1.0 - x
|
|
}
|
|
}
|
|
return (x)
|
|
}
|
|
|
|
func lgam(a float64) float64 {
|
|
lg, _ := math.Lgamma(a)
|
|
return lg
|
|
}
|