feat: random 包新增 ProbabilitySlice 函数,用于基于概率产生一个结果,当概率总和小于 1 会发生未命中的情况,概率总和大于 1 将等比缩放至 1
This commit is contained in:
parent
2712f3b98e
commit
7c9bc46a35
|
@ -1,5 +1,64 @@
|
||||||
package random
|
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 输入一个概率,返回是否命中
|
// Probability 输入一个概率,返回是否命中
|
||||||
// - 当 full 不为空时,将以 full 为基数,p 为分子,计算命中概率
|
// - 当 full 不为空时,将以 full 为基数,p 为分子,计算命中概率
|
||||||
func Probability(p int, full ...int) bool {
|
func Probability(p int, full ...int) bool {
|
||||||
|
|
|
@ -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...))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue