Java_遗传学算法_排课

1. GITHUB项目地址

https://github.com/LeXiang-Peng/admin_system_java/blob/master/src/main/java/com/plx/admin_system/utils/GeneticAlgorithm.java

2. 代码

package com.plx.admin_system.utils;

import com.plx.admin_system.utils.pojo.schduledCourse.CourseTask;

import java.util.*;

/**
 * @author plx
 */
public class GeneticAlgorithm {
    /**
     * 种群的规模(0-100) population size
     */
    private static final Integer POPULATION_SIZE = 32;
    /**
     * 种群的变异概率 mutation probability
     */
    private static final Double MUTATION_PROBABILITY = 0.3;
    /**
     * 精英种群的个数 elite size
     */
    private static final Integer ELITE_SIZE = 15;
    /**
     * 进化代数(100-500) max iteration
     */
    private static final Integer MAX_ITERATION = 500;
    /**
     * 所有的种群 种群中 存放每一个 **需要编排的课程列表** (个体)
     */
    private List<List<CourseTask>> population;

    /**
     * 一键排课
     *
     * @param tasks
     * @param classroomListSize
     * @return
     */
    public List<CourseTask> evolute(List<CourseTask> tasks, Integer classroomListSize) {
        //初始化
        this._init_population(tasks, classroomListSize);
        //res 为备选集
        //如果无法满足当前_conflicts = 0,即完美解不存在(或者本次迭代中未找到), 则返回备选集最优解
        LinkedHashMap<List<CourseTask>, Integer> res = new LinkedHashMap<>();
        //迭代 —— 通过变异交叉获取可能的解集,接着验证解集的适应度分值
        for (int i = 0; i < MAX_ITERATION; i++) {
            List<List<CourseTask>> newPopulation = new ArrayList<>();
            //获取适应度分值
            LinkedHashMap<List<CourseTask>, List<Integer>> rateMap = _rate(population, ELITE_SIZE, tasks.size());
            //获取演化的结果
            List<CourseTask> result = evolvingProcess(res, newPopulation, rateMap, classroomListSize);
            if (Objects.nonNull(result)) {
                return result;
            }
        }
        //当完美解不存在时,返回最优解
        return getBestIndividual(res);
    }

    /**
     * evolute 随机生成单个课程的安排
     *
     * @param tasks             未编排的课程
     * @param courseList        已编排的课程
     * @param classroomListSize
     * @return
     */
    public List<CourseTask> evolute(List<CourseTask> tasks, List<CourseTask> courseList, Integer classroomListSize) {
        //初始化
        this._init_population(tasks, classroomListSize);
        //res 为备选集
        //如果无法满足当前_conflicts = 0,即完美解不存在(或者本次迭代中未找到), 则返回备选集最优解
        LinkedHashMap<List<CourseTask>, Integer> res = new LinkedHashMap<>();
        //迭代 —— 通过变异交叉获取可能的解集,接着验证解集的适应度分值
        for (int i = 0; i < MAX_ITERATION; i++) {
            List<List<CourseTask>> newPopulation = new ArrayList<>();
            //获取适应度分值
            LinkedHashMap<List<CourseTask>, List<Integer>> rateMap = _rate(population, courseList, ELITE_SIZE, tasks.size());
            //获取演化的结果
            List<CourseTask> result = evolvingProcess(res, newPopulation, rateMap, classroomListSize);
            if (Objects.nonNull(result)) {
                return result;
            }
        }
        //当完美解不存在时,返回最优解
        return getBestIndividual(res);
    }

    /**
     * get best individual 获取备选集中的最优解
     *
     * @param res
     * @return
     */
    List<CourseTask> getBestIndividual(Map<List<CourseTask>, Integer> res) {
        List<Map.Entry<List<CourseTask>, Integer>> list = new ArrayList<>(res.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<List<CourseTask>, Integer>>() {
            @Override
            public int compare(Map.Entry<List<CourseTask>, Integer> o1, Map.Entry<List<CourseTask>, Integer> o2) {
                return o1.getValue().compareTo(o2.getValue());
            }
        });
        return res.entrySet().iterator().next().getKey();
    }


