模拟抢红包中的发红包

本例子来源于程序员小灰微信公众号上面的文章中讲到的二倍均值法。
原文链接:https://mp.weixin.qq.com/s/AIE33sdT2QI6UL8cs1kJCQ

剩余红包金额为M,剩余人数为N,那么有如下公式:
每次抢到的金额 = 随机区间 (0, M / N X 2)
这个公式,保证了每次随机金额的平均值是相等的,不会因为抢红包的先后顺序而造成不公平。
举个栗子:
假设有10个人,红包总额100元。
100/10X2 = 20, 所以第一个人的随机范围是(0,20 ),平均可以抢到10元。
假设第一个人随机到10元,那么剩余金额是100-10 = 90 元。
90/9X2 = 20, 所以第二个人的随机范围同样是(0,20 ),平均可以抢到10元。
假设第二个人随机到10元,那么剩余金额是90-10 = 80 元。
80/8X2 = 20, 所以第三个人的随机范围同样是(0,20 ),平均可以抢到10元。
以此类推,每一次随机范围的均值是相等的。

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 模拟抢红包
 * @author pilaf
 * @create: 2018-10-31 08:40
 */
public class Demo {

    /**
     * 分配红包
     *
     * @param totalPeopleNum 参与抢红包的总人数
     * @param totalAmount    红包总金额(以分为单位)
     * @return
     */
    public static List<Integer> divideRedPackage(int totalPeopleNum, int totalAmount) {
        List<Integer> amountList = new ArrayList<>();
        //剩余的红包总金额
        int restTotalAmount = totalAmount;
        //剩余的人数
        int restPeopleNum = totalPeopleNum;
        Random random = new Random();

        for (int i = 0; i < totalPeopleNum - 1; i++) {
            //随机范围:[1,剩余人均金额的两倍),左闭右开
            int amount = random.nextInt(restTotalAmount * 2 / restPeopleNum - 1) + 1;
            restPeopleNum--;
            restTotalAmount -= amount;
            amountList.add(amount);
        }

        amountList.add(restTotalAmount);

        return amountList;
    }


    public static void main(String[] args) {
        //10个人分10块钱红包
        List<Integer> amountList = divideRedPackage(10, 10 * 100);
        BigDecimal sum = new BigDecimal("0");
        for (Integer amount : amountList) {
            BigDecimal money = new BigDecimal(amount).divide(new BigDecimal(100));
            //注意这儿,一定要把sum.add(money)再赋值给sum,否则循环外面的sum会是0。因为BigDecimal是Immutable类。
            sum = sum.add(money);
            System.out.println("红包金额:" + money + "元");
        }
        //看看发出去的红包总额是否真的等于收到的红包总额
        System.out.println("收到的红包总金额:" + sum+"元");
    }
}

运行一次的输出为:

红包金额:1.38元
红包金额:0.05元
红包金额:1.42元
红包金额:1.61元
红包金额:0.27元
红包金额:0.75元
红包金额:1.17元
红包金额:1.94元
红包金额:0.92元
红包金额:0.49元
收到的红包总金额:10.00

以前一直以为BigDecimal是可变的,写demo的时候运行时,一开始并没有把for循环中的sum.add(money)结果重新赋值给sum

sum = sum.add(money);

而是

sum.add(money);

导致最后在for循环外输出收到的红包总金额一直是零,查看了BigDecimal源码中的注释:

Immutable, arbitrary-precision signed decimal numbers. A BigDecimal consists of an arbitrary precision integer unscaled value and a 32-bit integer scale. If zero or positive, the scale is the number of digits to the right of the decimal point. If negative, the unscaled value of the number is multiplied by ten to the power of the negation of the scale. The value of the number represented by the BigDecimal is therefore (unscaledValue × 10-scale).

才想起来BigDecimal是不可变类,就像String类一样。

猜你喜欢

转载自blog.csdn.net/lzufeng/article/details/83574711