Java实现抢红包算法,附完整代码(公平版和手速版) 二倍均值算法 抢红包通用业务算法
今天研究红包雨 了解到二倍均值算法 微信发放100元10个红包随机的
首先我们先看一下拼手气红包的功能要求:
所有红包累计金额等于红包总金额
每个红包金额不能小于0.01元,也就是说必须保证每个用户至少能抢到一个预设的最小金额,人民币红包设置的最小金额一般是0.01元,如果是发放其他类型的红包(比如积分、其他类型货币等),则需要自定义一个最小金额
抢红包的期望收益应与先后顺序无关,即每个红包的金额大小和抢红包的先后顺序无关,保证红包尽量的公平
1.要求金额至少1分钱
2.合起来的总额 不能多于也不能少于100元
3.金额要平均 不能一个98元 一个1元 要尽可能均等 保证红包公平,即要保证每个人抢到红包的几率相等
当我们在群里抢红包时真的是手速越快红包金额越大吗?
答案当然是并不是, 都说了是拼手气, 岂能是拼手速!
不过也可以有拼手速的方法
二倍均值法( 公平版)
这是一种很合理很公平的抢红包算法了, 绝对不会让你拼手速的, 就别天真了。
在此我们假设
红包剩余金额为 M
红包剩余数量为 N
这种算法就是每次都在区间[0, M / N× 2] 随机取一个数
假设100元红包发10个人, 那么合理的做法应该是每个人领到10元的概率相同。
第一个人随机金额的范围为[0, 100 / 10× 2], 也就是[0, 20], 这样平均可以领到10元, 此时剩余金额为100 - 10 = 90。
第二个人随机金额的范围为[0, 90 / 9× 2], 也就是[0, 20], 这样平均也可以领到10元, 此时剩余金额为90 - 10 = 80。
第三个人随机金额的范围为[0, 80 / 8× 2], 也就是[0, 20], 这样平均也可以领到10元。
这样推导下去, 每个人领到相同金额的概率应该就是相同的了。
代码:
剩余红包金额为M,剩余人为N,那么有如下公式:
每次抢到金额 = 随机区间(0,(剩余红包金额M 除以 剩余数量N)x2)
public static List < Double > doubleMeanMethod(double money, int number) { List < Double > result = new ArrayList < Double > (); if(money < 0 && number < 1) return null; double amount, sum = 0; int remainingNumber = number; int i = 1; while(remainingNumber > 1) { amount = nextDouble(0.01, 2 * (money / remainingNumber)); sum += amount; System.out.println("第" + i + "个人领取的红包金额为:" + format(amount)); money -= amount; remainingNumber--; result.add(amount); i++; } result.add(money); System.out.println("第" + i + "个人领取的红包金额为:" + format(money)); sum += money; System.out.println("验证发出的红包总金额为:" + format(sum)); return result; }
线段切割法( 手速版)
这就是拼手速了, 是时候展示手速了。
这个算法可以把总金额想象成一条线段, 每个人都有机会切一刀, 前面的人切剩下的后面的人再接着切, 这样越是前面的人截取的长度( 理解成领取到的红包金额) 越大的概率就越大。
代码:
public static void lineSegmentCutting(double money, int number) { if(money < 0 && number < 1) System.out.println("输入错误!"); double begin = 0, end = money; double y = 0; for(int i = 0; i < number - 1; i++) { double nn = 0; double amount = nextDouble(begin, end); nn = amount - begin; System.out.println("第" + (i + 1) + "个人领取的红包金额为:" + format(nn)); y += nn; begin = amount; } System.out.println("第" + number + "个人领取的红包金额为:" + format(end - begin)); y += (end - begin); System.out.println("验证发出的红包总金额为:" + format(y)); }