feat: random 包新增 ProbabilitySlice 函数,用于基于概率产生一个结果,当概率总和小于 1 会发生未命中的情况,概率总和大于 1 将等比缩放至 1

This commit is contained in:
kercylan98 2023-09-15 14:11:30 +08:00
parent 2712f3b98e
commit 7c9bc46a35
2 changed files with 76 additions and 0 deletions

View File

@ -1,5 +1,64 @@
package random
import (
"math/rand"
"time"
)
// ProbabilitySlice 按概率随机从切片中产生一个数据并返回命中的对象及是否未命中
// - 当总概率小于 1 将会发生未命中的情况
func ProbabilitySlice[T any](getProbabilityHandle func(data T) float64, data ...T) (hit T, 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, true
}
hit = data[i]
return hit, false
}
// Probability 输入一个概率,返回是否命中
// - 当 full 不为空时,将以 full 为基数p 为分子,计算命中概率
func Probability(p int, full ...int) bool {

View File

@ -0,0 +1,17 @@
package random_test
import (
"github.com/kercylan98/minotaur/utils/random"
"testing"
)
func TestProbabilitySlice(t *testing.T) {
var awards = []int{1, 2, 3, 4, 5, 6, 7}
var probability = []float64{0.1, 2, 0.1, 0.1, 0.1, 0.1, 0.1}
for i := 0; i < 50; i++ {
t.Log(random.ProbabilitySlice(func(data int) float64 {
return probability[data-1]
}, awards...))
}
}