今天来看一下红包的分配,参考几年前流传的微信红包分配算法,今天用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. Java框架之Spring01-IOC-bean配置-文件引入-注解装配

    Spring 框架,即framework.是对特定应用领域中的应用系统的部分设计和实现的整体结构.就相当于让别人帮你完成一些基础工作,它可以处理系统很多细节问题,而且框架一般是成熟,稳健的. Spri ...

  2. Go Web 编程之 模板(一)

    概述 模板引擎是 Web 编程中必不可少的一个组件.模板能分离逻辑和数据,使得逻辑简洁清晰,并且模板可复用.引用第二篇文章<程序结构>一文中的图示,我们可以看到模板引擎在 Web 程序结构 ...

  3. DevExpress 控件用法笔记(VB)

    1.ChartControl 显示条形图 ChartControl1.Titles.Clear() ChartControl1.Series.Clear() Dim db As DataTable S ...

  4. tensorflow数据读取机制tf.train.slice_input_producer 和 tf.train.batch 函数

    tensorflow中为了充分利用GPU,减少GPU等待数据的空闲时间,使用了两个线程分别执行数据读入和数据计算. 具体来说就是使用一个线程源源不断的将硬盘中的图片数据读入到一个内存队列中,另一个线程 ...

  5. 曹工说Spring Boot源码(12)-- Spring解析xml文件,到底从中得到了什么(context:component-scan完整解析)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  6. Java多线程的创建(一)

    方法一:继承Thread类实现 1.创建一个类A,并继承Thread类 2.重写A的run()方法 3.创建A的实例对象b,即创建了线程对象 4.使用b调用start()方法:启动线程(会自动调用ru ...

  7. matplotlib 散点图

    一.特点 离散的数据,查看分布规律,走向趋势 二.使用 1.核心 plt.scatter(x, y) # x为x轴的数据,可迭代对象,必须是数字 # y为y轴的数据,可迭代对象,必须是数字 # x和y ...

  8. IDEA新建maven项目没有webapp目录解决方法

    转载地址:https://www.cnblogs.com/oldzhang1222/p/10429827.html 先创建的页面修改路径 修改路径如下 添加并完善路径\src\main\webapp ...

  9. selenium常见的元素定位方法

    一.获取元素 1)通过谷歌浏览器自动的工具访问百度首页,我们可以看到,页面上的元素都是由一行行的代码组成的,它们之间有层级地组织起来,每个元素之间都有不同的标签和值,我们可以通过这些不同的标签和值来找 ...

  10. 从maven安装配置到idea成功创建maven项目

    在文章开始之前,我还是想安抚下你躁动的心情,说实话这一套操作下来的确花了我不少时间,的确头疼. 不过对于现在在看文章的你,我还是想提倡多多尝试,耐心哈,别砸键盘......废话少说切入正题 一. ma ...