微信红包算法

红包算法思路

随机,额度在0.01和(剩余平均值*2)之间。
例如:发100块钱,总共10个红包,那么平均值是10块钱一个,那么发出来的红包的额度在0.01元~20元之间波动。
当前面3个红包总共被领了40块钱时,剩下60块钱,总共7个红包,那么这7个红包的额度在:0.01~((60/7) * 2)=17.14之间。
注意:这里的算法是每被抢一个后,剩下的会再次执行上面的这样的算法
这样算下去,会超过最开始的全部金额,因此到了最后面如果不够这么算,那么会采取如下算法:保证剩余用户能拿到最低1分钱即可。
如果前面的人手气不好,那么后面的余额越多,红包额度也就越多,因此实际概率一样的。

package arthur.dy.lee.red.envelope;

public class RedPackage {
    public int remainSize;
    public double remainMoney;//生产用BigDecimal

    public RedPackage(int remainSize, double remainMoney){
        this.remainSize = remainSize;
        this.remainMoney = remainMoney;
    }
}
package arthur.dy.lee.red.envelope;

import java.math.BigDecimal;
import java.security.SecureRandom;
import java.text.DecimalFormat;

/**
 * 微信红包算法
 * <p>
 * 随机,额度在0.01和(剩余平均值*2)之间。
 * 例如:发100块钱,总共10个红包,那么平均值是10块钱一个,那么发出来的红包的额度在0.01元~20元之间波动。
 * 当前面3个红包总共被领了40块钱时,剩下60块钱,总共7个红包,那么这7个红包的额度在:0.01~(60/7*2)=17.14之间。
 * 注意:这里的算法是每被抢一个后,剩下的会再次执行上面的这样的算法
 * 这样算下去,会超过最开始的全部金额,因此到了最后面如果不够这么算,那么会采取如下算法:保证剩余用户能拿到最低1分钱即可。
 * 如果前面的人手气不好,那么后面的余额越多,红包额度也就越多,因此实际概率一样的。
 */
public class RedEnvelope {

    public static double getRandomMoney(RedPackage redPackage) {
        // remainSize 剩余的红包数量
        // remainMoney 剩余的钱
        if (redPackage.remainSize == 1) {
            redPackage.remainSize--;
            return (double) Math.floor(redPackage.remainMoney * 100) / 100;
        }
        double min = 0.01;
        double max = (redPackage.remainMoney / redPackage.remainSize) * 2;

        SecureRandom random = new SecureRandom();
        double money = random.nextDouble() * max;
        if (money < min) {
            money = min;
        }

        money = (double) Math.floor(money * 100) / 100;

        redPackage.remainSize--;
        redPackage.remainMoney = redPackage.remainMoney - money;

        return money;
    }

    public static void main(String[] args) {
        DecimalFormat df = new DecimalFormat("#.00");

        for (int i = 0; i < 20; i++) {
            RedPackage redPackage = new RedPackage(10, 100);

            BigDecimal count = new BigDecimal(0);
            while (redPackage.remainSize != 0) {
                double money = RedEnvelope.getRandomMoney(redPackage);
                System.out.print(money + " ");
                count = count.add(new BigDecimal(money));
            }
            System.out.println(" |totalCount=" + df.format(count));
            System.out.println("-------");

        }
    }
}

运行10次结果

13.75 2.82 19.18 3.0 7.62 4.2 1.61 20.74 26.92 0.16  |totalCount=100.00
12.66 1.1 17.01 5.73 4.58 3.74 18.56 8.94 18.55 9.13  |totalCount=100.00
2.43 20.83 1.1 14.4 16.31 15.9 11.42 5.83 0.09 11.69  |totalCount=100.00
12.91 15.4 16.72 6.47 13.14 9.52 3.9 9.59 5.85 6.5  |totalCount=100.00
10.07 0.1 3.62 2.33 7.61 27.12 9.34 22.12 6.39 11.3  |totalCount=100.00
12.22 6.61 6.78 0.68 2.29 0.24 8.46 1.62 35.79 25.3  |totalCount=99.99
0.68 5.4 6.5 4.44 14.24 0.64 0.07 0.03 10.15 57.85  |totalCount=100.00
10.79 2.08 7.15 17.5 0.52 6.39 6.01 2.65 0.54 46.37  |totalCount=100.00
19.98 6.85 12.3 12.15 12.12 1.47 7.72 4.53 18.53 4.35  |totalCount=100.00
0.64 4.46 5.48 0.03 18.84 1.85 24.59 14.22 21.98 7.91  |totalCount=100.00

带范围的红包算法思路

思路:

  • 1、剩余金额=总值-抽奖次数*最小值
  • 2、剩余金额和次数当作红包算法,计算返回抽奖值,最后再加上最小值即可。
package arthur.dy.lee.red.envelope;

import java.math.BigDecimal;
import java.security.SecureRandom;
import java.text.DecimalFormat;

