diff --git a/utils/random/probability.go b/utils/random/probability.go index a9763ab..6740ecf 100644 --- a/utils/random/probability.go +++ b/utils/random/probability.go @@ -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 { diff --git a/utils/random/probabillity_test.go b/utils/random/probabillity_test.go new file mode 100644 index 0000000..243c100 --- /dev/null +++ b/utils/random/probabillity_test.go @@ -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...)) + } +}