金额随机分配算法(修改版)

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

 

posted @ 2018-02-09 18:45  请输入...昵称  阅读(1404)  评论(0编辑  收藏  举报