zoukankan      html  css  js  c++  java
  • 红包随机算法,给定一定的金额,一定的人数,保证每个人都能随机获得一定的金额。

    前段时间做了一个笔试题,觉得很有意思,特此记录下来。

    题目如下

    //题目:请编写一个红包随机算法。需求为:给定一定的金额,一定的人数,保证每个人都能随机获得一定的金额。
    //比如100元的红包,10个人抢,每人分得一些金额。
    //约束条件为,最佳手气金额不能超过最大金额的90%,每人都有红包可抢。
    //请给出java代码实现,返回每个人的分配金额并打印出来。

    随机分配法

    随机法,每次抢红包时计算出本次能够获得的最小金额和最大金额,然后在这个区域间中取一个随机值并计算得出这次抢到的红包金额,这种方法,优点是实现简单,但是,先抢的人会很赚,抢到大红包的概率很高,越到后面的人越吃亏。 

    public class RedEnvelopMain {
    
        // 最佳手气获得红包金额,最大金额/总金额,的占比
        public static final double BEST_LUCK_PERCENT = 0.9;
    
        // 单人每次最小抢到的金额,默认为1分钱
        public static final double ONE_PERSON_MIN_DRAW_AMOUNT = 1;
    
        /**
         * 拆红包方法
         * 红包金额分配算法
         *
         * @param totalAmount 红包总金额
         * @param personNum   抢红包总人数
         */
        public void redEnvelopLuckyDraw(double totalAmount, int personNum) {
    
            if (totalAmount <= 0 || personNum < 1) {
                System.out.println("输入参数非法,请检查");
                return;
            }
    
            // 红包总金额 >= 分配人数 * 每人最小中奖金额
            if (totalAmount < (ONE_PERSON_MIN_DRAW_AMOUNT * personNum)) {
                System.out.println("红包总金额不能小于(中奖人数*单人单次中奖金额),请核对红包金额和发放人数");
                return;
            }
    
            double minDrawAmount = ONE_PERSON_MIN_DRAW_AMOUNT;
            double maxDrawAmount = totalAmount * BEST_LUCK_PERCENT;
    
            double drawLuckAmount = 0;
    
            for (Integer i = 0; i < personNum; i++) {
    
                int remainPersonNum = personNum - i - 1;
    
                // 假设剩下的人都中了单人最高金额,那么他此次最少能中的金额
                double othersAllDrawMaxAmountBalance = totalAmount - (maxDrawAmount * remainPersonNum);
                minDrawAmount = minDrawAmount > othersAllDrawMaxAmountBalance ? minDrawAmount : othersAllDrawMaxAmountBalance;
    
                // 每次抽奖前,计算此次抽奖最大可能出现的金额,假设10人分10元,第一人中8元,则剩下9人,要分2元,此时最大中奖金额发生变化
                //double othersAllDrawMinAmountBalance = totalAmount - (minDrawAmount * remainPersonNum);
                double othersAllDrawMinAmountBalance = totalAmount / remainPersonNum * 2;
                maxDrawAmount = maxDrawAmount < othersAllDrawMinAmountBalance ? maxDrawAmount : othersAllDrawMinAmountBalance;
    
                drawLuckAmount = (int) Math.floor((maxDrawAmount - minDrawAmount) * Math.random() + minDrawAmount);
    
                // 每个人抢到红包后,红包内的剩余金额
                totalAmount = totalAmount - drawLuckAmount;
    
                System.out.println("第" + (i + 1) + "个人抢到:" + drawLuckAmount + "元");
            }
        }
    
        public static void main(String[] args) {
    
            RedEnvelopMain redEnvelopMain = new RedEnvelopMain();
    
            redEnvelopMain.redEnvelopLuckyDraw(100, 10);
        }
    }

    二倍均值法

    假设总金额是M元,N个人,每次抢的金额=(0, (M/N) *2),比如,还是之前说的条件,金额100,人数10,

    第一个人抢的金额是 (0,20),抢到的数值,根据正态分布,应该是10左右,远低于10的概率很小,同样远大于10的概率和很小,这里假设第一个人抢到的数值是10;

    第二个人抢的金额是(0,90/9 *2)=(0,20),同第一个人,第二个人红包金额也应该是10附近;

    余下同理

    import java.math.BigDecimal;
    import java.util.Objects;
    
    public class RedEnvelopStrongerMain {
    
        // 最佳手气金额不能超过最大金额的90%
        public static final BigDecimal BEST_LUCK_PERCENT = new BigDecimal(0.9);
    
        // 单人每次最小抢到的金额,默认为1分钱
        public static final BigDecimal ONE_PERSON_MIN_DRAW_AMOUNT = new BigDecimal(1);
    
        /**
         * 拆红包方法
         * 红包金额分配算法
         *
         * @param totalAmount    红包总金额
         * @param personQuantity 抢红包总人数
         */
        public void redEnvelopLuckyDraw(BigDecimal totalAmount, Integer personQuantity) {
    
            if (Objects.isNull(totalAmount) || totalAmount.compareTo(BigDecimal.ZERO) <= 0
                    || Objects.isNull(personQuantity) || personQuantity < 1) {
                System.out.println("输入参数非法,请检查");
                return;
            }
    
            BigDecimal personNum = new BigDecimal(personQuantity);
            // 红包总金额 >= 分配人数 * 每人最小中奖金额
            if (totalAmount.compareTo(ONE_PERSON_MIN_DRAW_AMOUNT.multiply(personNum)) < 0) {
                System.out.println("红包总金额不能小于(中奖人数*单人单次中奖金额),请核对红包金额和发放人数");
                return;
            }
    
            BigDecimal minDrawAmount = ONE_PERSON_MIN_DRAW_AMOUNT;
    
            BigDecimal drawLuckAmount;
    
            for (Integer i = 0; i < personQuantity; i++) {
    
                Integer remainPersonQuantity = personQuantity - i - 1;
    
                if (remainPersonQuantity == 0) {
                    // 最后一个人,直接把剩余金额返回
                    drawLuckAmount = totalAmount;
                    totalAmount = totalAmount.subtract(drawLuckAmount);
                    System.out.println("第" + (i + 1) + "个人抢到:" + drawLuckAmount + "元");
                    break;
                }
    
                BigDecimal remainPersonNum = new BigDecimal(remainPersonQuantity);
    
                // 最大不超过剩余金额的90%
                BigDecimal maxDrawAmount = totalAmount.multiply(BEST_LUCK_PERCENT).setScale(2, BigDecimal.ROUND_UP);
    
                // 二倍均值法,使每个人的中奖金额都按均值概率分布
                BigDecimal doubleAverageAmount = totalAmount.divide(remainPersonNum, 2, BigDecimal.ROUND_UP).multiply(new BigDecimal(2)).setScale(2, BigDecimal.ROUND_UP);
                maxDrawAmount = doubleAverageAmount.compareTo(maxDrawAmount) < 0 ? doubleAverageAmount : maxDrawAmount;
    
                BigDecimal othersAllDrawMaxAmountBalance = totalAmount.subtract(maxDrawAmount.multiply(remainPersonNum));
                minDrawAmount = othersAllDrawMaxAmountBalance.compareTo(minDrawAmount) < 0 ? minDrawAmount : othersAllDrawMaxAmountBalance;
    
                drawLuckAmount = (maxDrawAmount.subtract(minDrawAmount)).multiply(new BigDecimal(Math.random())).setScale(2, BigDecimal.ROUND_UP);
    
                drawLuckAmount = drawLuckAmount.compareTo(minDrawAmount) < 0 ? minDrawAmount : drawLuckAmount;
    
                // 每个人抢到红包后,红包内的剩余金额
                totalAmount = totalAmount.subtract(drawLuckAmount);
    
                System.out.println("第" + (i + 1) + "个人抢到:" + drawLuckAmount + "元");
            }
        }
    
        public static void main(String[] args) {
    
            RedEnvelopStrongerMain redEnvelopMain = new RedEnvelopStrongerMain();
    
            redEnvelopMain.redEnvelopLuckyDraw(new BigDecimal(100), 3);
        }
    }

    本篇文章如有帮助到您,请给「翎野君」点个赞,感谢您的支持。

    原文链接:https://www.cnblogs.com/lingyejun/p/15389021.html

    作者:翎野君
    出处:http://www.cnblogs.com/lingyejun/
    若本文如对您有帮助,不妨点击一下右下角的【推荐】。
    如果您喜欢或希望看到更多我的文章,可扫描二维码关注我的微信公众号《翎驿》。
    转载文章请务必保留出处和署名,否则保留追究法律责任的权利。
  • 相关阅读:
    第二次作业
    构造之法现代软件工程
    软件工程的作业五个问题!
    第四次作业
    第五次作业
    软件工程第二次作业
    第五次作业·
    第五次作业
    软件工程第二次作业
    软件工程第一次作业
  • 原文地址:https://www.cnblogs.com/lingyejun/p/15389021.html
Copyright © 2011-2022 走看看