From 7c9bc46a3506d5722da7b0062b9b493f709fbb97 Mon Sep 17 00:00:00 2001 From: kercylan98 Date: Fri, 15 Sep 2023 14:11:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20random=20=E5=8C=85=E6=96=B0=E5=A2=9E=20?= =?UTF-8?q?ProbabilitySlice=20=E5=87=BD=E6=95=B0=EF=BC=8C=E7=94=A8?= =?UTF-8?q?=E4=BA=8E=E5=9F=BA=E4=BA=8E=E6=A6=82=E7=8E=87=E4=BA=A7=E7=94=9F?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E7=BB=93=E6=9E=9C=EF=BC=8C=E5=BD=93=E6=A6=82?= =?UTF-8?q?=E7=8E=87=E6=80=BB=E5=92=8C=E5=B0=8F=E4=BA=8E=201=20=E4=BC=9A?= =?UTF-8?q?=E5=8F=91=E7=94=9F=E6=9C=AA=E5=91=BD=E4=B8=AD=E7=9A=84=E6=83=85?= =?UTF-8?q?=E5=86=B5=EF=BC=8C=E6=A6=82=E7=8E=87=E6=80=BB=E5=92=8C=E5=A4=A7?= =?UTF-8?q?=E4=BA=8E=201=20=E5=B0=86=E7=AD=89=E6=AF=94=E7=BC=A9=E6=94=BE?= =?UTF-8?q?=E8=87=B3=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/random/probability.go | 59 +++++++++++++++++++++++++++++++ utils/random/probabillity_test.go | 17 +++++++++ 2 files changed, 76 insertions(+) create mode 100644 utils/random/probabillity_test.go 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...)) + } +}