    private List<CourseTask> evolvingProcess(LinkedHashMap<List<CourseTask>, Integer> res,
                                             List<List<CourseTask>> newPopulation,
                                             LinkedHashMap<List<CourseTask>, List<Integer>> rateMap,
                                             Integer classroomListSize) {
        //遍历适应度分值,查看是否出现解
        for (List<CourseTask> key : rateMap.keySet()) {
            //出现解,即conflicts(必须达成的条件)= 0,
            if (Objects.equals(rateMap.get(key).get(0), 0)) {
                //将满足必要条件的结果集存下来,作为备选集(用作保底)
                List<CourseTask> res_data = new ArrayList<>();
                for (CourseTask temp : key) {
                    res_data.add(new CourseTask(temp));
                }
                res.put(res_data, rateMap.get(key).get(1));
                //尽量返回完美解
                if (Objects.equals(rateMap.get(key).get(1), 0)) {
                    return key;
                }
            }
        }
        //精英种群
        for (List<CourseTask> key : rateMap.keySet()) {
            newPopulation.add(key);
        }
        while (newPopulation.size() < POPULATION_SIZE) {
            if (Math.random() < MUTATION_PROBABILITY) {
                //将变异个体加入种群中
                newPopulation.add(_mutate(newPopulation, classroomListSize));
            } else {
                //通过两个精英个体进行交叉
                _crossOver(newPopulation);
            }
        }
        this.population = newPopulation;
        return null;
    }

    /**
     * 初始化
     *
     * @param tasks
     * @param classroomListSize
     */
    private void _init_population(List<CourseTask> tasks, Integer classroomListSize) {
        this.population = new ArrayList<>();
        for (int i = 0; i < POPULATION_SIZE; i++) {
            List<CourseTask> individual = new ArrayList<>();
            for (int j = 0; j < tasks.size(); j++) {
                CourseTask temp = tasks.get(j);
                temp.init(classroomListSize);
                individual.add(new CourseTask(temp));
            }
            population.add(individual);
        }
    }

    /**
     * 一键排课
     * 计算适应度分值
     *
     * @return
     */
    private LinkedHashMap<List<CourseTask>, List<Integer>> _rate(List<List<CourseTask>> population,
                                                                 Integer elite, Integer courseSize) {

        LinkedHashMap<List<CourseTask>, List<Integer>> temp_res = new LinkedHashMap();
        CourseTask gene;
        CourseTask _gene;
        for (List<CourseTask> individual : population) {
            /**
             * 防止在同一天或者同一个时段的课过于密集
             * 记录横向和纵向的课程数(横向 —— 时间段/纵向 —— 每天)
             * 用于后续非必要条件冲突值计算
             */
            int[] weekDayArr = new int[5];
            int[] courseTimeArr = new int[4];
            /**
             * 碰撞冲突值(分 必要条件和非必要条件——conflicts(必要条件的冲突值),_conflicts(非必要条件的冲突值))
             */
            List<Integer> conflictList = new ArrayList<>();
            //必须达成的条件,即值必须为0
            int conflicts = 0;
            //非必须,优中选优
            int _conflicts = 0;
            /**
             * 利用下方的 0 - courseSize-2 的循环遍历,所以需要初始化courseSize - 1
             */
            Integer weekDay = individual.get(courseSize - 1).getWeekDay();
            Integer courseTime = individual.get(courseSize - 1).getCourseTime();
            // 记录下 每天的课程数 和 每个时间段的课程数
            weekDayArr[weekDay] += 1;
            courseTimeArr[courseTime] += 1;

            if (individual.get(courseSize - 1).studentTotalOverflows()) {
                conflicts++;
            }

            for (int i = 0; i < courseSize - 1; i++) {
                gene = individual.get(i);

                weekDayArr[gene.getWeekDay()] += 1;
                courseTimeArr[gene.getCourseTime()] += 1;

                if (gene.studentTotalOverflows()) {
                    conflicts++;
                }

                for (int j = i + 1; j < courseSize; j++) {
                    _gene = individual.get(j);
                    conflicts += getConflicts(gene, _gene);
                }
            }
            /**
             * 计算非必要条件的冲突
             */
            _conflicts = get_conflicts(weekDayArr, courseTimeArr);
            conflictList.add(conflicts);
            conflictList.add((_conflicts));
            temp_res.put(individual, conflictList);
        }
        return compare(temp_res, elite);
    }

