前段時間做了一個筆試題,覺得很有意思,特此記錄下來。
題目如下
//題目:請編寫一個紅包隨機算法。需求為:給定一定的金額,一定的人數,保證每個人都能隨機獲得一定的金額。 //比如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); } }
本篇文章如有幫助到您,請給「翎野君」點個贊,感謝您的支持。