研究微信红包分配算法之Golang版
今天来看一下红包的分配,参考几年前流传的微信红包分配算法,今天用Golang实现一版,并测试验证结果。
微信红包的随机算法是怎样实现的?https://www.zhihu.com/question/22625187
红包核心算法
分配:红包里的金额怎么算?为什么出现各个红包金额相差很大?
答:随机,额度在0.01和(剩余平均值*2)之间
每次拆红包,额度范围在【0.01 ~ 剩余平均值*2】之间,这是很妙的一个设计。
比如发100元,共发10个红包,那么平均值10元,第一个拆出来的红包的额度在0.01元~20元之间波动,可以确保不会一个人把红包全领了的情况,因为最大就是剩余平均值的2倍。
比如发0.1元,共发10个红包,每个0.01元,这种就不用随机算法了,直接平均分配吧。
No bb, show your code!
设计红包结构体
//reward.go
//红包
type Reward struct {
Count int //个数
Money int //总金额(分)
RemainCount int //剩余个数
RemainMoney int //剩余金额(分)
BestMoney int //手气最佳金额
BestMoneyIndex int //手气最佳序号
MoneyList []int //拆分列表
}
- 我这里用int整型做金额计算,可以避免浮点数精度问题,展示的时候除100,就是元单位了。
核心红包随机分配算法
//reward.go
// 抢红包
func GrabReward(reward *Reward) int {
if reward.RemainCount <= 0 {
panic("RemainCount <= 0")
}
//最后一个
if reward.RemainCount - 1 == 0 {
money := reward.RemainMoney
reward.RemainCount = 0
reward.RemainMoney = 0
return money
}`
//是否可以直接0.01
if (reward.RemainMoney / reward.RemainCount) == 1 {
money := 1
reward.RemainMoney -= money
reward.RemainCount--
return money
}
//红包算法参考 https://www.zhihu.com/question/22625187
//最大可领金额 = 剩余金额的平均值x2 = (剩余金额 / 剩余数量) * 2
//领取金额范围 = 0.01 ~ 最大可领金额
maxMoney := int(reward.RemainMoney / reward.RemainCount) * 2
rand.Seed(time.Now().UnixNano())
money := rand.Intn(maxMoney)
for money == 0 {
//防止零
money = rand.Intn(maxMoney)
}
reward.RemainMoney -= money
//防止剩余金额负数
if reward.RemainMoney < 0 {
money += reward.RemainMoney
reward.RemainMoney = 0
reward.RemainCount = 0
} else {
reward.RemainCount--
}
return money
}
分配算法完成后,验证一下,用单元测试的办法验证
//reward_test.go
func TestGrabReward2(t *testing.T) {
chanReward := make(chan Reward)
rand.Seed(time.Now().UnixNano())
go func(){
//随机生成1000个红包
for i:=0; i < 1000; i++ {
//随机红包个数 1~50
count := rand.Intn(50) + 1
//随机红包总金额 1~100元
money := rand.Intn(10000) + 100
avg := money / count
for avg == 0 {
//保证金额足够分配
count = rand.Intn(50) + 1
money = rand.Intn(10000) + 100
avg = money / count
}
reward := Reward{Count: count, Money: money,
RemainCount: count, RemainMoney: money}
chanReward <- reward
}
close(chanReward)
}()
//打印拆包列表,带手气最佳
for reward := range chanReward {
for i := 0; reward.RemainCount > 0; i++ {
money := GrabReward(&reward)
if money > reward.BestMoney {
reward.BestMoneyIndex, reward.BestMoney = i, money
}
reward.MoneyList = append(reward.MoneyList, money)
}
t.Logf("总个数:%d, 总金额:%.2f", reward.Count, float32(reward.Money)/100)
for i := range reward.MoneyList {
money := reward.MoneyList[i]
isBest := ""
if reward.BestMoneyIndex == i {
isBest = " ** 手气最佳"
}
t.Logf("money_%d : (%.2f)%s\n", i+1, float32(money)/100, isBest)
}
t.Log("-------")
}
}
运行结果
reward_test.go:106: 总个数:7, 总金额:86.59
reward_test.go:113: money_1 : (16.29)
reward_test.go:113: money_2 : (4.93)
reward_test.go:113: money_3 : (22.89) ** 手气最佳
reward_test.go:113: money_4 : (3.17)
reward_test.go:113: money_5 : (20.51)
reward_test.go:113: money_6 : (0.12)
reward_test.go:113: money_7 : (18.68)
reward_test.go:115: -------
reward_test.go:106: 总个数:10, 总金额:53.79
reward_test.go:113: money_1 : (3.56)
reward_test.go:113: money_2 : (6.39)
reward_test.go:113: money_3 : (0.36)
reward_test.go:113: money_4 : (2.60)
reward_test.go:113: money_5 : (10.11)
reward_test.go:113: money_6 : (5.76)
reward_test.go:113: money_7 : (2.84)
reward_test.go:113: money_8 : (14.04) ** 手气最佳
reward_test.go:113: money_9 : (1.95)
reward_test.go:113: money_10 : (6.18)
reward_test.go:115: -------
性能测试
//性能测试
func BenchmarkGrabReward(b *testing.B) {
chanReward := make(chan *Reward, b.N)
rand.Seed(time.Now().UnixNano())
go func(){
//随机生成红包
for i:=0; i < b.N; i++ {
//随机红包个数 1~50
count := rand.Intn(50) + 1
//随机红包总金额 1~100元
money := rand.Intn(10000) + 100
avg := money / count
for avg == 0 {
//保证金额足够分配
count = rand.Intn(50) + 1
money = rand.Intn(10000) + 100
avg = money / count
}
reward := Reward{Count: count, Money: money,
RemainCount: count, RemainMoney: money}
chanReward <- &reward
}
close(chanReward)
}()
//打印拆包列表,带手气最佳
for reward := range chanReward {
for i := 0; reward.RemainCount > 0; i++ {
money := GrabReward(reward)
if money > reward.BestMoney {
reward.BestMoneyIndex, reward.BestMoney = i, money
}
reward.MoneyList = append(reward.MoneyList, money)
}
_ = fmt.Sprintf("总个数:%d, 总金额:%.2f", reward.Count, float32(reward.Money)/100)
for i := range reward.MoneyList {
money := reward.MoneyList[i]
isBest := ""
if reward.BestMoneyIndex == i {
isBest = " ** 手气最佳"
}
_ = fmt.Sprintf("money_%d : (%.2f)%s\n", i+1, float32(money)/100, isBest)
}
}
}
性能测试结果
BenchmarkGrabReward-8 4461 244842 ns/op
//4核8线的CPU运运行4461次,平均每次244842纳秒=0.244842毫秒
性能可以说是很优秀的,这是因为这个测试是纯内存计算,没有网络IO,没有存储写盘,纯粹是为了验证算法,所以性能是很高的。
完成!
研究微信红包分配算法之Golang版的更多相关文章
- java实现微信红包分配算法
红包算法分析 有人认为,抢红包的额度是从0.01到剩余平均值*N(N是一个系数,决定最大的红包值)之间,比如一共发了10块钱,发了10个红包:第一个人可以拿到(0.01~1*N)之间的一个红包值,当然 ...
- PHP微信红包生成算法的程序源码(用抛物线的模型实现)
代码如下: <?php /* * 红包生成随机算法 */ header("Content-type:text/html;charset=utf-8"); date_defau ...
- PHP用抛物线的模型实现微信红包生成算法的程序源码
<?php /* *Author:Kermit *Time:2015-8-26 *Note:红包生成随机算法 */ header("Content-type:text/html;cha ...
- PHP微信红包的算法实现探讨
header("Content-Type: text/html;charset=utf-8");//输出不乱码,你懂的 $total=10;//红包总额 $num=8;// 分成8 ...
- PHP实现微信随机红包算法和微信红包的架构设计简介
微信红包的架构设计简介: 原文:https://www.zybuluo.com/yulin718/note/93148 @来源于QCon某高可用架构群整理,整理朱玉华. 背景:有某个朋友在朋友圈咨询微 ...
- PHP实现微信红包算法和微信红包的架构设计简介
微信红包的架构设计简介: 原文:https://www.zybuluo.com/yulin718/note/93148 @来源于QCon某高可用架构群整理,整理朱玉华. 背景:有某个朋友在朋友圈咨询微 ...
- 如何用 js 实现一个类似微信红包的随机算法
如何用 js 实现一个类似微信红包的随机算法 js, 微信红包, 随机算法 "use strict"; /** * * @author xgqfrms * @license MIT ...
- 微信红包中使用的技术:AA收款+随机算法
除夕夜你领到红包了吗?有的说“我领了好几K!”“我领了几W!” 土豪何其多,苦逼也不少!有的说“我出来工作了,没压岁钱了,还要发红包”.那您有去抢微信红包吗?微信群中抢“新年红包”春节爆红.618微信 ...
- Python微信红包算法
sklearn实战-乳腺癌细胞数据挖掘(博主亲自录制视频) https://study.163.com/course/introduction.htm?courseId=1005269003& ...
随机推荐
- 案例分析丨谷歌设计冲刺 4 天决定 Clips 的功能特性
这次为大家带来Google冲刺日程案例,之前案例分析可以戳对应文字直达:ING集团.音乐流媒体Spotify.美国知名超市Target 作为一款工具类产品,谷歌十分注重产品候选人的技术背景,同时也看重 ...
- vue+bootstrap4+mybatis分页
先看效果 Springboot+Mybatis+Pagehelper分页具体实现略. Controller返回数据 @GetMapping("/findByPage") publi ...
- 256位AES加密和解密
/// <summary> /// 256位AES加密 /// </summary> /// <param name="toEncrypt">& ...
- Android Linker简介
简单介绍Android linker的基础知识,基于Android 10分支. linker的作用 考虑简单的HelloWorld程序. $ tree . . |-- jni | |-- Androi ...
- numpy 数值的修改
一.步骤 1.查找值 使用数组的索引和切片 2.修改值 直接赋值 例子 import numpy as np arr1 = np.arange(0, 24).reshape(4, 6) # 使用数组的 ...
- 关于checkbox取值的问题
今天,在做一个小界面的时候,要用到checkbox来判断用户是否选择记住密码. 本来我想这个不是挺简单吗,直接用jquery获取checkbox的checked属性值好了,如下: var rememb ...
- python练习题-9-6
本代码是<Python核心编程(第二版)>第九章的9-6练习题,完成的功能为:文件比较:比较两个文本文件是否相同,如果不同,给出第一个不同处的行号和列号. #!/usr/bin/env p ...
- typescript 为什么使用 let呢
为什么是使用let呢 因为已经厌烦了 int var 呵呵
- BZOJ 1770 lights燈
题目传送门 分析: 跑着去学了一波异或方程组高斯消元 (全世界就我不知道系列..) 然后我们可以列方程组诶 (a[1][x]&x[1])^(a[2][x]&x[2])^...^(a[n ...
- NOIP【2016普及组】 考后有感(买铅笔,回文日期,海港,魔法阵)
普及组考试已落下大幕,但我们那扑通扑通等待成绩的心仍然无法平静,先来给四道题做一个总结: 一. 买铅笔 自评难度:1星 其实这道题没有什么难度,生命之题,满分必拿,100分,保底啦~\(≧▽≦)/~ ...