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));
}

 

posted @ 2024-11-29 16:29  山河永慕~  阅读(31)  评论(0编辑  收藏  举报