mirror of
https://github.com/duke-git/lancet.git
synced 2025-09-26 19:41:20 +08:00
460 lines
9.9 KiB
Go
460 lines
9.9 KiB
Go
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
|
// Use of this source code is governed by MIT license
|
|
|
|
// Package mathutil implements some functions for math calculation.
|
|
package mathutil
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"golang.org/x/exp/constraints"
|
|
)
|
|
|
|
// Exponent calculate x^n.
|
|
// Play: https://go.dev/play/p/uF3HGNPk8wr
|
|
func Exponent(x, n int64) int64 {
|
|
if n == 0 {
|
|
return 1
|
|
}
|
|
|
|
t := Exponent(x, n/2)
|
|
|
|
if n%2 == 1 {
|
|
return t * t * x
|
|
}
|
|
|
|
return t * t
|
|
}
|
|
|
|
// Fibonacci calculate fibonacci number before n.
|
|
// Play: https://go.dev/play/p/IscseUNMuUc
|
|
func Fibonacci(first, second, n int) int {
|
|
if n <= 0 {
|
|
return 0
|
|
}
|
|
if n < 3 {
|
|
return 1
|
|
} else if n == 3 {
|
|
return first + second
|
|
} else {
|
|
return Fibonacci(second, first+second, n-1)
|
|
}
|
|
}
|
|
|
|
// Factorial calculate n!.
|
|
// Play: https://go.dev/play/p/tt6LdOK67Nx
|
|
func Factorial(n uint) uint {
|
|
if n == 0 || n == 1 {
|
|
return 1
|
|
}
|
|
|
|
result := uint(1)
|
|
for i := uint(2); i <= n; i++ {
|
|
result *= i
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// Percent calculate the percentage of value to total.
|
|
// Play: https://go.dev/play/p/s0NdFCtwuyd
|
|
func Percent(val, total float64, n int) float64 {
|
|
if total == 0 {
|
|
return float64(0)
|
|
}
|
|
tmp := val / total * 100
|
|
result := RoundToFloat(tmp, n)
|
|
|
|
return result
|
|
}
|
|
|
|
// RoundToString round off to n decimal places.
|
|
// Play: https://go.dev/play/p/kZwpBRAcllO
|
|
func RoundToString[T constraints.Float | constraints.Integer](x T, n int) string {
|
|
tmp := math.Pow(10.0, float64(n))
|
|
x *= T(tmp)
|
|
r := math.Round(float64(x))
|
|
result := strconv.FormatFloat(r/tmp, 'f', n, 64)
|
|
return result
|
|
}
|
|
|
|
// RoundToFloat round off to n decimal places.
|
|
// Play: https://go.dev/play/p/ghyb528JRJL
|
|
func RoundToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
|
|
tmp := math.Pow(10.0, float64(n))
|
|
x *= T(tmp)
|
|
r := math.Round(float64(x))
|
|
return r / tmp
|
|
}
|
|
|
|
// TruncRound round off n decimal places.
|
|
// Play: https://go.dev/play/p/aumarSHIGzP
|
|
func TruncRound[T constraints.Float | constraints.Integer](x T, n int) T {
|
|
floatStr := fmt.Sprintf("%."+strconv.Itoa(n+1)+"f", x)
|
|
temp := strings.Split(floatStr, ".")
|
|
var newFloat string
|
|
if len(temp) < 2 || n >= len(temp[1]) {
|
|
newFloat = floatStr
|
|
} else {
|
|
newFloat = temp[0] + "." + temp[1][:n]
|
|
}
|
|
result, _ := strconv.ParseFloat(newFloat, 64)
|
|
return T(result)
|
|
}
|
|
|
|
// FloorToFloat round down to n decimal places.
|
|
// Play: https://go.dev/play/p/vbCBrQHZEED
|
|
func FloorToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
|
|
tmp := math.Pow(10.0, float64(n))
|
|
x *= T(tmp)
|
|
r := math.Floor(float64(x))
|
|
return r / tmp
|
|
}
|
|
|
|
// FloorToString round down to n decimal places.
|
|
// Play: https://go.dev/play/p/Qk9KPd2IdDb
|
|
func FloorToString[T constraints.Float | constraints.Integer](x T, n int) string {
|
|
tmp := math.Pow(10.0, float64(n))
|
|
x *= T(tmp)
|
|
r := math.Floor(float64(x))
|
|
result := strconv.FormatFloat(r/tmp, 'f', n, 64)
|
|
return result
|
|
}
|
|
|
|
// CeilToFloat round up to n decimal places.
|
|
// Play: https://go.dev/play/p/8hOeSADZPCo
|
|
func CeilToFloat[T constraints.Float | constraints.Integer](x T, n int) float64 {
|
|
tmp := math.Pow(10.0, float64(n))
|
|
x *= T(tmp)
|
|
r := math.Ceil(float64(x))
|
|
return r / tmp
|
|
}
|
|
|
|
// CeilToString round up to n decimal places.
|
|
// Play: https://go.dev/play/p/wy5bYEyUKKG
|
|
func CeilToString[T constraints.Float | constraints.Integer](x T, n int) string {
|
|
tmp := math.Pow(10.0, float64(n))
|
|
x *= T(tmp)
|
|
r := math.Ceil(float64(x))
|
|
result := strconv.FormatFloat(r/tmp, 'f', n, 64)
|
|
return result
|
|
}
|
|
|
|
// Max return max value of numbers.
|
|
// Play: https://go.dev/play/p/cN8DHI0rTkH
|
|
func Max[T constraints.Ordered](items ...T) T {
|
|
if len(items) < 1 {
|
|
panic("mathutil.Max: empty list")
|
|
}
|
|
|
|
max := items[0]
|
|
|
|
for _, v := range items {
|
|
if max < v {
|
|
max = v
|
|
}
|
|
}
|
|
|
|
return max
|
|
}
|
|
|
|
// MaxBy return the maximum value of a slice using the given comparator function.
|
|
// Play: https://go.dev/play/p/pbe2MT-7DV2
|
|
func MaxBy[T any](slice []T, comparator func(T, T) bool) T {
|
|
var max T
|
|
|
|
if len(slice) == 0 {
|
|
return max
|
|
}
|
|
|
|
max = slice[0]
|
|
|
|
for i := 1; i < len(slice); i++ {
|
|
val := slice[i]
|
|
|
|
if comparator(val, max) {
|
|
max = val
|
|
}
|
|
}
|
|
|
|
return max
|
|
}
|
|
|
|
// Min return min value of numbers.
|
|
// Play: https://go.dev/play/p/21BER_mlGUj
|
|
func Min[T constraints.Ordered](items ...T) T {
|
|
if len(items) < 1 {
|
|
panic("mathutil.min: empty list")
|
|
}
|
|
|
|
min := items[0]
|
|
|
|
for _, v := range items {
|
|
if min > v {
|
|
min = v
|
|
}
|
|
}
|
|
|
|
return min
|
|
}
|
|
|
|
// MinBy return the minimum value of a slice using the given comparator function.
|
|
// Play: https://go.dev/play/p/XuJDKrDdglW
|
|
func MinBy[T any](slice []T, comparator func(T, T) bool) T {
|
|
var min T
|
|
|
|
if len(slice) == 0 {
|
|
return min
|
|
}
|
|
|
|
min = slice[0]
|
|
|
|
for i := 1; i < len(slice); i++ {
|
|
val := slice[i]
|
|
|
|
if comparator(val, min) {
|
|
min = val
|
|
}
|
|
}
|
|
|
|
return min
|
|
}
|
|
|
|
// Sum return sum of passed numbers.
|
|
// Play: https://go.dev/play/p/1To2ImAMJA7
|
|
func Sum[T constraints.Integer | constraints.Float](numbers ...T) T {
|
|
var sum T
|
|
|
|
for _, v := range numbers {
|
|
sum += v
|
|
}
|
|
|
|
return sum
|
|
}
|
|
|
|
// Average return average value of numbers.
|
|
// Play: https://go.dev/play/p/Vv7LBwER-pz
|
|
func Average[T constraints.Integer | constraints.Float](numbers ...T) float64 {
|
|
var sum float64
|
|
for _, num := range numbers {
|
|
sum += float64(num)
|
|
}
|
|
return sum / float64(len(numbers))
|
|
}
|
|
|
|
// Range creates a slice of numbers from start with specified count, element step is 1.
|
|
// Play: https://go.dev/play/p/9ke2opxa8ZP
|
|
func Range[T constraints.Integer | constraints.Float](start T, count int) []T {
|
|
size := count
|
|
if count < 0 {
|
|
size = -count
|
|
}
|
|
|
|
result := make([]T, size)
|
|
|
|
for i, j := 0, start; i < size; i, j = i+1, j+1 {
|
|
result[i] = j
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// RangeWithStep creates a slice of numbers from start to end with specified step.
|
|
// Play: https://go.dev/play/p/akLWz0EqOSM
|
|
func RangeWithStep[T constraints.Integer | constraints.Float](start, end, step T) []T {
|
|
result := []T{}
|
|
|
|
if start >= end || step == 0 {
|
|
return result
|
|
}
|
|
|
|
for i := start; i < end; i += step {
|
|
result = append(result, i)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// AngleToRadian converts angle value to radian value.
|
|
// Play: https://go.dev/play/p/CIvlICqrHql
|
|
func AngleToRadian(angle float64) float64 {
|
|
radian := angle * (math.Pi / 180)
|
|
return radian
|
|
}
|
|
|
|
// RadianToAngle converts radian value to angle value.
|
|
// Play: https://go.dev/play/p/dQtmOTUOMgi
|
|
func RadianToAngle(radian float64) float64 {
|
|
angle := radian * (180 / math.Pi)
|
|
return angle
|
|
}
|
|
|
|
// PointDistance get two points distance.
|
|
// Play: https://go.dev/play/p/RrG4JIaziM8
|
|
func PointDistance(x1, y1, x2, y2 float64) float64 {
|
|
a := x1 - x2
|
|
b := y1 - y2
|
|
c := math.Pow(a, 2) + math.Pow(b, 2)
|
|
|
|
return math.Sqrt(c)
|
|
}
|
|
|
|
// IsPrime checks if number is prime number.
|
|
// Play: https://go.dev/play/p/Rdd8UTHZJ7u
|
|
func IsPrime(n int) bool {
|
|
if n < 2 {
|
|
return false
|
|
}
|
|
|
|
for i := 2; i <= int(math.Sqrt(float64(n))); i++ {
|
|
if n%i == 0 {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// GCD return greatest common divisor (GCD) of integers.
|
|
// Play: https://go.dev/play/p/CiEceLSoAKB
|
|
func GCD[T constraints.Integer](integers ...T) T {
|
|
result := integers[0]
|
|
|
|
for k := range integers {
|
|
result = gcd(integers[k], result)
|
|
|
|
if result == 1 {
|
|
return 1
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// find greatest common divisor (GCD)
|
|
func gcd[T constraints.Integer](a, b T) T {
|
|
if b == 0 {
|
|
return a
|
|
}
|
|
|
|
return gcd(b, a%b)
|
|
}
|
|
|
|
// LCM return Least Common Multiple (LCM) of integers.
|
|
// Play: https://go.dev/play/p/EjcZxfY7G_g
|
|
func LCM[T constraints.Integer](integers ...T) T {
|
|
result := integers[0]
|
|
|
|
for k := range integers {
|
|
result = lcm(integers[k], result)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// find Least Common Multiple (LCM) via GCD.
|
|
func lcm[T constraints.Integer](a, b T) T {
|
|
if a == 0 || b == 0 {
|
|
panic("lcm function: provide non zero integers only.")
|
|
}
|
|
return a * b / gcd(a, b)
|
|
}
|
|
|
|
// Cos returns the cosine of the radian argument.
|
|
// Play: https://go.dev/play/p/Sm89LoIfvFq
|
|
func Cos(radian float64, precision ...int) float64 {
|
|
t := 1.0 / (2.0 * math.Pi)
|
|
radian *= t
|
|
radian -= 0.25 + math.Floor(radian+0.25)
|
|
radian *= 16.0 * (math.Abs(radian) - 0.5)
|
|
radian += 0.225 * radian * (math.Abs(radian) - 1.0)
|
|
|
|
if len(precision) == 1 {
|
|
return TruncRound(radian, precision[0])
|
|
}
|
|
|
|
return TruncRound(radian, 3)
|
|
}
|
|
|
|
// Sin returns the sine of the radian argument.
|
|
// Play: https://go.dev/play/p/TWMQlMywDsP
|
|
func Sin(radian float64, precision ...int) float64 {
|
|
return Cos((math.Pi/2)-radian, precision...)
|
|
}
|
|
|
|
// Log returns the logarithm of base n.
|
|
// Play: https://go.dev/play/p/_d4bi8oyhat
|
|
func Log(n, base float64) float64 {
|
|
return math.Log(n) / math.Log(base)
|
|
}
|
|
|
|
// Abs returns the absolute value of x.
|
|
// Play: https://go.dev/play/p/fsyBh1Os-1d
|
|
func Abs[T constraints.Integer | constraints.Float](x T) T {
|
|
if x < 0 {
|
|
return (-x)
|
|
}
|
|
|
|
return x
|
|
}
|
|
|
|
// Div returns the result of x divided by y.
|
|
// Play: https://go.dev/play/p/WLxDdGXXYat
|
|
func Div[T constraints.Float | constraints.Integer](x T, y T) float64 {
|
|
return float64(x) / float64(y)
|
|
}
|
|
|
|
// Variance returns the variance of numbers.
|
|
// Play: https://go.dev/play/p/uHuV4YgXf8F
|
|
func Variance[T constraints.Float | constraints.Integer](numbers []T) float64 {
|
|
n := len(numbers)
|
|
if n == 0 {
|
|
return 0
|
|
}
|
|
|
|
avg := Average(numbers...)
|
|
var sum float64
|
|
|
|
for _, v := range numbers {
|
|
sum += (float64(v) - avg) * (float64(v) - avg)
|
|
}
|
|
|
|
return sum / float64(n)
|
|
}
|
|
|
|
// StdDev returns the standard deviation of numbers.
|
|
// Play: https://go.dev/play/p/FkNZDXvHD2l
|
|
func StdDev[T constraints.Float | constraints.Integer](numbers []T) float64 {
|
|
return math.Sqrt(Variance(numbers))
|
|
}
|
|
|
|
// Permutation calculate P(n, k).
|
|
// Play: https://go.dev/play/p/MgobwH_FOxj
|
|
func Permutation(n, k uint) uint {
|
|
if n < k {
|
|
return 0
|
|
}
|
|
|
|
nFactorial := Factorial(n)
|
|
nMinusKFactorial := Factorial(n - k)
|
|
|
|
return nFactorial / nMinusKFactorial
|
|
}
|
|
|
|
// Combination calculate C(n, k).
|
|
// Play: https://go.dev/play/p/ENFQRDQUFi9
|
|
func Combination(n, k uint) uint {
|
|
if n < k {
|
|
return 0
|
|
}
|
|
|
|
nFactorial := Factorial(n)
|
|
kFactorial := Factorial(k)
|
|
nMinusKFactorial := Factorial(n - k)
|
|
|
|
return nFactorial / (kFactorial * nMinusKFactorial)
|
|
}
|