国科大学习生活(期末复习资料、课程大作业解析、学习文档等): 文章专栏(点击跳转)
微信红包随机算法(含代码实现)
1. 背景
昨天晚上我妈在微信家庭群里发了一个4人拼手气红包,总金额520RMB,本来是一场平平无奇的抢红包事件,直到抢红包结果出现:
可以发现,不仅有两个人都抢了200¥,而且很明显的是我们的金额都没有小数点!于是我发了一个小额拼手气红包(4人共10¥),结果出现:
解释一下为什么这个红包被设置成4人共10¥的形式:
因为如果是没有小数点的话,那4个人的金额完全可以是(例如):1、2、3、4元。
好家伙,不仅有了小数点,还是两位!让我对微信红包随机算法的随机性产生了怀疑,或者说,他在对于出现不同规模金额的红包的情况时,会做相应不同的处理。
于是我开始思考微信红包的随机算法到底是什么样子的,然后就有了今天这篇文章。
2. 资料搜集
在Edge上搜索微信红包随机算法,弹出来的第一条文章就是:
最全解密微信红包随机算法(含代码实现)-腾讯云开发者社区-腾讯云 (tencent.com)
这是目前能找到的仅有的一份,有微信团队人员参与的微信红包算法技术要点的讨论资料。分享于2015年,差不多是微信红包刚火没多久,大概是微信技术团队的人当时没有现在这些技术之外的顾虑,所以作了有限的分享。
对与文章所说:
问:关于分配算法,红包里的金额怎么算?为什么出现各个红包金额相差很大?
答:随机,额度在 0.01 和剩余平均值 2 之间。 例如:发 100 块钱,总共 10 个红包,那么平均值是 10 块钱一个,那么发出来的红包的额度在 0.01元~20元之间波动。
当前面 3 个红包总共被领了 40 块钱时,剩下 60 块钱,总共 7 个红包,那么这 7 个红包的额度在:0.01~(60/7 * 2)=17.14之间。
注意:这里的算法是每被抢一个后,剩下的会再次执行上面的这样的算法(不知基于什么样的考虑)。
这样算下去,会超过最开始的全部金额,因此到了最后面如果不够这么算,那么会采取如下算法:保证剩余用户能拿到最低1分钱即可。
如果前面的人手气不好,那么后面的余额越多,红包额度也就越多,因此实际概率一样的。
3. 代码演示
从上面资料中可以看到,先抢的人是有机会抢到更大数额的红包的(但并不意味着先抢就能抢到大额红包),根据资料提到的这个“随机,额度在 0.01 和剩余平均值 2 之间”,以及查找相关资料可以得微信随机红包的算法实现的确是真正的随机,其使用了一种称为“二倍均值法”的算法来生成随机红包金额。下面是它的实现过程:
- 发红包的人设置红包总金额和红包个数。
- 算法开始执行,生成一个随机红包金额的序列。
- 算法首先计算出每个红包的最大和最小金额。
- 然后,随机生成一个红包的金额,范围在最小和最大之间。
- 这个随机值将作为其中一个红包的金额。
- 接下来,算法将红包总金额减去这个随机值,并将红包个数减一。
- 重复步骤4到步骤6直到所有红包都被生成。
- 最后一个红包的金额等于剩余的红包金额。
是不是还是有点懵?别担心,接下来上代码!
根据其核心逻辑,我用JAVA将其实现,代码如下:
package LeetCode;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class RandomRedPacketGenerator {
public List<Double> generateRandomRedPackets(double totalAmount, int numPackets) {
List<Double> packets = new ArrayList<>();
Random random = new Random();
int curNumPackets =numPackets - 1;
for (int i = 0; i < curNumPackets; i++) {
// 计算最小和最大金额
double minAmount = 0.01; // 每个红包至少0.01元
double maxAmount = totalAmount / numPackets * 2;
// 随机生成红包金额
double amount = round(random.nextDouble() * (maxAmount - minAmount) + minAmount);
packets.add(amount);
// 更新红包总金额和个数
totalAmount -= amount;
numPackets--;
}
// 最后一个红包的金额等于剩余的红包金额
packets.add(round(totalAmount));
return packets;
}
private double round(double value) {
double scale = Math.pow(10, 2);
return Math.round(value * scale) / scale;
}
public static void main(String[] args) {
double totalAmount = 10.0; // 红包总金额为10元
int numPackets = 6; // 红包个数为6
RandomRedPacketGenerator generator = new RandomRedPacketGenerator();
List<Double> redPackets = generator.generateRandomRedPackets(totalAmount, numPackets);
System.out.println(redPackets);
}
}
以上代码使用java.util.Random类来生成随机金额,并使用一个循环来生成指定数量的红包金额。每个红包的金额都在最小和最大金额之间随机生成,并通过调用round()方法进行舍入保留指定的小数位数。
测试小额拼手气红包(6包10元):
测试大额拼手气红包(4包520元):
很明显,核心随机算法实现的仅仅是提供了一种红包分发策略,但实际上,通过
上边这张图片不难看出----4人红包金额均没有小数点,所以真实的微信随机红包算法一定还涉及其他因素,例如更复杂的金额分配优化等。
4. 结论
真正的微信红包随机算法一定不仅仅是做到随机分配金额那么简单,它在使用随机红包算法的核心—“二倍均值法”的基础上,一定又加入了诸如金额小数点分配优化等操作。
要再次说明的是,上述有微信团队人员参与的微信红包算法技术要点的讨论资料。是分享于2015年的,差不多是微信红包刚火没多久,大概是微信技术团队的人当时没有现在这些技术之外的顾虑,所以作了有限的分享。距离今天已经8年有余,所以有可能当初的随机算法算出的随机红包就是会出现小数点的情况,后来才慢慢优化了金额分配操作;当然,也可能是当时就已经存在了金额分配优化、时间戳种子等辅助算法,只是我们不知道而已。
资料中其实是存在当时分享的算法源码的,只不过下载需要money而我只不过是一个小小的好奇心并且考虑到那已经是15年版本的算法,所以没有去下载(手动狗头)。
探索完毕!希望微信赶紧开源吧 ,让我好好看看他的源码(再次手动狗头)。
16点31分 2023年12月23日
若有错误,欢迎大家积极讨论交流!