微信红包分割算法

一、前言

看小灰公众号有个文章,如何实现抢红包算法,很感兴趣,便跟着研究了一下。

如一个红包100元,分为5个人去抢,我自己脑海中首先浮现的就是挨个取随机数就行了呗,即 第一个随机范围为(0,100),值为i,第二个的随机范围就变为(0,100-i),以此类推即可。但这样就会暴露出几个较大的问题,即红包分配及其不均匀,并且前面拿到大红包的概率会更大。

因此公众号中出了另外两个方案。1. 二倍均值法;2. 线段切割法

二、方法

1. 二倍均值法

剩余红包金额为M,剩余人数为N,那么有如下公式:

每次抢到的金额 = 随机区间 (0, M / N X 2)。如果为100红包5个人去抢

每次每个人可以抢到的期望均为20;实现如下:

    public static List<Integer> divideRedPackage(Integer totalAmount, Integer totalPeopleNum) {
        List<Integer> amountList = new ArrayList<Integer>();

        Integer restAmount = totalAmount;

        Integer restPeopleNum = totalPeopleNum;

        Random random = new Random();

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

        return amountList;
    }

    public static void main(String[] args) {
        List<Integer> amountList = divideRedPackageNew(1000, 5);
        for (Integer amount : amountList) {
            System.out.println("抢到金额:" + new BigDecimal(amount).divide(new BigDecimal(100)));
        }
    }

2. 线段切割法

线段切割法:我们可以把红包总金额想象成一条很长的线段,而每个人抢到的金额,则是这条主线段所拆分出的若干子线段。如下图所示,100红包分五个人,即需要四个随机的“线段”:

《微信红包分割算法》

上面的方法看似来说很“科学”,但真的那么好吗,如果发生了线段切割点的碰撞怎么办?

公众号中给的方式是,直接给当前线段+1或-1一直到没有碰撞位置。这样就会导致如果一旦发生了碰撞,就会出现一个0.01的红包,这样好吗?

我当前的解法也不够好,如果碰撞就继续随机,直到随机到为止,我的解法也非常不好,因为如果给的是0.06元,5个人的红包,这样的碰撞机率便会很高,导致整个算法很有可能会一直迭代下去,出现大问题;

我当前的解法代码如下:请大家慎用。。。因为如果碰撞率高的话,真的会有很大的效率问题,所以当前临时的解决方案就还是给碰撞端+1或-1直至不碰撞为止。

 public static List<Integer> divideRedPackageNew(Integer totalAmount, Integer totalPeopleNum) {
        //人数比钱数多则直接返回错误
        if(totalAmount<totalPeopleNum){
            System.out.println("钱数人数设置错误!");
            return null;
        }
        //随机分割totalPeopleNum-1个点
        List<Integer> indexList = new ArrayList<>();
        List<Integer> amountList = new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i < totalPeopleNum - 1; i++) {

            int index;
            {
                index = random.nextInt(totalAmount - 2) + 1;
                //如果出现碰撞怎么办
            }
            while (indexList.contains(index));
            indexList.add(index);
        }
        Collections.sort(indexList);
        int start = 0;
        for (Integer index:indexList) {
            amountList.add(index-start);
            start = index;
        }
        amountList.add(totalAmount-start);
        return amountList;
    }

 

点赞