今天来看一下红包的分配,参考几年前流传的微信红包分配算法,今天用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. 再也不怕和老外交流了!我用python实现一个微信聊天翻译助手!

    前言 在前面的一篇文章如何用python“优雅的”调用有道翻译中咱们清楚的写过如何一层一层的解开有道翻译的面纱,并且笔者说过那只是脑洞的开始.现在笔者又回来了.当你遇到一些外国小哥哥小姐姐很心动.想结 ...

  2. ACM北大暑期课培训第一天

    今天是ACM北大暑期课开课的第一天,很幸运能参加这次暑期课,接下来的几天我将会每天写博客来总结我每天所学的内容.好吧下面开始进入正题: 今天第一节课,郭炜老师给我们讲了二分分治贪心和动态规划. 1.二 ...

  3. 每日一问2:堆(heap)和栈(stack)的区别

    因为这里没有明确指出堆是指数据结构还是存储方式,所以两个尝试都回答一下. 一.堆和栈作为数据结构 1.堆(heap),也叫做优先队列(priority queue),队列中允许的操作是先进先出(FIF ...

  4. [JavaScript设计模式]惰性单例模式

    惰性单例模式 之前介绍了JS中类的单例模式,这次我们讨论下单例模式的应用.在众多网站中,登录框的实现方式就是一个单例,点击一次就展示一次,所以我们可以在页面加载好的时候就创建一个登录框,点击页面上的登 ...

  5. Keystone V3 API Examples

    There are few things more useful than a set of examples when starting to work with a new API. Here a ...

  6. Spring Cloud(二):Web服务客户端之Ribbon

    上文介绍了服务如何通过Eureka实现注册,以及如何从Eureka获取已经注册的服务列表.那么拿到注册服务列表后, 如何进行服务调用?一个简单的实现是可以从被调用服务的实例列表中选择一个服务实例,通过 ...

  7. 区间dp - 全部按下一列石头

    There is one last gate between the hero and the dragon. But opening the gate isn't an easy task. The ...

  8. 转:详解G1垃圾收集器

    G1垃圾收集器入门 说明 concurrent: 并发, 多个线程协同做同一件事情(有状态) parallel: 并行, 多个线程各做各的事情(互相间无共享状态) 参考: What’s the dif ...

  9. hdu6638 线段树求最大子段和

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6638 Problem Description There are n pirate chests bu ...

  10. Jenkins-正则表达式提取Cookie

    场景描述:用户登录查询数据:登录时响应头返回Cookie,将Cookie使用正则表达式提取出来,在下一个请求“查询数据”时,在请求头中使用此Cookie 1.添加“登录”http请求 2.在后置处理器 ...