-
Notifications
You must be signed in to change notification settings - Fork 177
Open
Description
不多解释,直接看代码~~
const lotteryBase = 10000
type LotteryPrize struct {
Id int64 // 奖品ID
Name string // 奖品名
Rate float64 // 中奖概率
Type int8 // 自行配置,比如实物奖、虚拟奖、谢谢惠顾之类的
probability int64 // 概率数 * lotteryBase 未导出, 只有内部程序需要使用它
StockNum int64 // 库存数
}
func GetLotteryPrize(lotteryPrizes []*LotteryPrize) (prize *LotteryPrize, err error) {
var allProbability int64 //所有奖品的概率数总和
var totalStockNum int64 // 所有奖品总库存
// 奖品按照概率数从小到大排序
sort.SliceStable(lotteryPrizes, func(i, j int) bool {
return lotteryPrizes[i].probability < lotteryPrizes[j].probability
})
// 也可以把奖品Slice打成乱序的,更公平些
// RandSlice(lotteryPrizes)
for _, prize := range lotteryPrizes {
prize.probability = int64(prize.Rate * lotteryBase)
allProbability += prize.probability
totalStockNum += prize.StockNum
}
if totalStockNum == 0 || allProbability > lotteryBase {
err = errors.New("奖品配置错误!")
return nil, err
}
for _, prize := range lotteryPrizes {
rand.Seed(time.Now().UnixNano())
// 生成随机数
lotteryNum := rand.Int63n(allProbability) + 1
// 如果lotteryNum在奖品的probability内且奖品有库存则抽中金品
if lotteryNum <= prize.probability && prize.StockNum >= 1 {
return prize, nil
}
// 如果未抽中,缩减概率总数(allProbability - prize.probability)
// 这样 筛选到最终,总会有一个数满足要求。
// 不想每次都给用户发奖的话就把谢谢惠顾也设置成奖品,概率调成最大
allProbability -= prize.probability
}
return
}
func RandSlice(slice interface{}) {
rv := reflect.ValueOf(slice)
if rv.Type().Kind() != reflect.Slice {
return
}
length := rv.Len()
if length < 2 {
return
}
swap := reflect.Swapper(slice)
rand.Seed(time.Now().Unix())
for i := length - 1; i >= 0; i-- {
j := rand.Intn(length)
swap(i, j)
}
return
}Metadata
Metadata
Assignees
Labels
No labels