/**
 * 带范围的红包,最小值6和最大值为12
 *
 * 思路:
 * 1、剩余金额=总值-抽奖次数*最小值
 * 2、剩余金额和次数当作红包算法,计算返回抽奖值,最后再加上最小值即可。
 */
public class RangeRedEnvelope {

    public static double minRange = 6; //红包最大值
    public static double maxRange = 12; //红包最大值
    public static DecimalFormat df = new DecimalFormat("#.00");

    public static void getWrappedRandomMoney(RedPackage redPackage) {

        //加上范围后,不同点1
        redPackage.remainMoney = redPackage.remainMoney - redPackage.remainSize * minRange;
        double money;
        BigDecimal count = new BigDecimal(0);
        while (redPackage.remainSize >0) {
            money = (double) Math.floor((getRandomMoney(redPackage) + minRange) * 100) / 100;
            System.out.print(money + " ");
            count = count.add(new BigDecimal(money));

        }
        System.out.println(" |totalCount=" + df.format(count));
    }

    public static double getRandomMoney(RedPackage redPackage) {
        // remainSize 剩余的红包数量
        // remainMoney 剩余的钱
        if (redPackage.remainSize == 1) {
            redPackage.remainSize--;
            return (double) Math.floor(redPackage.remainMoney * 100) / 100;
        }
        double min = 0.01;
        double max = (redPackage.remainMoney / redPackage.remainSize) * 2;

        SecureRandom random = new SecureRandom();
        double money;

        if (max > (maxRange - minRange)) { //加上范围后,不同点2
            money = getRangeNumber(maxRange - minRange - 1, maxRange - minRange);

        } else {
            money = random.nextDouble() * max;
        }

        if (money < min) {
            money = min;
        }
        money = (double) Math.floor(money * 100) / 100;

        redPackage.remainSize--;
        redPackage.remainMoney = redPackage.remainMoney - money;
        return money;
    }

    public static double getRangeNumber(double min, double max) {
        SecureRandom random = new SecureRandom();
        return random.nextDouble() * (max - min) + min;
    }

    public static void main(String[] args) {

        for (int i = 0; i < 20; i++) {
            RedPackage redPackage = new RedPackage(10, 100);
            while (redPackage.remainSize != 0) {
                RangeRedEnvelope.getWrappedRandomMoney(redPackage);
            }
            //System.out.println("-------");
        }
    }
}

运行10次后的结果

11.47 11.35 11.59 11.39 11.7 9.41 9.65 7.13 6.95 9.35  |totalCount=99.99
11.1 11.66 11.86 11.9 6.01 11.05 11.8 6.69 7.94 9.98  |totalCount=99.99
11.08 11.55 11.82 11.79 7.96 11.32 8.0 9.37 6.47 10.62  |totalCount=99.98
11.97 11.58 11.55 11.13 9.31 9.85 6.26 11.33 8.83 8.19  |totalCount=100.00
11.38 11.84 11.35 11.6 6.67 11.82 10.4 9.19 7.73 8.0  |totalCount=99.98
11.01 11.05 11.26 11.78 11.27 8.62 8.19 11.3 7.07 8.44  |totalCount=99.99
11.9 11.61 11.45 11.1 7.21 11.53 8.18 11.15 8.17 7.7  |totalCount=100.00
11.47 11.8 11.03 11.57 11.1 6.67 11.05 10.55 6.47 8.28  |totalCount=99.99
11.57 11.09 11.95 11.52 10.1 6.96 11.5 9.91 8.14 7.26  |totalCount=100.00
11.27 11.26 11.16 11.88 11.79 6.61 11.62 9.92 6.99 7.5  |totalCount=100.00
11.89 11.52 11.1 11.99 8.53 8.24 11.0 6.06 11.83 7.83  |totalCount=99.99
11.65 11.73 11.89 11.46 9.86 7.92 9.64 9.94 9.09 6.8  |totalCount=99.98
11.44 11.46 11.78 11.09 11.97 10.62 7.73 9.39 7.23 7.29  |totalCount=100.00
11.84 11.8 11.11 11.23 11.76 6.5 11.34 8.51 9.78 6.11  |totalCount=99.98
11.64 11.12 11.96 11.38 10.76 10.91 8.76 8.87 7.89 6.7  |totalCount=99.99
11.43 11.79 11.13 11.08 11.18 7.6 11.53 9.69 8.02 6.54  |totalCount=99.99
11.91 11.57 11.2 11.7 6.79 11.11 8.27 11.14 6.28 10.03  |totalCount=100.00
11.35 11.51 11.86 11.73 8.37 11.63 9.14 9.87 8.07 6.45  |totalCount=99.98
11.42 11.87 11.24 11.87 6.06 11.34 11.8 9.06 8.56 6.76  |totalCount=99.98
11.3 11.74 11.41 11.95 7.09 11.33 6.71 11.09 10.89 6.47  |totalCount=99.98

就范围红包的算法来说,刚开始需要取值比较大,会造成相对的不公平。

参考:
微信红包的随机算法是怎样实现的?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值