Files
bl/common/utils/help.go
昔念 09a295db34
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
```
feat(utils): 添加基于权重的概率随机函数

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

函数特性:
- 支持泛型,可处理任意类型的数据
- 实现权重随机算法,通过前缀和匹配随机值
- 包含完整的参数校验和错误
2026-02-05 00:29:19 +08:00

105 lines
3.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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]) {
return i, &slice[i], true
}
}
return -1, nil, false
}
func LastFourElements[T any](s []T, n1 int) []T {
n := len(s)
if n <= n1 {
// 切片长度小于等于4时返回整个切片
return s
}
// 切片长度大于4时返回最后4个元素从n-4索引到末尾
return s[n-n1:]
}
func RemoveLast(s string) string {
if s == "" {
return ""
}
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)
}