一个支持实时计算随机红包的工具,支持最大值Integer.MAX
参考链接:www.cnblogs.com/hanganglin/…
package com.example.demo;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* @author zhanglu
* @date Created in 2020/8/18 14:45
* @description
* @modifiedBy
* @version:
*/
public class hongbao4 {
/**
* 返回一次抽奖在指定中奖概率下是否中奖
*
* @param rate 中奖概率
* @return
*/
public static boolean canReward(double rate) {
return Math.random() <= rate;
}
/**
* 返回min~max区间内随机数,含min和max
*
* @param min
* @param max
* @return
*/
private static int getRandomVal(int min, int max) {
Random random = new Random();
return random.nextInt(max - min + 1) + min;
}
/**
* 红包 合法性校验
*
* @param money
* @param count
* @return
*/
private static boolean isRight(int money, int count, int min, int max) {
double avg = money / count;
//小于最小金额
if (avg < min) {
return false;
//大于最大金额
} else if (avg > max) {
return false;
}
return true;
}
/**
* 带概率偏向的随机算法,概率偏向subMin~subMax区间
* 返回boundMin~boundMax区间内随机数(含boundMin和boundMax),同时可以指定子区间subMin~subMax的优先概率
* 例:传入参数(10, 50, 20, 30, 0.8),则随机结果有80%概率从20~30中随机返回,有20%概率从10~50中随机返回
*
* @param boundMin 边界
* @param boundMax
* @param subMin
* @param subMax
* @param subRate
* @return
*/
public static int getRandomValWithSpecifySubRate(int boundMin, int boundMax, int subMin, int subMax, double subRate) {
if (canReward(subRate)) {
return getRandomVal(subMin, subMax);
}
return getRandomVal(boundMin, boundMax);
}
/**
* 随机分配第n个红包
*
* @param totalBonus 总红包量
* @param totalNum 总份数
* @param sendedBonus 已发送红包量
* @param sendedNum 已发送份数
* @param rdMin 随机下限
* @param rdMax 随机上限
* @return
*/
private static Integer randomBonusWithSpecifyBound(Integer totalBonus, Integer totalNum, Integer sendedBonus,
Integer sendedNum, Integer rdMin, Integer rdMax, double bigRate) {
if (!isRight(totalBonus, totalNum, rdMin, rdMax)) {
return null;
}
Integer avg = totalBonus / totalNum; // 平均值
Integer leftLen = avg - rdMin;
Integer rightLen = rdMax - avg;
Integer boundMin = 0, boundMax = 0;
// 大范围设置小概率
if (leftLen.equals(rightLen)) {
boundMin = Math.max((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMax), rdMin);
boundMax = Math.min((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMin), rdMax);
} else if (rightLen.compareTo(leftLen) > 0) {
// 上限偏离
Integer standardRdMax = avg + leftLen; // 右侧对称上限点
Integer _rdMax = canReward(bigRate) ? rdMax : standardRdMax;
boundMin = Math.max((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * standardRdMax), rdMin);
boundMax = Math.min((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMin), _rdMax);
} else {
// 下限偏离
Integer standardRdMin = avg - rightLen; // 左侧对称下限点
Integer _rdMin = canReward(bigRate) ? rdMin : standardRdMin;
boundMin = Math.max((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMax), _rdMin);
boundMax = Math.min((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * standardRdMin), rdMax);
}
// 已发平均值偏移修正-动态比例
if (boundMin.equals(boundMax)) {
return getRandomVal(boundMin, boundMax);
}
double currAvg = sendedNum == 0 ? (double) avg : (sendedBonus / (double) sendedNum); // 当前已发平均值
double middle = (boundMin + boundMax) / 2.0;
Integer subMin = boundMin, subMax = boundMax;
// 期望值
double exp = avg - (currAvg - avg) * sendedNum / (double) (totalNum - sendedNum);
if (middle > exp) {
subMax = (int) Math.round((boundMin + exp) / 2.0);
} else {
subMin = (int) Math.round((exp + boundMax) / 2.0);
}
Integer expBound = (boundMin + boundMax) / 2;
Integer expSub = (subMin + subMax) / 2;
double subRate = (exp - expBound) / (double) (expSub - expBound);
return getRandomValWithSpecifySubRate(boundMin, boundMax, subMin, subMax, subRate);
}
/**
* 生成红包一次分配结果
*
* @param totalBonus 红包总金额
* @param totalNum 红包总数量
* @param rdMin 红包随机最大值
* @param rdMax 红包随机最小值
* @param bigRate 指定大范围区间的概率
* @return
*/
public static List<Integer> createBonusList(Integer totalBonus, Integer totalNum, Integer rdMin, Integer rdMax, double bigRate) {
Integer sendedBonus = 0;
Integer sendedNum = 0;
List<Integer> bonusList = new ArrayList<>();
while (sendedNum < totalNum) {
Integer bonus = randomBonusWithSpecifyBound(totalBonus, totalNum, sendedBonus, sendedNum, rdMin, rdMax, bigRate);
if (null == bonus) {
return bonusList;
}
bonusList.add(bonus);
sendedNum++;
sendedBonus += bonus;
}
return bonusList;
}
public static void main(String[] arg) {
List<Integer> bonusList = createBonusList(20000, 200, 1, 500, 0.8);
System.out.println(bonusList);
Integer totle = 0;
for (Integer i : bonusList) {
totle = totle + i;
}
System.out.println("总计发出红包金额:" + totle);
}
}