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;
}
}