今天来看一下红包的分配,参考几年前流传的微信红包分配算法,今天用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版的更多相关文章

  1. java实现微信红包分配算法

    红包算法分析 有人认为,抢红包的额度是从0.01到剩余平均值*N(N是一个系数,决定最大的红包值)之间,比如一共发了10块钱,发了10个红包:第一个人可以拿到(0.01~1*N)之间的一个红包值,当然 ...

  2. PHP微信红包生成算法的程序源码(用抛物线的模型实现)

    代码如下: <?php /* * 红包生成随机算法 */ header("Content-type:text/html;charset=utf-8"); date_defau ...

  3. PHP用抛物线的模型实现微信红包生成算法的程序源码

    <?php /* *Author:Kermit *Time:2015-8-26 *Note:红包生成随机算法 */ header("Content-type:text/html;cha ...

  4. PHP微信红包的算法实现探讨

    header("Content-Type: text/html;charset=utf-8");//输出不乱码,你懂的 $total=10;//红包总额 $num=8;// 分成8 ...

  5. PHP实现微信随机红包算法和微信红包的架构设计简介

    微信红包的架构设计简介: 原文:https://www.zybuluo.com/yulin718/note/93148 @来源于QCon某高可用架构群整理,整理朱玉华. 背景:有某个朋友在朋友圈咨询微 ...

  6. PHP实现微信红包算法和微信红包的架构设计简介

    微信红包的架构设计简介: 原文:https://www.zybuluo.com/yulin718/note/93148 @来源于QCon某高可用架构群整理,整理朱玉华. 背景:有某个朋友在朋友圈咨询微 ...

  7. 如何用 js 实现一个类似微信红包的随机算法

    如何用 js 实现一个类似微信红包的随机算法 js, 微信红包, 随机算法 "use strict"; /** * * @author xgqfrms * @license MIT ...

  8. 微信红包中使用的技术:AA收款+随机算法

    除夕夜你领到红包了吗?有的说“我领了好几K!”“我领了几W!” 土豪何其多,苦逼也不少!有的说“我出来工作了,没压岁钱了,还要发红包”.那您有去抢微信红包吗?微信群中抢“新年红包”春节爆红.618微信 ...

  9. Python微信红包算法

    sklearn实战-乳腺癌细胞数据挖掘(博主亲自录制视频) https://study.163.com/course/introduction.htm?courseId=1005269003& ...

随机推荐

  1. 2018湘潭邀请赛 AFK题解 其他待补...

    A.HDU6276:Easy h-index Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/O ...

  2. vue设置选中时的样式名称

    第一种方式:在router中全局设置 export default new Router({ mode:'history', linkActiveClass:'index', routes: [ { ...

  3. 详细解析Java虚拟机的栈帧结构

    欢迎关注微信公众号:万猫学社,每周一分享Java技术干货. 什么是栈帧? 正如大家所了解的,Java虚拟机的内存区域被划分为程序计数器.虚拟机栈.本地方法栈.堆和方法区.(什么?你还不知道,赶紧去看看 ...

  4. php部署后错误排查流程

    未使用框架的php程序不可用时,没有框架提供的调试信息,因此要按照请求的整个生命周期来调试程序, 具体错误依次排查网络,服务器,环境,代码的步骤层层深入,最终定位到错误的发生点. 1 访问程序部署的服 ...

  5. Vs中提交了代码但是不想推送到Git中

    1:首先就是我fix code 是要提交上去的,所以我就开始提交呢,但是,一看提交后,还没有推送到git就是现在下面的这个状态 上面这个是==> 这是先新增的文件,第一步.但是第一步就差推送了, ...

  6. 通过VS2019使用Web部署发布.net core程序

    服务器:Windows Server2012R2 服务器已安装好IIS 需要启用Web Management Service  与 Web部署代理服务 服务器默认是没有Web部署代理服务的  需要安装 ...

  7. Java.前端.Layer.open.btn验证无效

    今天遇到了一个很可笑的问题,在.Layer弹窗open中设置了多个按钮,只有yes按钮有效,btn2点击后直接关闭弹窗,排查了2个小时后终于解决,就是btn2要return false! var in ...

  8. Java Swing图形界面开发

    本文转自xietansheng的CSDN博客内容,这是自己见过的最通俗易懂.最适合快速上手做Java GUI开发的教程了,这里整合一下作为自己以后复习的笔记: 原文地址:https://blog.cs ...

  9. [ZJOI2006]书架(权值splay)

    [ZJOI2006]书架(luogu) Description 题目描述 小T有一个很大的书柜.这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列.她用1到n的正整数给每本书都编了号. 小T在看 ...

  10. qt creator源码全方面分析(0)

    本人主攻C++和Qt. 上两天刚研究完Qt install framework(IFW)应用程序安装框架. google没发现有正儿八经的官方文档的翻译,我就进行了翻译哈!! 系列文章具体见:http ...