Java 实现微信红包分配算法
·
红包算法分析与实现
1. 算法原理分析
在知乎和一些其他博客中,很多人都提出了自己的观点。我选取其中的一个算法进行分析。
1.1 基本思路
有人认为,抢红包的额度是从0.01到剩余平均值×N(N是一个系数,决定最大的红包值)之间。例如:一共发了10块钱,发了10个红包,第一个人可以拿到(0.01~1×N)之间的一个红包值。
为了确保所有人至少有1分钱拿,不能前几个人就把钱拿光了,因此需要有一个判断算法。
1.2 示例代码
public class test{
public static void main(String[] args)
{
float num=10, N=1.9f; // 开始将系数设为1.9
int people=10;
for(int i=0; i<10; i++)
{
System.out.println("the number" + people + " can get " + num/people*N);
num = num - num/people*N;
people--;
}
System.out.println("there remain" + num);
}
}
the number10 can get 1.9
the number9 can get 1.71
the number8 can get 1.5176251
the number7 can get 1.3225019
the number6 can get 1.1241267
the number5 can get 0.9217838
the number4 can get 0.71438247
the number3 can get 0.5000677
the number2 can get 0.2750373
the number1 can get 0.027503723
there remain-0.01302808
问题分析:
-
当10个人结束后剩余的钱为负数,说明最后一个人达不到他的最大值
-
如果将系数改为2.3,会出现number1为负数的情况
the number10 can get 2.3 the number9 can get 1.9677777 the number8 can get 1.6480138 the number7 can get 1.3419542 the number6 can get 1.0511974 the number5 can get 0.7778861 the number4 can get 0.5250732 the number3 can get 0.29754147 the number2 can get 0.10413953 the number1 can get -0.031241853 there remain0.017658439 -
因此,在使用系数的算法时,添加判断金额是否合理是很重要的
2. 算法实现
2.1 设置金额的上下限
private static final float MINMONEY = 0.01f;
private static final float MAXMONEY = 200f;
我们的红包金额至少有0.01,最多有200(单笔红包金额不会超过200)。
2.2 判断金额是否合法
private boolean isRight(float money, int count) {
double avg = money/count;
if(avg < MINMONEY) {
return false;
}
else if(avg > MAXMONEY) {
return false;
}
return true;
}
2.3 随机产生红包
private float randomRedPacket(float money, float mins, float maxs, int count) {
if(count == 1) {
return (float)(Math.round(money*100))/100;
}
if(mins == maxs) {
return mins; // 如果最大值和最小值一样,就返回mins
}
float max = maxs > money ? money : maxs;
float one = ((float)Math.random()*(max-mins)+mins);
one = (float)(Math.round(one*100))/100;
float moneyOther = money - one;
if(isRight(moneyOther, count-1)) {
return one;
} else {
// 重新分配
float avg = moneyOther / (count-1);
if(avg < MINMONEY) {
return randomRedPacket(money, mins, one, count);
} else if(avg > MAXMONEY) {
return randomRedPacket(money, one, maxs, count);
}
}
return one;
}
2.4 实现红包分配
private static final float TIMES = 2.1f;
public List<Float> splitRedPackets(float money, int count) {
if(!isRight(money, count)) {
return null;
}
List<Float> list = new ArrayList<Float>();
float max = (float)(money*TIMES/count);
max = max > MAXMONEY ? MAXMONEY : max;
for(int i=0; i<count; i++) {
float one = randomRedPacket(money, MINMONEY, max, count-i);
list.add(one);
money -= one;
}
return list;
}
2.5 编写主函数
public static void main(String[] args) {
RedPacketUtil util = new RedPacketUtil();
System.out.println(util.splitRedPackets(200, 100));
}
运行方式:
javac RedPacketUtil.java
java RedPacketUtil
RedPacketUtil
[1.75, 1.52, 1.27, 2.81, 3.5, 1.37, 2.03, 0.77, 1.1, 1.89, 1.87, 3.46, 2.34, 0.99, 1.67, 3.63, 1.03, 4.13, 3.56, 0.57, 2.79, 0.96, 1.63, 3.36, 3.92, 3.12, 2.44, 1.2, 1.44, 0.24, 0.43, 3.16, 3.32, 3.24, 1.53, 3.53, 2.49, 1.3, 2.22, 4.05, 2.34, 0.4, 2.02, 3.71, 2.08, 2.73, 2.02, 0.06, 2.07, 1.72, 2.42, 0.95, 0.32, 2.85, 1.96, 3.67, 0.36, 2.33, 1.89, 0.25, 2.34, 1.02, 1.19, 0.69, 0.21, 3.03, 1.25, 0.41, 3.79, 1.54, 3.43, 3.09, 3.09, 2.9, 0.19, 2.08, 3.88, 0.67, 3.66, 0.41, 1.31, 2.37, 2.82, 0.65, 1.67, 1.39, 0.22, 1.23, 1.01, 3.28, 2.98, 0.89, 3.89, 0.84, 4.06, 1.06, 0.06, 1.71, 1.32, 4.59]
3. 数据分析
3.1 增加模拟次数
这个红包分配算法是网友们猜测的,暂时不去考虑微信是不是这么做的。
3.1.1 模拟实现
在上节实验中的完整代码中导入工具类库:
import java.util.Scanner;
修改main函数:
public static void main(String[] args) {
System.out.println("please input a number");
Scanner sc = new Scanner(System.in);
int num = sc.nextInt(), count = num, NUM = 20;
float[] index = new float[NUM];
while(num-- > 0) {
RedPacketUtil util = new RedPacketUtil();
List<Float> temp = util.splitRedPackets(20, NUM);
for(int i=0; i<temp.size(); i++) {
index[i] += temp.get(i);
}
}
for(int i=0; i<NUM; i++) {
System.out.println("the number " + i + " can get " + (index[i]/count));
}
}
模拟结果:
-
模拟20块红包分给20个人,运行10次
please input a number 10 the number 0 can get 1.234 the number 1 can get 1.014 the number 2 can get 1.186 the number 3 can get 1.02 the number 4 can get 1.129 the number 5 can get 1.12 the number 6 can get 1.0849999 the number 7 can get 0.844 the number 8 can get 0.759 the number 9 can get 1.08 the number 10 can get 1.242 the number 11 can get 0.73800004 the number 12 can get 1.0509999 the number 13 can get 0.82699996 the number 14 can get 0.80700004 the number 15 can get 0.995 the number 16 can get 0.96599996 the number 17 can get 0.99599993 the number 18 can get 0.56700003 the number 19 can get 1.34 -
当运行次数达到1000000次时,结果呈现正态分布
please input a number 1000000 the number 0 can get 1.0548186 the number 1 can get 1.05548 the number 2 can get 1.0544133 the number 3 can get 1.0541049 the number 4 can get 1.0543569 the number 5 can get 1.0551047 the number 6 can get 1.0537063 the number 7 can get 1.0544281 the number 8 can get 1.0545907 the number 9 can get 1.0557877 the number 10 can get 1.0539982 the number 11 can get 1.0553659 the number 12 can get 1.0531938 the number 13 can get 1.0456945 the number 14 can get 1.0171772 the number 15 can get 0.95570326 the number 16 can get 0.8545562 the number 17 can get 0.7179943 the number 18 can get 0.56496185 the number 19 can get 1.1307287
3.2 人数变化对结果的影响
修改main函数中的代码,将人数增加到200人,模拟100000次:
please input a number
1000000
the number 0 can get 10.497068
the number 1 can get 10.507459
the number 2 can get 10.505903
the number 3 can get 10.488457
the number 4 can get 10.510313
the number 5 can get 10.50352
the number 6 can get 10.502333
the number 7 can get 10.51191
the number 8 can get 10.499479
the number 9 can get 10.498564
the number 10 can get 10.502607
the number 11 can get 10.50349
the number 12 can get 10.494
the number 13 can get 10.404507
the number 14 can get 10.170025
the number 15 can get 9.569489
the number 16 can get 8.599382
the number 17 can get 7.242853
the number 18 can get 5.732309
the number 19 can get 11.738466
分析结果:
- 偏后的人拿到大金额红包的概率会小很多
- 红包分配呈现一定的规律性,符合概率分布特征
4. 算法优化建议
4.1 性能优化
- 可以考虑使用更高效的随机数生成算法
- 减少重复计算,如平均值的计算
4.2 精度优化
- 当前使用四舍五入到分的精度,可以考虑使用BigDecimal提高精度
- 避免浮点数计算带来的精度问题
4.3 稳定性优化
- 增加更多的边界条件检查
- 提供更详细的错误处理机制
5. 总结
该红包分配算法通过合理的随机数生成和金额限制,实现了相对公平的红包分配机制。通过大量模拟实验验证了其在不同场景下的稳定性,为实际应用提供了理论基础和实现参考。
完整代码:
import java.util.ArrayList;
import java.util.List;
public class RedPacketUtil {
private static final float MINMONEY = 0.01f;
private static final float MAXMONEY = 200f;
private static final float TIMES = 2.1f;
private boolean isRight(float money, int count) {
double avg = money / count;
if (avg < MINMONEY) {
return false;
} else if (avg > MAXMONEY) {
return false;
}
return true;
}
private float randomRedPacket(float money, float mins, float maxs, int count) {
if (count == 1) {
return (float) (Math.round(money * 100)) / 100;
}
if (mins == maxs) {
return mins; // 如果最大值和最小值一样,就返回mins
}
float max = maxs > money ? money : maxs;
float one = ((float) Math.random() * (max - mins) + mins);
one = (float) (Math.round(one * 100)) / 100;
float moneyOther = money - one;
if (isRight(moneyOther, count - 1)) {
return one;
} else {
// 重新分配
float avg = moneyOther / (count - 1);
if (avg < MINMONEY) {
return randomRedPacket(money, mins, one, count);
} else if (avg > MAXMONEY) {
return randomRedPacket(money, one, maxs, count);
}
}
return one;
}
public List<Float> splitRedPackets(float money, int count) {
if (!isRight(money, count)) {
return null;
}
List<Float> list = new ArrayList<Float>();
float max = (float) (money * TIMES / count);
max = max > MAXMONEY ? MAXMONEY : max;
for (int i = 0; i < count; i++) {
float one = randomRedPacket(money, MINMONEY, max, count - i);
list.add(one);
money -= one;
}
return list;
}
public static void main(String[] args) {
RedPacketUtil util = new RedPacketUtil();
System.out.println(util.splitRedPackets(200, 100));
}
}
如果觉得本文对你有帮助,可以点击下方打赏扫码支持一下。你的鼓励是我持续写下去的最大动力,感谢!
更多推荐

所有评论(0)