二倍均值法计算红包金额

在日常 Java 开发中,业务上可能会遇到一些拉新引流的相关需求,比如:发放红包、签到领现金等(这里以发放红包为例)。

那么就会面临一个问题:怎么保证红包在领取时,金额随机,且至少能领0.01元?

一般会有两种红包方案:

“拼手速”红包

我们假设有一个100元的红包。第一个人可以在0.01元到100元之间,随机地分配到一定金额。
如果我们把第一个人抽到的所有可能的金额都计算在内,并取个平均值,那么他平均能获得50元。
这50元在数学上还有个形象的名字,叫作数学期望。
既然是“期望”,就总会有落空的时候,也不排除会有意外的惊喜。
因此,第一个人抽到的金额可能不足50元,也可能大于50元。

如上所述,虽然每一个人在抢红包时都是随机分配数额,貌似很公平,但总会有“先来后到”。

“雨露均沾”红包

        原理其实很简单,就是把每个人可能抽到的最高金额强行降低,即使你是第一个打开红包的人,也不允许你任意的从0.01到100元之间抽取金额。

        这时候就需要有个相对“公平”的算法来计算相应金额:二倍均值法

假设红包总金额是X,红包个数为Y,每个红包的最低金额是0.01元
那么每次抢到的红包金额的范围在 (0.01, (X/Y) *2) 之间。

即:每次运算时,在 0.01 ~ (剩余金额 / 剩余个数 * 2) 的范围内随机取值
也就是最大值为:0~100的随机值 / 100 * (剩余金额 / 剩余个数 * 2)

闲话少说,直接上示例代码:

public class ComputeRedPacketAmount {

    //二倍均值法:
    // 假设红包总金额是X,红包个数为Y,每个红包的最低金额是0.01元,
    // 那么每次抢到的红包金额的范围在 (0.01, (X/Y) *2) 之间。
    public static void compute(BigDecimal amount, BigDecimal min, BigDecimal num) {
        //总金额扣除 (最低金额 * 总个数),也就是预留最低金额
        BigDecimal remain = amount.subtract(min.multiply(num));
        final Random random = new Random();
        final BigDecimal hundred = new BigDecimal("100");
        final BigDecimal two = new BigDecimal("2");
        BigDecimal sum = BigDecimal.ZERO;
        BigDecimal remainNum;

        BigDecimal redAmount;
        for (int i = 0; i < num.intValue(); i++) {
            final int nextInt = random.nextInt(100);
            remainNum = num.subtract(new BigDecimal(i));
            System.out.println("当前随机值为:" + nextInt);
            if (i == num.intValue() - 1) {
                //最后一个人分配剩余金额
                redAmount = remain;
            } else {
                // 0~100的随机值 * (剩余金额 * 2 / 剩余个数) / 100
                //如: 0~100的随机值 * (100 * 2 / 10) / 100
                redAmount = new BigDecimal(nextInt)
                        .multiply(remain.multiply(two).divide(remainNum, 2, RoundingMode.CEILING))
                        .divide(hundred, 2, RoundingMode.FLOOR);
                System.out.println("随机金额为:" + redAmount);
            }
            if (remain.compareTo(redAmount) > 0) {
                remain = remain.subtract(redAmount);
            } else {
                remain = BigDecimal.ZERO;
            }
            sum = sum.add(min.add(redAmount));
            System.out.println("第" + (i + 1) + "个人抢红包金额为:" + min.add(redAmount));
            System.out.println("剩余金额为:" + remain);
            System.out.println("=============================");
        }
        System.out.println("总共领取的金额为:" + sum);
    }
}

测试结果如下:

public class ComputeRedPacketAmount {

    public static void main(String[] args) {
        BigDecimal amount = new BigDecimal("1");
        BigDecimal min = new BigDecimal("0.01");
        BigDecimal num = new BigDecimal("10");
        compute(amount, min, num);
    }
}


当前随机值为:55
随机金额为:0.09
第1个人抢红包金额为:0.10
剩余金额为:0.81
=============================
当前随机值为:82
随机金额为:0.14
第2个人抢红包金额为:0.15
剩余金额为:0.67
=============================
当前随机值为:70
随机金额为:0.11
第3个人抢红包金额为:0.12
剩余金额为:0.56
=============================
当前随机值为:84
随机金额为:0.13
第4个人抢红包金额为:0.14
剩余金额为:0.43
=============================
当前随机值为:88
随机金额为:0.13
第5个人抢红包金额为:0.14
剩余金额为:0.30
=============================
当前随机值为:26
随机金额为:0.03
第6个人抢红包金额为:0.04
剩余金额为:0.27
=============================
当前随机值为:35
随机金额为:0.04
第7个人抢红包金额为:0.05
剩余金额为:0.23
=============================
当前随机值为:12
随机金额为:0.01
第8个人抢红包金额为:0.02
剩余金额为:0.22
=============================
当前随机值为:47
随机金额为:0.10
第9个人抢红包金额为:0.11
剩余金额为:0.12
=============================
当前随机值为:21
第10个人抢红包金额为:0.13
剩余金额为:0
=============================
总共领取的金额为:1.00

原理:

由于有最低领取金额 0.01元,那么先给所有人分0.01元,这时剩余的金额为 amount - num * 0.01

在当前剩余金额中计算随机分配金额:0~100的随机值 / 100 * (剩余金额 / 剩余个数) * 2

得到结果后,再加上已分配的 0.01元,即为最终领取金额

以上就是二倍均值法的相关分析

我是【辛勤de小蜜蜂】关注我,我们下期见


  • 由于博主才疏学浅,难免会有纰漏,假如您发现了错误或遗漏的地方,还望留言斧正,我会尽快对其加以修正。

  • 如果您觉得文章还不错,您的转发、分享、点赞、留言就是对我最大的鼓励。

  • 感谢您的阅读,十分欢迎并感谢您的关注。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辛勤de小蜜蜂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值