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