首先,如果红包只有一个,本轮直接使用全部金额,确保红包发完。
然后,
计算出本次红包最少要领取多少,才能保证红包领完,即本轮下水位;
本轮最多领取多少,才能保证每个人都领到,即本轮上水位。
主要方式如下:
计算本次红包金额下水位:假设本次领到最小值1分,那接下来每次都领到200元红包能领完,那下水位为1分;如果不能领完,那按接下来每次都领200元,剩下的本轮应全部领走,是本轮的下水位。
计算本轮红包上水位:假设本轮领200元,剩下的钱还足够接下来每轮领1分钱,那本轮上水位为200元;如果已经不够领,那按接下来其他领1分,计算本轮的上水位。
为了使红包金额不要太悬殊,使用红包均值调整上水位。如果上水位金额大于两倍红包均值,那么使用两倍红包均值作为上水位。换句话说,每一轮抢到的红包金额,最高为两倍剩下红包的均值。
最后,获取随机数并用上水位取余,如果结果比下水位还小,则直接使用下水位,否则使用随机金额为本轮拆到金额。
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Random;
public class RandomRedPocket implements RedPocket {
BigDecimal avgAmount;
private BigDecimal totalAmount;
private BigDecimal lowestAmount;
private Integer remainNum;
public RandomRedPocket(BigDecimal totalAmount, int num) {
this.totalAmount = totalAmount;
this.remainNum = num;
this.avgAmount = totalAmount.divide(new BigDecimal(num), 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(2));
this.lowestAmount = BigDecimal.valueOf(0.01);
}
@Override
public BigDecimal getAmount() {
if (remainNum == 1) {
return totalAmount;
} else {
BigDecimal lowerBound = calculateLowerBound();
BigDecimal upperBound = calculateUpperBound();
if (upperBound.compareTo(avgAmount) > 0) {
upperBound = avgAmount;
}
remainNum = remainNum - 1;
BigDecimal randomAmount = genRandomMoney(lowerBound, upperBound);
totalAmount = totalAmount.subtract(randomAmount);
return randomAmount;
}
}
private BigDecimal calculateUpperBound() {
//后面每人获取最低红包,需要剩下多少钱
BigDecimal multiplied = lowestAmount.multiply(BigDecimal.valueOf(remainNum - 1));
BigDecimal upperBound = totalAmount.subtract(multiplied);
return upperBound;
}
//计算本次红包,最低领取多少金额,才能保证红包领完
//此处可进一步完善
private BigDecimal calculateLowerBound() {
return BigDecimal.valueOf(0.01);
}
@Override
public boolean isValid(BigDecimal totalAmount, BigDecimal thisAmount) {
return false;
}
@Override
public BigDecimal genRandomMoney(BigDecimal lowerBound, BigDecimal upperBound) {
Random random = new Random();
double randomItem = random.nextDouble();
BigDecimal decimal = upperBound.subtract(lowerBound).multiply(new BigDecimal(randomItem)).add(lowerBound);
decimal = decimal.setScale(2, RoundingMode.HALF_UP);
return decimal;
}
}