紅包隨機算法,給定一定的金額,一定的人數,保證每個人都能隨機獲得一定的金額。


前段時間做了一個筆試題,覺得很有意思,特此記錄下來。

題目如下

//題目:請編寫一個紅包隨機算法。需求為:給定一定的金額,一定的人數,保證每個人都能隨機獲得一定的金額。
//比如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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM