feat: add math utils.
This commit is contained in:
134
math/gaussian.go
Normal file
134
math/gaussian.go
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
package math
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// prop
|
||||||
|
//mean: the mean (μ) of the distribution
|
||||||
|
//variance: the variance (σ^2) of the distribution
|
||||||
|
//standardDeviation: the standard deviation (σ) of the distribution
|
||||||
|
|
||||||
|
// combination
|
||||||
|
|
||||||
|
type Gaussian struct {
|
||||||
|
mean float64
|
||||||
|
variance float64
|
||||||
|
standardDeviation float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGaussian(mean, variance float64) *Gaussian {
|
||||||
|
if variance <= 0.0 {
|
||||||
|
panic("error")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Gaussian{
|
||||||
|
mean: mean,
|
||||||
|
variance: variance,
|
||||||
|
standardDeviation: math.Sqrt(float64(variance)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erfc Complementary error function
|
||||||
|
// From Numerical Recipes in C 2e p221
|
||||||
|
func Erfc(x float64) float64 {
|
||||||
|
z := math.Abs(x)
|
||||||
|
t := 1 / (1 + z/2)
|
||||||
|
r := t * math.Exp(-z*z-1.26551223+t*(1.00002368+
|
||||||
|
t*(0.37409196+t*(0.09678418+t*(-0.18628806+
|
||||||
|
t*(0.27886807+t*(-1.13520398+t*(1.48851587+
|
||||||
|
t*(-0.82215223+t*0.17087277)))))))))
|
||||||
|
if x >= 0 {
|
||||||
|
return r
|
||||||
|
} else {
|
||||||
|
return 2 - r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ierfc Inverse complementary error function
|
||||||
|
// From Numerical Recipes 3e p265
|
||||||
|
func Ierfc(x float64) float64 {
|
||||||
|
if x >= 2 {
|
||||||
|
return -100
|
||||||
|
}
|
||||||
|
if x <= 0 {
|
||||||
|
return 100
|
||||||
|
}
|
||||||
|
var xx float64
|
||||||
|
if x < 1 {
|
||||||
|
xx = x
|
||||||
|
} else {
|
||||||
|
xx = 2 - x
|
||||||
|
}
|
||||||
|
t := math.Sqrt(-2 * math.Log(xx/2))
|
||||||
|
r := -0.70711 * ((2.30753+t*0.27061)/
|
||||||
|
(1+t*(0.99229+t*0.04481)) - t)
|
||||||
|
|
||||||
|
for j := 0; j < 2; j++ {
|
||||||
|
e := Erfc(r) - xx
|
||||||
|
r += e / (1.12837916709551257*math.Exp(-(r*r)) - r*e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if x < 1 {
|
||||||
|
return r
|
||||||
|
} else {
|
||||||
|
return -r
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// fromPrecisionMean Construct a new distribution from the precision and precisionmean
|
||||||
|
func fromPrecisionMean(precision, precisionmean float64) *Gaussian {
|
||||||
|
return NewGaussian(precisionmean/precision, 1/precision)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PROB
|
||||||
|
|
||||||
|
// Pdf pdf(x): the probability density function, which describes the probability
|
||||||
|
// of a random variable taking on the value x
|
||||||
|
func (g *Gaussian) Pdf(x float64) float64 {
|
||||||
|
m := g.standardDeviation * math.Sqrt(2*math.Pi)
|
||||||
|
e := math.Exp(-math.Pow(x-g.mean, 2) / (2 * g.variance))
|
||||||
|
return e / m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cdf cdf(x): the cumulative distribution function,
|
||||||
|
// which describes the probability of a random
|
||||||
|
// variable falling in the interval (−∞, x]
|
||||||
|
func (g *Gaussian) Cdf(x float64) float64 {
|
||||||
|
return 0.5 * Erfc(-(x-g.mean)/(g.standardDeviation*math.Sqrt(2)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ppf ppf(x): the percent point function, the inverse of cdf
|
||||||
|
func (g *Gaussian) Ppf(x float64) float64 {
|
||||||
|
return g.mean - g.standardDeviation*math.Sqrt(2)*Ierfc(2*x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add add(d): returns the result of adding this and the given distribution
|
||||||
|
func (g *Gaussian) Add(d *Gaussian) *Gaussian {
|
||||||
|
return NewGaussian(g.mean+d.mean, g.variance+d.variance)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub sub(d): returns the result of subtracting this and the given distribution
|
||||||
|
func (g *Gaussian) Sub(d *Gaussian) *Gaussian {
|
||||||
|
return NewGaussian(g.mean-d.mean, g.variance+d.variance)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale scale(c): returns the result of scaling this distribution by the given constant
|
||||||
|
func (g *Gaussian) Scale(c float64) *Gaussian {
|
||||||
|
return NewGaussian(g.mean*c, g.variance*c*c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mul mul(d): returns the product distribution of this and the given distribution. If a constant is passed in the distribution is scaled.
|
||||||
|
func (g *Gaussian) Mul(d *Gaussian) *Gaussian {
|
||||||
|
precision := 1 / g.variance
|
||||||
|
dprecision := 1 / d.variance
|
||||||
|
return fromPrecisionMean(precision+dprecision, precision*g.mean+dprecision*d.mean)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Div div(d): returns the quotient distribution of this and the given distribution. If a constant is passed in the distribution is scaled by 1/d.
|
||||||
|
func (g *Gaussian) Div(d *Gaussian) *Gaussian {
|
||||||
|
precision := 1 / g.variance
|
||||||
|
dprecision := 1 / d.variance
|
||||||
|
return fromPrecisionMean(precision-dprecision, precision*g.mean-dprecision*d.mean)
|
||||||
|
}
|
||||||
47
math/gaussian_test.go
Normal file
47
math/gaussian_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package math
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGaussian(t *testing.T) {
|
||||||
|
g := NewGaussian(3.0, 1)
|
||||||
|
|
||||||
|
fmt.Printf("g: %#v\n", g)
|
||||||
|
fmt.Printf("pdf: %f\n", g.Pdf(5))
|
||||||
|
fmt.Printf("cdf: %f\n", g.Cdf(2))
|
||||||
|
fmt.Printf("ppf: %f\n", g.Ppf(5))
|
||||||
|
|
||||||
|
d := NewGaussian(0, 1)
|
||||||
|
fmt.Printf("ppf: %f, %f\n", d.Pdf(-2), 0.053991)
|
||||||
|
fmt.Printf("ppf: %f, %f\n", d.Pdf(-1), 0.241971)
|
||||||
|
fmt.Printf("ppf: %f, %f\n", d.Pdf(0), 0.398942)
|
||||||
|
fmt.Printf("ppf: %f, %f\n", d.Pdf(1), 0.241971)
|
||||||
|
fmt.Printf("ppf: %f, %f\n", d.Pdf(2), 0.053991)
|
||||||
|
|
||||||
|
fmt.Printf("cdf: %f, %f\n", d.Cdf(-1.28155), 0.1)
|
||||||
|
fmt.Printf("cdf: %f, %f\n", d.Cdf(-0.67499), 0.25)
|
||||||
|
fmt.Printf("cdf: %f, %f\n", d.Cdf(0), 0.5)
|
||||||
|
fmt.Printf("cdf: %f, %f\n", d.Cdf(0.67499), 0.75)
|
||||||
|
fmt.Printf("cdf: %f, %f\n", d.Cdf(1.28155), 0.9)
|
||||||
|
|
||||||
|
fmt.Printf("ppf: %f, %f\n", d.Ppf(0.1), -1.28155)
|
||||||
|
fmt.Printf("ppf: %f, %f\n", d.Ppf(0.25), -0.67499)
|
||||||
|
fmt.Printf("ppf: %f, %f\n", d.Ppf(0.5), 0.0)
|
||||||
|
fmt.Printf("ppf: %f, %f\n", d.Ppf(0.75), 0.67449)
|
||||||
|
fmt.Printf("ppf: %f, %f\n", d.Ppf(0.9), 1.28155)
|
||||||
|
|
||||||
|
d = d.Mul(NewGaussian(0, 1))
|
||||||
|
fmt.Printf("Mul: %#v\n", d)
|
||||||
|
fmt.Printf("%#v\n%#v", NewGaussian(1, 1).Scale(2), NewGaussian(2, 4))
|
||||||
|
|
||||||
|
d = NewGaussian(1, 1).Div(NewGaussian(1, 2))
|
||||||
|
fmt.Printf("div\n")
|
||||||
|
fmt.Printf("%#v\n%#v\n", d, NewGaussian(1, 2))
|
||||||
|
fmt.Printf("%#v\n%#v\n", NewGaussian(1, 1).Scale(1/(1.0/2.0)), NewGaussian(2, 4))
|
||||||
|
|
||||||
|
fmt.Printf("ADD:\n%#v\n%#v\n", NewGaussian(1, 1).Add(NewGaussian(1, 2)), NewGaussian(2, 3))
|
||||||
|
fmt.Printf("SUB:\n%#v\n%#v\n", NewGaussian(1, 1).Sub(NewGaussian(1, 2)), NewGaussian(0, 3))
|
||||||
|
fmt.Printf("SCALE:\n%#v\n%#v\n", NewGaussian(1, 1).Scale(2), NewGaussian(2, 4))
|
||||||
|
}
|
||||||
44
math/math.go
Normal file
44
math/math.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package math
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sign 符号函数(Sign function,简称sgn)是一个逻辑函数,用以判断实数的正负号。为避免和英文读音相似的正弦函数(sine)混淆,它亦称为Signum function。
|
||||||
|
func Sign[T int | int8 | int16 | int32 | int64 | float32 | float64](x T) T {
|
||||||
|
switch {
|
||||||
|
case x < 0: // x < 0 : -1
|
||||||
|
return -1
|
||||||
|
case x > 0: // x > 0 : +1
|
||||||
|
return +1
|
||||||
|
default: // x == 0 : 0
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mean 计算给定数据的平均值
|
||||||
|
func Mean(num []float64) float64 {
|
||||||
|
var count = len(num)
|
||||||
|
var sum float64 = 0
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
sum += num[i]
|
||||||
|
}
|
||||||
|
return sum / float64(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variance 使用平均值计算给定数据的方差
|
||||||
|
func Variance(mean float64, num []float64) float64 {
|
||||||
|
var count = len(num)
|
||||||
|
var variance float64 = 0
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
variance += math.Pow(num[i]-mean, 2)
|
||||||
|
}
|
||||||
|
return variance / float64(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StandardDeviation 使用方差计算给定数据的标准偏差
|
||||||
|
func StandardDeviation(num []float64) float64 {
|
||||||
|
var mean = Mean(num)
|
||||||
|
var variance = Variance(mean, num)
|
||||||
|
return math.Sqrt(variance)
|
||||||
|
}
|
||||||
30
math/math_test.go
Normal file
30
math/math_test.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package math
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSign(t *testing.T) {
|
||||||
|
assert.True(t, Sign(2) == 1)
|
||||||
|
assert.True(t, Sign(-2) == -1)
|
||||||
|
assert.True(t, Sign(0) == 0)
|
||||||
|
|
||||||
|
assert.True(t, Sign(int64(2)) == 1)
|
||||||
|
assert.True(t, Sign(int64(-2)) == -1)
|
||||||
|
assert.True(t, Sign(int64(0)) == 0)
|
||||||
|
|
||||||
|
assert.True(t, Sign(float32(2)) == 1)
|
||||||
|
assert.True(t, Sign(float32(-2)) == -1)
|
||||||
|
assert.True(t, Sign(float32(0)) == 0)
|
||||||
|
|
||||||
|
assert.True(t, Sign(float64(2)) == 1)
|
||||||
|
assert.True(t, Sign(float64(-2)) == -1)
|
||||||
|
assert.True(t, Sign(float64(0)) == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStandardDeviation(t *testing.T) {
|
||||||
|
assert.Equal(t, StandardDeviation([]float64{3, 5, 9, 1, 8, 6, 58, 9, 4, 10}), 15.8117045254457)
|
||||||
|
assert.Equal(t, StandardDeviation([]float64{1, 3, 5, 7, 9, 11, 2, 4, 6, 8}), 3.0397368307141326)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user