金额随机分配算法(修改版)
import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; /** * 金额随机分配算法 * @author kwf * @since 2018-2-9 11:03:59 */ public class Main { //需要设置的参数 private static final double MIN_MONEY = 0.1; //用户获得的最小金额 private static final double MAX_MONEY = 0; //用户获得的最大金额(可能存在误差。主要用于计算倍数,值为0则使用默认倍数) private static final double TOTAL_MONEY = 888;//总金额 private static final int TOTAL_COUNT = 3000;//总份数 private static final boolean IS_UPSET = true;//结果是否需要打乱 private static double times = 10;//倍数(用户获得的最大金额=当前平均值*倍数,当前平均值=剩余金额/剩余份数)(若最大金额不为0则会被重新赋值) private static double AVG_SCALE = 0.8;//趋于均值的比例 private static double AVG_FLOAT_SCALE = 0.5;//均值上下浮动的比例 private static double avgMoney = TOTAL_MONEY / TOTAL_COUNT;//平均值 private static int avgCount = (int)Math.floor(TOTAL_COUNT * AVG_SCALE);//趋于均值的份数 private static double randomCount = TOTAL_COUNT - avgCount;//随机分配份数 private static double leftMoney = TOTAL_MONEY; //剩余金额 private static int leftCount = TOTAL_COUNT;//剩余份数 private static double avgTotal = 0;//均值列表总值 private static double randomTotal = 0;//随机列表总值 private static int runCount = 0;//运行次数 private static int minCount = 0;//算法中获得的最小金额的个数 private static int avgBottomCount = 0;//均值以下的个数 private static double maxValue, minValue; //算法中获得的最大值和最小值 private static List<Double> list = new ArrayList<>(); //用于存储金额列表 private static int treeTime = 1;//递归次数,超过10次直接返回最小值,防止递归层数过深导致的栈溢出 public static void main(String[] args) { System.out.println("倍数为" + setTimes() + ",平均值为" + avgMoney); if(!isRight(TOTAL_MONEY, TOTAL_COUNT)) { //如果设置金额和份数不合法则报错 initAllAvgList(); } else{ initAvgList(); initRandomList(); } if(IS_UPSET) { System.out.println("打乱前:" + list); //打印打乱前的金额列表 Collections.shuffle(list);//打乱列表 System.out.println("打乱后:" + list); //打印打乱后的金额列表 } else{ System.out.println(list); //打印金额列表 } System.out.println("均值总额为" + Math.round(avgTotal) + ",随机总额为" + Math.round(randomTotal)); System.out.println("算法运行了" + runCount + "次"); //打印金额列表 maxValue = minValue = list.get(0); for(double value:list) { maxValue = value > maxValue ? value : maxValue; minValue = value < minValue ? value : minValue; } System.out.println("最大值为" + maxValue + ",最小值为" + minValue); System.out.println("小于平均值的个数为" + avgBottomCount); System.out.println("最小金额的个数为" + minCount); } /** * 填充真实均值列表 */ private static void initAllAvgList() { for(int i = 0; i < TOTAL_COUNT; i++) { double money = Math.floor(avgMoney * 100) / 100; list.add(money); avgTotal += money; if(money < avgMoney) avgBottomCount++; if(Double.doubleToLongBits(money) == Double.doubleToLongBits(MIN_MONEY)) minCount++; } } /** * 填充浮动均值列表 */ private static void initAvgList() { for(int i = 0; i < avgCount; i++) { double money = getAvgMoney(); list.add(money); avgTotal += money; if(money < avgMoney) avgBottomCount++; if(Double.doubleToLongBits(money) == Double.doubleToLongBits(MIN_MONEY)) minCount++; } } /** * 填充随机列表 */ private static void initRandomList() { for(int i = 0; i < randomCount; i++) { double money = getRandomMoney(); list.add(money); randomTotal += money; if(money < avgMoney) avgBottomCount++; if(Double.doubleToLongBits(money) == Double.doubleToLongBits(MIN_MONEY)) minCount++; } } /** * 均值上下浮动算法 * @return 误差均值(均值+均值*浮动比例) */ private static double getAvgMoney() { runCount++; if(treeTime >= 10) { treeTime = 1; return MIN_MONEY; } if (leftCount == 1) { return (double) Math.round(leftMoney * 100) /100; } Random r = new Random(); double money = avgMoney + (r.nextDouble() * 2 -1) * AVG_FLOAT_SCALE * avgMoney; money = money <= MIN_MONEY ? MIN_MONEY : money; money = Math.floor(money * 100) / 100; if(isRight(leftMoney - money, leftCount - 1)) { treeTime = 1; leftMoney -= money; leftCount--; return money; } else {//如果不合法则递归调用随机算法,直到合法 treeTime++; return getAvgMoney(); } } /** * 随机算法 * @return 随机金额(最小金额~当前均值*倍数) */ private static double getRandomMoney() { runCount++; if(treeTime >= 10) { treeTime = 1; return MIN_MONEY; } if (leftCount == 1) { return (double) Math.round(leftMoney * 100) /100; } Random r = new Random(); double max = leftMoney / leftCount * times; double money = r.nextDouble() * max; money = money <= MIN_MONEY ? MIN_MONEY : money; money = Math.floor(money * 100) / 100; if(isRight(leftMoney - money, leftCount - 1)) { treeTime = 1; leftMoney -= money; leftCount--; return money; } else {//如果不合法则递归调用随机算法,直到合法 treeTime++; return getRandomMoney(); } } /** * 判断金额和份数是否合法,平均值小于最小金额则视为不合法 * @param money 金额 * @param count 份数 * @return 合法性 */ private static boolean isRight(double money, int count) { return money / count >= MIN_MONEY; } /** * 设置倍数(仅当设置了最大金额才有效,否则为默认倍数) * @return 倍数 */ private static double setTimes() { if(MAX_MONEY != 0) { times = MAX_MONEY / (TOTAL_MONEY / TOTAL_COUNT); } return times; } }