vRp.CD2g_test/utils/random/probability.go

103 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package random
import (
"math/rand"
"time"
)
// ProbabilitySlice 按概率随机从切片中产生一个数据并返回命中的对象及是否未命中
// - 当总概率小于 1 将会发生未命中的情况
func ProbabilitySlice[T any](getProbabilityHandle func(data T) float64, data ...T) (hit T, miss bool) {
hit, _, miss = ProbabilitySliceIndex(getProbabilityHandle, data...)
return hit, miss
}
// ProbabilitySliceIndex 按概率随机从切片中产生一个数据并返回命中的对象及对象索引以及是否未命中
// - 当总概率小于 1 将会发生未命中的情况
func ProbabilitySliceIndex[T any](getProbabilityHandle func(data T) float64, data ...T) (hit T, index int, miss bool) {
rd := rand.New(rand.NewSource(time.Now().UnixNano()))
var total float64
for _, d := range data {
total += getProbabilityHandle(d)
}
scaleFactor := 1.0
var indexLimit = len(data)
var limitP = 0.0
if total > 1.0 {
scaleFactor = 1.0 / total
} else if total < 1.0 {
indexLimit++
limitP = 1.0 - total
}
var overlayProbability []float64
cumulativeProbability := 0.0
for i := 0; i < indexLimit; i++ {
if i < len(data) {
cumulativeProbability += getProbabilityHandle(data[i]) * scaleFactor
overlayProbability = append(overlayProbability, cumulativeProbability)
} else {
cumulativeProbability += limitP * scaleFactor
overlayProbability = append(overlayProbability, cumulativeProbability)
}
}
if total < 1.0 {
overlayProbability[len(overlayProbability)-1] = 1.0
}
var r = rd.Float64()
var i, count = 0, len(overlayProbability)
for i < count {
h := int(uint(i+count) >> 1)
if overlayProbability[h] <= r {
i = h + 1
} else {
count = h
}
}
if i >= len(data) {
return hit, -1, true
}
hit = data[i]
return hit, i, false
}
// Probability 输入一个概率,返回是否命中
// - 当 full 不为空时,将以 full 为基数p 为分子,计算命中概率
func Probability(p int, full ...int) bool {
var f = 100
if len(full) > 0 {
f = full[0]
if f <= 0 {
f = 100
} else if p > f {
return true
}
}
r := Int(1, f)
return r <= p
}
// ProbabilityChooseOne 输入一组概率,返回命中的索引
func ProbabilityChooseOne(ps ...int) int {
var f int
for _, p := range ps {
f += p
}
if f <= 0 {
panic("total probability less than or equal to 0")
}
r := Int(1, f)
for i, p := range ps {
if r <= p {
return i
}
r -= p
}
panic("probability choose one error")
}