```
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful

feat(utils): 添加基于权重的概率随机函数

添加了两个泛型函数用于实现权重随机选择功能:
- RandomByIntProbs: 核心函数,支持任意类型元素数组和int型概率数组
- RandomByProbs: 封装函数,兼容string型概率数组输入

函数特性:
- 支持泛型,可处理任意类型的数据
- 实现权重随机算法,通过前缀和匹配随机值
- 包含完整的参数校验和错误
This commit is contained in:
昔念
2026-02-05 00:29:19 +08:00
parent e65b50dd33
commit 09a295db34
3 changed files with 100 additions and 76 deletions

View File

@@ -1,5 +1,12 @@
package utils
import (
"errors"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/grand"
)
func FindWithIndex[T any](slice []T, predicate func(item T) bool) (int, *T, bool) {
for i := range slice {
if predicate(slice[i]) {
@@ -24,3 +31,74 @@ func RemoveLast(s string) string {
runes := []rune(s)
return string(runes[:len(runes)-1])
}
// ************************** 函数1核心函数双数组+int型概率泛型**************************
// randomByIntProbs 核心概率计算函数:接收任意类型元素数组 + int型概率数组实现权重随机
// T any支持任意类型的元素切片职责单一只处理纯int概率的核心逻辑
func RandomByIntProbs[T any](natureSet []T, probs []int) (T, error) {
// 定义泛型零值,用于错误返回
var zeroT T
// 1. 合法性校验:元素数组为空 或 概率数组与元素数组长度不匹配
if len(natureSet) == 0 {
return zeroT, errors.New("natureSet is empty")
}
if len(probs) == 0 || len(natureSet) != len(probs) {
// 长度不匹配,降级为等概率随机(兼容原有逻辑)
return natureSet[grand.Intn(len(natureSet))], nil
}
// 2. 校验概率值非负,并计算总概率
totalProb := 0
for i, p := range probs {
if p < 0 {
return zeroT, errors.New("invalid prob value: index " + gconv.String(i) + " (must be non-negative integer)")
}
totalProb += p
}
// 3. 总概率为0降级为等概率随机
if totalProb == 0 {
return natureSet[grand.Intn(len(natureSet))], nil
}
// 4. 计算前缀和(权重随机核心逻辑)
prefixSum := make([]int, len(probs))
prefixSum[0] = probs[0]
for i := 1; i < len(probs); i++ {
prefixSum[i] = prefixSum[i-1] + probs[i]
}
// 5. 生成随机数匹配前缀和,返回对应元素
randVal := grand.Intn(totalProb)
for i, sum := range prefixSum {
if randVal < sum {
return natureSet[i], nil
}
}
// 极端情况兜底,返回第一个元素
return natureSet[0], nil
}
// ************************** 函数2封装函数兼容string型概率泛型**************************
// randomByProbs 兼容原有入参格式:接收任意类型元素数组 + string型概率数组
// 内部完成 string[] -> int[] 的转换,然后调用核心函数 randomByIntProbs
func RandomByProbs[T any](natureSet []T, probs []string) (T, error) {
//var zeroT T
// 1. 若string概率数组为空直接调用核心函数核心函数会处理降级逻辑
if len(probs) == 0 {
return RandomByIntProbs(natureSet, []int{})
}
// 2. string[] 转换为 int[](使用 gconv.Int 完成转换)
probInts := make([]int, len(probs))
for i, pStr := range probs {
// gconv.Int 灵活转换失败返回0
probInts[i] = gconv.Int(pStr)
}
// 3. 调用核心函数,复用概率计算逻辑
return RandomByIntProbs(natureSet, probInts)
}