| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- package rand
- import "math/rand"
- type (
- Weight[V any] struct {
- items []*item[V]
- totalScore int32
- }
- item[V any] struct {
- value V
- score int32
- }
- )
- func NewSliceWeight[V any](list []V, fn func(V) int32) *Weight[V] {
- weight := &Weight[V]{}
- for _, v := range list {
- score := fn(v)
- weight.addItem(v, score)
- }
- return weight
- }
- func NewMapWeight[K comparable, V any](maps map[K]V, fn func(V) int32) *Weight[V] {
- weight := &Weight[V]{}
- for _, v := range maps {
- score := fn(v)
- weight.addItem(v, score)
- }
- return weight
- }
- func (p *Weight[V]) addItem(v V, score int32) {
- if score < 1 {
- // pass
- return
- }
- p.totalScore += score
- p.items = append(p.items, &item[V]{
- value: v,
- score: p.totalScore,
- })
- }
- func (p *Weight[V]) SelectOne() (v V) {
- if p.totalScore < 1 {
- return
- }
- randScore := rand.Int31n(p.totalScore)
- for _, row := range p.items {
- if row.score >= randScore {
- return row.value
- }
- }
- return
- }
- func (p *Weight[V]) Select(count int) []V {
- if p.totalScore < 1 {
- return nil
- }
- s := make([]V, count)
- for i := range count {
- randScore := rand.Int31n(p.totalScore)
- for _, row := range p.items {
- if row.score >= randScore {
- s[i] = row.value
- break
- }
- }
- }
- return s
- }
- func (p *Weight[V]) All() []V {
- s := make([]V, len(p.items))
- for i, row := range p.items {
- s[i] = row.value
- }
- return s
- }
- func (p *Weight[V]) SelectIfPresent() (v V, ok bool) {
- if p.totalScore < 1 {
- return
- }
- randScore := rand.Int31n(p.totalScore)
- for _, row := range p.items {
- if row.score >= randScore {
- return row.value, true
- }
- }
- return
- }
- func (p *Weight[V]) TotalScore() int32 {
- return p.totalScore
- }
- func SelectMapping[V any, I any](p *Weight[V], num int32, mapper func(V) (I, bool)) []I {
- if mapper == nil {
- return []I{}
- }
- s := []I{}
- for i := int32(0); i < num; i++ {
- if mapValue, ok := mapper(p.SelectOne()); ok {
- s = append(s, mapValue)
- }
- }
- return s
- }
- // SelectUnique 选择不重复的元素,当请求数量大于可用元素数量时返回所有元素
- func (p *Weight[V]) SelectUnique(count int) []V {
- if p.totalScore < 1 {
- return nil
- }
- item_count := len(p.items)
- if count <= 0 {
- return []V{}
- }
- // 如果请求数量大于等于元素数量,直接返回所有元素
- if count >= item_count {
- return p.All()
- }
- // 创建副本以避免修改原始数据
- remainingItems := make([]*item[V], item_count)
- copy(remainingItems, p.items)
- result := make([]V, 0, count)
- for i := 0; i < count && len(remainingItems) > 0; i++ {
- remainingTotal := calculateTotalScore(remainingItems)
- randScore := rand.Int31n(remainingTotal)
- // 在剩余元素中查找
- selectedIndex := -1
- for idx, row := range remainingItems {
- if row.score >= randScore {
- selectedIndex = idx
- result = append(result, row.value)
- break
- }
- }
- if selectedIndex >= 0 {
- // 移除已选中的元素
- remainingItems = append(remainingItems[:selectedIndex], remainingItems[selectedIndex+1:]...)
- // 更新后续元素的分数
- updateScores(remainingItems, selectedIndex)
- }
- }
- return result
- }
- // 计算剩余元素的总分数
- func calculateTotalScore[V any](items []*item[V]) int32 {
- if len(items) == 0 {
- return 0
- }
- return items[len(items)-1].score
- }
- // 更新从指定位置开始的元素分数
- func updateScores[V any](items []*item[V], startIdx int) {
- var prevScore int32
- if startIdx > 0 {
- prevScore = items[startIdx-1].score
- } else {
- prevScore = 0
- }
- for i := startIdx; i < len(items); i++ {
- // 计算该项的实际分数值(与前一元素的差值)
- actualScore := items[i].score
- if i > 0 {
- actualScore -= items[i-1].score
- }
- // 更新累计分数
- prevScore += actualScore
- items[i].score = prevScore
- }
- }
|