研究微信红包分配算法之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& ...
随机推荐
- C++Primer第五版 3.2.3节练习
练习 3.6:编写一段程序,使用范围for语句将字符串内的所有字符用X代替. #include<iostream> #include<string> using namespa ...
- 关于redis分布式锁
Lock 分布式锁 1.安全属性:互斥,不管任何时候,只有一个客户端能持有同一个锁. 2.效率属性A:不会死锁,最终一定会得到锁,就算一个持有锁的客户端宕掉或者发生网络分区. 3.效率属性B:容错,只 ...
- 记录我的 python 学习历程-Day13 匿名函数、内置函数 II、闭包
一.匿名函数 以后面试或者工作中经常用匿名函数 lambda,也叫一句话函数. 课上练习: # 正常函数: def func(a, b): return a + b print(func(4, 6)) ...
- String.valueOf(null)
public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); ...
- JMeter——分布式压测
一.Jmeter4.0分布式压测准备工作 压测注意事项 the firewalls on the systems are turned off or correct ports ...
- PHP——常量
一.什么是常量 常量可以理解为值不变的量(如圆周率)或者是常量值被定义后,在脚本的其他任何地方都不可以被改变.PHP中的常量分为自定义常量和系统常量,自定义常量是根据我们开发的需要,而定义的常量,它通 ...
- C语言进阶——编译预处理指令
编译预处理指令 • #开头的是编译预处理指令 • 它们不是C语⾔的成分,但是C语⾔程序离不开它们 • #define⽤来定义⼀个宏 #define • #define <名字> <值 ...
- react元素获取e时,点击target为空的现象
今天呢,学习react过程中,我要获取一个元素的e, checkAll=(e)=>{ console.log(e) console.log(e.target) } render() { retu ...
- 发现 一个业务管理系统 解决了 orm 和 前端框架 剩下的 是 业务逻辑 了 。 哈
解决了 orm 和 前端框架 剩下的 是 业务逻辑 了 . 哈 还有 各种 aop 组件 呢 . 大家 high 来 准备 用 fluent data 和 mysql 写一个 wcf 的 接口呢. ...
- selenium,xpath路径中引入变量
比如,我需要获取每一条微博的阅读数,总不可能所有微博都找出xpath,然后获取阅读数 找规律 “//*[@id='Pl_Official_MyProfileFeed__20']/div/div[2]/ ...