    /**
     * 单个课程所采用的
     *
     * @param population
     * @param courseList
     * @param elite
     * @param courseSize
     * @return
     */
    private LinkedHashMap<List<CourseTask>, List<Integer>> _rate(List<List<CourseTask>> population,
                                                                 List<CourseTask> courseList,
                                                                 Integer elite, Integer courseSize) {

        LinkedHashMap<List<CourseTask>, List<Integer>> temp_res = new LinkedHashMap();
        LinkedHashMap<List<CourseTask>, List<Integer>> res = new LinkedHashMap();
        CourseTask gene;
        CourseTask _gene;
        for (List<CourseTask> individual : population) {
            List<Integer> conflictList = new ArrayList<>();
            int conflicts = 0;
            int _conflicts = 0;

            int[] weekDayArr = new int[5];
            int[] courseTimeArr = new int[4];

            Integer weekDay = individual.get(courseSize - 1).getWeekDay();
            Integer courseTime = individual.get(courseSize - 1).getCourseTime();

            weekDayArr[weekDay] += 1;
            courseTimeArr[courseTime] += 1;

            if (individual.get(courseSize - 1).studentTotalOverflows()) {
                conflicts++;
            }

            for (CourseTask course : courseList) {
                weekDayArr[course.getWeekDay()] += 1;
                courseTimeArr[course.getCourseTime()] += 1;
                for (int i = 0; i < courseSize; i++) {
                    conflicts += getConflicts(individual.get(i), course);
                }
            }

            for (int i = 0; i < courseSize - 1; i++) {
                gene = individual.get(i);

                weekDayArr[gene.getWeekDay()] += 1;
                courseTimeArr[gene.getCourseTime()] += 1;

                if (gene.studentTotalOverflows()) {
                    conflicts++;
                }

                for (int j = i + 1; j < courseSize; j++) {
                    _gene = individual.get(j);
                    conflicts += getConflicts(gene, _gene);
                }
            }
            _conflicts = get_conflicts(weekDayArr, courseTimeArr);
            conflictList.add(conflicts);
            conflictList.add(_conflicts);
            temp_res.put(individual, conflictList);
        }
        return compare(temp_res, elite);
    }

    /**
     * 变异 —— 随机取出精英种群中 其中之一 进行变异
     *
     * @param population
     * @param classroomListSize
     * @return
     */
    private List<CourseTask> _mutate(List<List<CourseTask>> population, Integer classroomListSize) {
        //随机选择变异种群中的个体
        List<CourseTask> individual = new ArrayList<>(population.get(new Random().nextInt(population.size())));
        //变异
        for (CourseTask chromosome : individual) {
            //随机变异——染色体上的基因点
            int gene_index = new Random().nextInt(3);
            if (gene_index == 0) {
                chromosome.setClassroom(new Random().nextInt(classroomListSize));
            } else if (gene_index == 1) {
                chromosome.setWeekDay(new Random().nextInt(5));
            } else if (gene_index == 2) {
                chromosome.setCourseTime(new Random().nextInt(4));
            }
        }
        //返回变异的个体
        return individual;
    }

    /**
     * 交叉—— 随机取出精英种群中 其中之二 进行染色体部分交叉
     *
     * @param elitePopulation
     */
    private void _crossOver(List<List<CourseTask>> elitePopulation) {
        Random random = new Random();
        //随机取出两个个体
        int index = random.nextInt(elitePopulation.size());
        List<CourseTask> individual = elitePopulation.get(index);
        //防止取出相同的个体
        List<CourseTask> _individual = elitePopulation.get(random.nextInt(elitePopulation.size() - index));
        //随机交叉 —— 染色体基因点
        int gene_index = random.nextInt(3);
        Integer temp;
        CourseTask gene;
        CourseTask _gene;
        for (int i = 0; i < individual.size(); i++) {
            gene = individual.get(i);
            _gene = _individual.get(i);
            if (gene_index == 0) {
                temp = gene.getClassroom();
                gene.setClassroom(_gene.getClassroom());
                _gene.setClassroom(temp);
            } else if (gene_index == 1) {
                temp = gene.getWeekDay();
                gene.setWeekDay(_gene.getWeekDay());
                _gene.setWeekDay(temp);
            } else if (gene_index == 2) {
                temp = gene.getCourseTime();
                gene.setCourseTime(_gene.getCourseTime());
                _gene.setCourseTime(temp);
            }
        }
    }

