前段时间做了一个口试题,觉得很有意思,特此记录下来。
题目如下
// 题目: 请编写一个红包随机算法。需要为:给定肯定的金额,肯定的人数,保障每个人都能随机取得肯定的金额。
// 比方 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/lingy…