研究微信红包分配算法之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& ... 
随机推荐
- Exceptionless运用结果
			一.后台页面功能 列表菜单 SubmitLog - 记录一般日志 log Messages SubmitException - 记录一次日志 Exceptions SubmitNotFound - 4 ... 
- 升级添加到现有iOS Xcode项目的Flutter
			如果你在2019年8月之前将Flutter添加到现有iOS项目,本文值得你一看. 在2019年7月30日,合并合并请求flutter / flutter#36793之前Flutter 1.8.4-pr ... 
- Spring(一)开篇
			目录 1.Spring 介绍 2.Spring 框架的演变 3.Spring 整体架构 Core Container Data Access/Integration Web AOP Test 最后 1 ... 
- 微信授权流程和JSSDK调用流程
			概念理解 业务域名:当前业务使用的是哪个网站,好处:设置业务域名后,在微信内访问该域名下页面时,不会被重新排版.不出现“防欺诈盗号,请误支付或输入qq密码”的提示,微信认为该域名是安全的,客户也不觉得 ... 
- 一个由"2020年1月7日 京东出现的重大 Bug 漏洞"引起的思考...
			2020年1月7日,京东由于优惠券设置错误,导致大量产品以0元或者超低价成交,并且发货.网传小家电被薅24万件,损失损失金额高达7000多万.很多网友表示收到货了,在网上晒出到货截图.下面为购买截图: ... 
- APICloud打开三方地图整合
			一直想系统的整理打开地图的方法,今天抽时间把了百度,高德,腾讯,苹果自带地图都整理出来了,闲话不多说,直接上干货 ------------------------------------------- ... 
- importlib 根据字符串导入模块
			应用: Django中间件,rest framework 组件的全局配置文件 import importlib path = "abc.def.foo" module_path,c ... 
- hbase伪分布式安装以及实例演示
			参考指路:https://www.cnblogs.com/wang-jx/p/9672072.html (包含实例演示,这里就不copy人家的心血了) 1.下载对应安装包解压 1.1下载 同样建议选择 ... 
- sqlserver2008:在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。未找到或无法访问服务器。请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。
			在开始菜单中找到: 进入,点击左侧SQL Server服务, 将SQL Server(MSSQL.....)服务开启, 即可成功连接. 
- 英语学习app——Alpha发布2
			英语学习app--Alpha发布1 这个作业属这个作业属于哪个课程 https://edu.cnblogs.com/campus/xnsy/GeographicInformationScience/ ... 