    /**
     * get _conflicts
     *
     * @param weekDayArr
     * @param courseTimeArr
     * @return
     */
    private static Integer get_conflicts(int[] weekDayArr, int[] courseTimeArr) {
        Integer _conflicts = 0;
        /**
         * 记录课程是否分布均匀(粗略考虑)
         * 通过迭代 优中选优
         */
        //一天有4节课,如果能保证每天只有1——2节课,则比较均匀
        for (int item : weekDayArr) {
            if (item >= 4) {
                _conflicts += 30;
            }
            if (item >= 3) {
                _conflicts += 10;
            }
            if (item == 0) {
                _conflicts += 5;
            }
        }
        //某个时间节点的课程(如,1,2小节)有5天(周一到周五)
        //如果能保证每个时间节点的课程只有1——3节,则比较均匀
        Set courseTimeSet = new HashSet();
        for (int item : courseTimeArr) {
            courseTimeSet.add(item);
            if (item >= 5) {
                _conflicts += 30;
            }
            if (item >= 4) {
                _conflicts += 15;

            }
            if (item == 0) {
                _conflicts += 5;
            }
        }
        return _conflicts;
    }

    /**
     * get conflicts
     *
     * @param gene
     * @param _gene
     * @return
     */
    private static Integer getConflicts(CourseTask gene, CourseTask _gene) {
        /**
         * 如下见名知意
         */
        Integer conflicts = 0;
        if (gene.courseOverlapsDuringSameTime(_gene)) {
            conflicts++;
        }
        if (gene.clazzOverlapsDuringSameTime(_gene)) {
            conflicts++;
        }
        if (gene.lecturerOverlapsDuringSameTime(_gene)) {
            conflicts++;
        }
        if (gene.SameCourseDuringSameDay(_gene)) {
            conflicts++;
        }
        return conflicts;
    }

    LinkedHashMap<List<CourseTask>, List<Integer>> compare(LinkedHashMap<List<CourseTask>, List<Integer>> temp_res,
                                                           Integer elite) {

        LinkedHashMap<List<CourseTask>, List<Integer>> res = new LinkedHashMap();
        List<Map.Entry<List<CourseTask>, List<Integer>>> list = new ArrayList<>(temp_res.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<List<CourseTask>, List<Integer>>>() {
            @Override
            public int compare(Map.Entry<List<CourseTask>, List<Integer>> o1, Map.Entry<List<CourseTask>, List<Integer>> o2) {
                /**
                 * 这样做的目的是防止,头重脚轻的局面
                 * 假设有这样一种情况,当必要条件已经满足,即conflicts == 0
                 * 但是其非必要条件的冲突特别大,假设为_conflicts = 40
                 * 如果使用,必要条件相等的时候,再使用非必要条件进行排序这种方式,就会导致[0,40]会排在[1,0]的前面,这样就很不合理。
                 * 只用片面的条件进行比较,必要会导致片面的结果
                 * 所有我采用,当非必要条件相差不大(默认+=1的范围),则考虑是否用非必要条件进行比较
                 * 如果非必要条件相差很大(默认+=10的范围),则使用非必要条件进行排序,否则使用必要条件进行排序。
                 */
                //如果必要条件相差不大,即差值在+-1内(包含1),则进行下面的判断
                if (Math.abs((o1.getValue().get(0) - o2.getValue().get(0))) <= 1) {
                    //如果非必要条件的差距很大,则用非必要条件进行排序
                    if (Math.abs((o1.getValue().get(1) - o2.getValue().get(1))) >= 10) {
                        return o1.getValue().get(1).compareTo(o2.getValue().get(1));
                    }
                    //否则用必要条件进行排序
                    return o1.getValue().get(0).compareTo(o2.getValue().get(0));
                }
                return o1.getValue().get(0).compareTo(o2.getValue().get(0));
            }
        });
        Iterator<Map.Entry<List<CourseTask>, List<Integer>>> iterator = list.iterator();
        Map.Entry<List<CourseTask>, List<Integer>> entry = null;
        /**
         * 记录当前精英的个数
         */
        int count = 0;
        while (iterator.hasNext()) {
            entry = iterator.next();
            res.put(entry.getKey(), entry.getValue());
            count++;
            if (count == elite) {
                break;
            }
        }
        return res;
    }
}
posted @ 2025-04-02 19:43  彭乐祥  阅读(23)  评论(0)    收藏  举报