问题如下:

  1. 所有人抢到的金额之和要等于红包金额,不能多也不能少。
  2. 每个人至少抢到1分钱。
  3. 要保证红包拆分的金额尽可能分布均衡,不要出现两极分化太严重的情况。

错误解法

每次拆分的金额 = 随机区间[1分, 剩余金额-1分]

存在的问题:

  1. 第1个人抢到金额的随机范围是[0.01,99.99]元,在正常的情况下,抢到金额的中位数是50元。假设第1个人随机抢到了50元,那么剩余金额是50元。
  2. 第2个人抢到金额的随机范围就小得多了,只有[0.01,49.99]元,在正常的情况下,抢到金额的中位数是25元。假设第2个人随机抢到了25元,那么剩余金额是25元。
  3. 第3个人抢到金额的随机范围就更小了,只有[0,24.99]元,按中位数可以抢到12.5元。
  4. 以此类推,红包的随机范围将会越来越小,不符合红包拆分的金额尽可能分布均衡的需求。。。

二倍均值法

把每次随机金额的上限定为剩余人均金额的2倍。

红包金额为m元
剩余人数为n
每次抢到的金额 = 随机区间 [0.01,m /n × 2 - 0.01]元

保证了每一次抢到金额随机范围的均值是相等的,不会因为抢红包的先后顺序而造成不公平。

  1. 第1次随机的金额有一半概率超过20元,使得后面的随机金额上限不足39.99元;
  2. 第1次随机的金额同样也有一半的概率小于20元,使得后面的随机金额上限超过39.99元。
  3. 因此从整体来看,第2次随机的平均范围仍然是[0.01,39.99]元。

JAVA实现

    /**
     * 分配红包的金额
     * @param totalAmount     总金额(以分为单位)
     * @param totalPeopleNum  总人数
     * @return
     */
    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,剩余人均金额的2倍-1] 分
            //+1d的原因是:random.nextInt随机返回一个值在[0,num)的int类型的整数,包括0不包括num
            //random.nextInt+1的范围是[1,num+1)也就是[1,num]
            //就满足了随机区间: [1 , m /n × 2 - 1]分
            int amount = random.nextInt((restAmount/restpeopleNum)*2-1)+1;
            restAmount = restAmount-amount;
            restpeopleNum--;
            amountList.add(amount);
        }
        amountList.add(restAmount);
        return amountList;
    }

测试方法:

    public static void main(String[] args) {
        List<Integer> amountList = divideRedPackage(1000,10);
        for (Integer e:amountList){
            System.out.print(e+"  ");
        }
        System.out.println();
    }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

局限性
除最后一次外,其他每次抢到的金额都要小于剩余人均金额的2倍,并不是完全自由地随机抢红包。(随机数值的上限被限制死了

线段切割法

把红包总金额想象成一条很长的线段,而每个人抢到的金额,则是这条主线段所拆分出的若干子线段。

确定每一条子线段的长度

由“切割点”来决定。
当n个人一起抢红包时,就需要确定n-1个切割点。

当所有切割点确定以后,子线段的长度也随之确定。此时红包的拆分金额,就等同于每个子线段的长度

当n个人一起抢总金额为m的红包时,我们需要做n-1次随机运算,以此确定n-1个切割点。
随机的范围区间是[1, m-1]。
开始1和结束是m-1的原因是最少分配1分钱(这里1表示一分)
[0,1]一段,[m-1,m]一段(最小的情况)

需要考虑的问题:

  1. 当随机切割点出现重复时,如何处理。
  2. 如何尽可能降低时间复杂度和空间复杂度。

JAVA实现

HashMap和HashSet区别HashSet存取值

/**
     * 拆分红包V2
     * @param totalAmount  总金额(以分为单位)
     * @param totalPeopleNum  总人数
     */
    public static List<Integer> divideRedPackageV2(Integer totalAmount, Integer totalPeopleNum){
        List<Integer> amountList = new ArrayList<Integer>();
        Set<Integer> segments = new HashSet<Integer>();
        Random random = new Random();
        for(int i = 0; i< totalPeopleNum-1; i++){
            //切割的位置segment,随机的范围区间是[1, m-1]
            int segment =  random.nextInt(totalAmount-2) + 1;
            //delta == 1
            int delta = random.nextInt(1)==0 ? 1 : -1;
            //这是异常的情况
            // segments.contains(segment) ==true 表示切割位置重复
            //segment == 0,切割位置不正确
            while(segments.contains(segment) || segment == 0){
                //加上1位,用来解决以上俩个问题。
                //%totalAmount是解决超过线段长度的情况。
                segment = (segment+delta)%totalAmount;
            }
            segments.add(segment);
        }

        //依据切割的段来划分 钱数
        //从HashSet取值
        //Collections.sort排序方法,把切割位置从小到大排序。
        List<Integer> segmentList = new ArrayList<Integer>(segments);
        Collections.sort(segmentList);
        for(int i=0; i<segmentList.size(); i++){
            Integer amount;
            if(i==0){
                amount = segmentList.get(0);
            }else {
                amount = segmentList.get(i) - segmentList.get(i-1) ;
            }
            amountList.add(amount);
        }
        amountList.add(totalAmount - segmentList.get(segmentList.size()-1));

        return amountList;
    }

测试方法:

    public static void main(String[] args) {
        List<Integer> amountList = divideRedPackageV2(1000,10);
        for (Integer e:amountList){
            System.out.print(e+"  ");
        }
        System.out.println();
    }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

保证了每次划分都是随机,并且概率相同,并且没有最大限制。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