Golang 加权随机数

什么是加权随机数

所谓加权随机数,即是在随机数的基础上进行加权,每个元素的出现概率不是均等的。例如{'a': 2, 'b': 3, 'c': 5}, 那么a 出现的概率为20%,b出现的概率为30%, c 出现的概率为50%。

为什么需要加权随机数

对我而言,主要应用于一些线上抽奖活动,每个奖品出现的概率不能是均等的。所以要加权。

Golang 的支持

在github 搜索了一下,发现了有一个包。https://github.com/mroth/weightedrand 地址是这个。源码非常的精短,因为这个东西并不复杂。一般来说实现思路为:按出现频次生成一个数组,如上诉例子则为[a, a, b, b, b, c, c, c, c, c],然后将数组打乱获取需要的随机数。但这个有个问题就是数组大小等于总频次大小,一旦频次非常大,这个就会非常占内存。weightedrand 给出另外一个方案,利用二分法来获取随机数。以下是源码

package weightedrand

import (
	"math/rand"
	"sort"
)

// Choice is a generic wrapper that can be used to add weights for any object
type Choice struct {
	Item   interface{}
	Weight uint
}

// A Chooser caches many possible Choices in a structure designed to improve
// performance on repeated calls for weighted random selection.
type Chooser struct {
	data   []Choice
	totals []int
	max    int
}

// NewChooser initializes a new Chooser consisting of the possible Choices.
func NewChooser(cs ...Choice) Chooser {
	sort.Slice(cs, func(i, j int) bool {
		return cs[i].Weight < cs[j].Weight
	})// 核心点,对元素进行递增排序。
	totals := make([]int, len(cs))
	runningTotal := 0
	for i, c := range cs {
		runningTotal += int(c.Weight)
		totals[i] = runningTotal
	}
	return Chooser{data: cs, totals: totals, max: runningTotal}
}

// Pick returns a single weighted random Choice.Item from the Chooser.
func (chs Chooser) Pick() interface{} {
	r := rand.Intn(chs.max) + 1 // 使用最大值获取随机数,避免超过范围,随机生成的数需要排除0,故加1
	i := sort.SearchInts(chs.totals, r) // 核心点该方法使用二分法,找到对应的下标,如果没有则为大于该数的+1 下标,可能为len(a)即数组长度。
	return chs.data[i].Item
}

i := sort.SearchInts(chs.totals, r) // 核心点该方法使用二分法,找到对应的下标,如果没有则为大于该数的+1 下标,可能为len(a)即数组长度。如上诉例子,按该方法则会生成数组[0, 2, 5, 10],则随机数1, 2 为数组下标1, 随机数3,4,5为数组下标2。相当于随机数1,2 为a,同理随机数3,4,5 则为b,6,7,8,9,10 则为c。通过该方法比原来方法更节约内存空间,也减少了随机数计算。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章