基于Springboot+vue实现的在线学习系统(带遗传算法)

        小编最近在闭关修改,忙于开发和研究算法,所以更新的速度严重滞后,今天终于可以分享成果了。

        废话不多说,直接上图上代码!!!!!!!!!!!!!!

以上是系统总体的介绍,完成了大部分功能,还有很多细节需要处理。

        系统主要的亮点是不仅有考试系统,还有在线学习的功能。组卷能手动也能自动,同时加了遗传算法。

首先了解一下概念(转载):
 一、什么是遗传算法
遗传算法的首次提出是在1967年,Holland教授的学生Bagley在其博士论文中提到"GeneticAlgorithm" ,即GA;1975年Holland教授出版了第一本系统叙述其内容的专著,奠定理论基础。今天,遗传算法已经成为十分流行的算法,在组合优化,自动控制,图像处理,机器学习等领域都有很广泛的运用。


二、遗传算法的原理
遗传的算法的背景是达尔文的进化论,没错!算法的求解就是一个不停地繁衍、遗传、淘汰、进化的过程,以求出最适合的根。


三、进化论知识 
作为遗传算法生物背景的介绍,下面内容了解即可:
种群(Population):生物的进化以群体的形式进行,这样的一个群体称为种群。
个体:组成种群的单个生物。
基因 ( Gene ) :一个遗传因子。 
染色体 ( Chromosome ) :包含一组的基因。
生存竞争,适者生存:对环境适应度高的、牛B的个体参与繁殖的机会比较多,后代就会越来越多。适应度低的个体参与繁殖的机会比较少,后代就会越来越少。
遗传与变异:新个体会遗传父母双方各一部分的基因,同时有一定的概率发生基因变异。
简单说来就是:繁殖过程,会发生基因交叉( Crossover ) ,基因突变 ( Mutation ) ,适应度( Fitness )低的个体会被逐步淘汰,而适应度高的个体会越来越多。那么经过N代的自然选择后,保存下来的个体都是适应度很高的,其中很可能包含史上产生的适应度最高的那个个体。

遗传算法有3个最基本的操作:选择,交叉,变异。

选择:选择一些染色体来产生下一代。一种常用的选择策略是 “比例选择”,也就是个体被选中的概率与其适应度函数值成正比。假设群体的个体总数是M,那么那么一个体Xi被选中的概率为f(Xi)/( f(X1) + f(X2) + …….. + f(Xn) ) 。

交叉(Crossover):2条染色体交换部分基因,来构造下一代的2条新的染色体。例如:

交叉前:

00000|011100000000|10000

11100|000001111110|00101

交叉后:

00000|000001111110|10000

11100|011100000000|00101

染色体交叉是以一定的概率发生的,这个概率记为Pc 。

变异(Mutation):在繁殖过程,新产生的染色体中的基因会以一定的概率出错,称为变异。变异发生的概率记为Pm 。例如:

变异前:

000001110000000010000

变异后:

000001110000100010000

四、基本遗传算法优化 
   精英主义(Elitist Strategy)选择:是基本遗传算法的一种优化。为了防止进化过程中产生的最优解被交叉和变异所破坏,可以将每一代中的最优解原封不动的复制到下一代中。

五、系统主要功能介绍

命题与组卷:主要录入试题和试卷的创建

1、题目管理

对题目信息的管理,完成增、删、改、查、导入、导出功能。

2、试卷管理

创建试卷,完成完成增、删、改、查、导入、导出、手动组卷、自动组卷功能。

查看试卷,对已经组好的试卷可以查看详细内容。

手动组卷,创建试卷时,可以手动从题库中筛选试题。

自动组卷,创建试卷时,可以根据用户的要求随机从题库中筛选试题(通过遗传算法实现)。

测试管理,可以将生成的试卷给用户填写。分两种状态,可以启动试卷,也可以停止。

成绩统计,主要针对用户的考试成绩和作业成绩进行统计。

评阅管理,对已完成的试卷和作业进行评阅,系统先会自动打分,教师角色可以再次人式评阅。

前端学生登录,要以看到教师在后台新创建并启动的试卷

查看成绩,学生可以查看自己已经完成的试题成绩。

以上是系统部分功能,如果需要更详细的内容请私信小编。

  六、接下来看一下主要的代码:

package com.online.study.common.arithmetic;

import com.online.study.entity.Question;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 遗传算法组卷实现类
 */
@Component
public class GA {
    /**
     * 变异概率
     */
    private static final double mutationRate = 0.085;
    /**
     * 精英主义
     */
    private static final boolean elitism = true;
    /**
     * 淘汰数组大小
     */
    private static final int tournamentSize = 5;


    // 进化种群
    public static Population evolvePopulation(Population pop, RuleBean rule, List<Question> questionList) {
        Population newPopulation = new Population(pop.getLength());
        int elitismOffset;
        // 精英主义
        if (elitism) {
            elitismOffset = 1;
            // 保留上一代最优秀个体
            GetPaper fitness = pop.getFitness();
            fitness.setId(0);
            newPopulation.setPaper(0, fitness);
        }
        // 种群交叉操作,从当前的种群pop来创建下一代种群newPopulation
        for (int i = elitismOffset; i < newPopulation.getLength(); i++) {
            // 较优选择parent
            GetPaper parent1 = select(pop);
            GetPaper parent2 = select(pop);
            while (parent2.getId() == parent1.getId()) {
                parent2 = select(pop);
            }
            // 交叉
            GetPaper child = crossover(parent1, parent2, rule, questionList);
            child.setId(i);
            newPopulation.setPaper(i, child);
        }
        // 种群变异操作
        GetPaper tmpPaper;
        for (int i = elitismOffset; i < newPopulation.getLength(); i++) {
            tmpPaper = newPopulation.getPaper(i);
            mutate(tmpPaper, questionList);
            // 计算知识点覆盖率与适应度
            tmpPaper.setKpCoverage(rule);
            tmpPaper.setAdaptationDegree(rule, Global.KP_WEIGHT, Global.DIFFCULTY_WEIGHt);
        }
        return newPopulation;
    }

    /**
     * 交叉算子
     *
     * @param parent1
     * @param parent2
     * @return
     */
    public static GetPaper crossover(GetPaper parent1, GetPaper parent2, RuleBean rule, List<Question> questionList) {
        GetPaper child = new GetPaper(parent1.getQuestionSize());
        int s1 = (int) (Math.random() * parent1.getQuestionSize());
        int s2 = (int) (Math.random() * parent1.getQuestionSize());

        // parent1的startPos endPos之间的序列,会被遗传到下一代
        int startPos = s1 < s2 ? s1 : s2;
        int endPos = s1 > s2 ? s1 : s2;
        for (int i = startPos; i < endPos; i++) {
            child.saveQuestion(i, parent1.getQuestion(i));
        }

        // 继承parent2中未被child继承的question
        // 防止出现重复的元素
        for (int i = 0; i < startPos; i++) {
            if (!child.containsQuestion(parent2.getQuestion(i))) {
                child.saveQuestion(i, parent2.getQuestion(i));
            } else {
                int type = getTypeByIndex(i, rule);

                // 选择指定类型和知识点的试题数组
                Question[] singleArray = questionList.stream().filter(q -> q.getType() == type).filter(q -> q.getCtypeId() == rule.getCtypeId()).filter(q -> q.getPointId() == 1 || q.getPointId() == 2).toArray(Question[]::new);

                child.saveQuestion(i, singleArray[(int) (Math.random() * singleArray.length)]);

            }
        }
        for (int i = endPos; i < parent2.getQuestionSize(); i++) {
            if (!child.containsQuestion(parent2.getQuestion(i))) {
                child.saveQuestion(i, parent2.getQuestion(i));
            } else {
                int type = getTypeByIndex(i, rule);
                //查数据库
                Question[] singleArray = questionList.stream().filter(q -> q.getType() == type).filter(q -> q.getCtypeId() == rule.getCtypeId()).filter(q -> q.getPointId() == 1 || q.getPointId() == 2).toArray(Question[]::new);
                child.saveQuestion(i, singleArray[(int) (Math.random() * singleArray.length)]);
            }
        }

        return child;
    }

    private static int getTypeByIndex(int index, RuleBean rule) {
        int type = 0;
        // 单选
        if (index < rule.getSingleNum()) {
            type = 1;
        } else if (index < rule.getSingleNum() + rule.getCompleteNum()) {
            // 填空
            type = 2;
        } else {
            // 主观
            type = 3;
        }
        return type;
    }

    /**
     * 突变算子 每个个体的每个基因都有可能突变
     *
     * @param paper
     */
    public static void mutate(GetPaper paper, List<Question> questionList) {
        Question tmpQuestion;
        List<Question> list;
        int index;
        for (int i = 0; i < paper.getQuestionSize(); i++) {
            if (Math.random() < mutationRate) {
                // 进行突变,第i道
                tmpQuestion = paper.getQuestion(i);
                Question tempQuetion2 = paper.getQuestion(i);
                // 从题库中获取和变异的题目类型一样分数相同的题目(不包含变异题目)
                list = questionList.stream().filter(q -> q.getType() == tempQuetion2.getType()).filter(q -> q.getCtypeId() == tempQuetion2.getCtypeId()).filter(q -> q.getScore() == tempQuetion2.getScore()).collect(Collectors.toList());

                if (list.size() > 0) {
                    // 随机获取一道
                    index = (int) (Math.random() * list.size());
                    // 设置分数
                    list.get(index).setScore(tmpQuestion.getScore());
                    paper.saveQuestion(i, list.get(index));
                }
            }
        }
    }

    /**
     * 选择算子
     *
     * @param population
     */
    private static GetPaper select(Population population) {
        Population pop = new Population(tournamentSize);
        for (int i = 0; i < tournamentSize; i++) {
            pop.setPaper(i, population.getPaper((int) (Math.random() * population.getLength())));
            //System.out.println("选择算子========================" + i);
        }
        return pop.getFitness();
    }
}
package com.online.study.common.arithmetic;

import com.online.study.entity.Question;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Random;

/**
 * 种群,即多套试卷
 */
@Component
public class Population {


    public Population() {
    }

    /**
     * 试卷集合
     */
    private GetPaper[] papers;

    /**
     * 初始种群
     *
     * @param populationSize 种群规模
     * @param initFlag       初始化标志 true-初始化
     * @param rule           规则bean
     */
    public Population(int populationSize, boolean initFlag, RuleBean rule,List<Question> questionList) {
        papers = new GetPaper[populationSize];

        if (initFlag) {
            GetPaper paper;
            Random random = new Random();
            for (int i = 0; i < populationSize; i++) {
                paper = new GetPaper();
                paper.setId(i + 1);
                while (paper.getTotalScore() != rule.getTotalMark()) {

                    paper.getQuestionList().clear();

                    // 单选题
                    if (rule.getSingleNum() > 0) {
                        generateQuestion(1, random, rule.getSingleNum(), rule.getSingleScore(),rule.getCtypeId(),paper,questionList);
                     }
                    // 填空题
                    if (rule.getCompleteNum() > 0) {
                        generateQuestion(2, random, rule.getCompleteNum(), rule.getCompleteScore(),rule.getCtypeId(),paper,questionList);
                    }
                    // 主观题
                    if (rule.getSubjectiveNum() > 0) {
                        generateQuestion(3, random, rule.getSubjectiveNum(), rule.getSubjectiveScore(),rule.getCtypeId(),paper,questionList);
                    }
                }
                // 计算试卷知识点覆盖率
                paper.setKpCoverage(rule);

                // 计算试卷适应度
                paper.setAdaptationDegree(rule, Global.KP_WEIGHT, Global.DIFFCULTY_WEIGHt);

                papers[i] = paper;
            }
        }
    }


    private void generateQuestion(int type, Random random, int qustionNum, double score,int ctypeId,GetPaper paper,List<Question> questionList) {

        Question[] singleArray = questionList.stream().filter(q->q.getType()==type).filter(q->q.getCtypeId()==ctypeId).filter(q->q.getPointId()==1 || q.getPointId()==2).toArray(Question[]::new);

        if (singleArray.length < qustionNum) {

            return;
        }
        Question tmpQuestion=null;
        for (int j = 0; j < qustionNum; j++) {
            int index = random.nextInt(singleArray.length - j);

            // 初始化分数
            singleArray[index].setScore(score);

            // 保证不会重复添加试题
            if(paper.isExit(paper.getQuestionList(),singleArray[index].getId())) {
                paper.addQuestion(singleArray[index]);
            }
        }
    }

    /**
     * 获取种群中最优秀个体
     *
     * @return
     */
    public GetPaper getFitness() {
        GetPaper paper = papers[0];
        for (int i = 1; i < papers.length; i++) {
            if (paper.getAdaptationDegree() < papers[i].getAdaptationDegree()) {
                paper = papers[i];
            }
        }
        return paper;
    }

    public Population(int populationSize) {
        papers = new GetPaper[populationSize];
    }

    /**
     * 获取种群中某个个体
     *
     * @param index
     * @return
     */
    public GetPaper getPaper(int index) {
        return papers[index];
    }

    /**
     * 设置种群中某个个体
     *
     * @param index
     * @param paper
     */
    public void setPaper(int index, GetPaper paper) {
        papers[index] = paper;
    }

    /**
     * 返回种群规模
     *
     * @return
     */
    public int getLength() {
        return papers.length;
    }

}

controller调用算法

    /***遗传算法组*/
    @PostMapping("/takePaperAuto")
    public Result takePaperAuto(@RequestBody PaperDTO paperDTO) {
        // 删除老的试卷
        UpdateWrapper<PaperQuestion> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("paper_id", paperDTO.getPaperId());
        paperQuestionService.remove(updateWrapper);
        QueryWrapper<Question> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("ctype_id",paperDTO.getCtypeId());
        // 根据课程id查出所有该课程的题目,然后再根据type去分
        List<Question> questionList = questionService.list(queryWrapper);
        List<Question> type1List = questionList.stream().filter(question -> question.getType() == 1).collect(Collectors.toList());  // 选择
        List<Question> type2List = questionList.stream().filter(question -> question.getType() == 2).collect(Collectors.toList());  // 填空
        List<Question> type3List = questionList.stream().filter(question -> question.getType() == 3).collect(Collectors.toList());  // 问答
        if (null!=paperDTO.getType1() && type1List.size() < paperDTO.getType1()) {
            throw new ServiceException("-1", "选择题数量不足");
        }
        if (null!=paperDTO.getType2() && type2List.size() < paperDTO.getType2()) {
            throw new ServiceException("-1", "判断题数量不足");
        }
        if (null!=paperDTO.getType3() && type3List.size() < paperDTO.getType3()) {
            throw new ServiceException("-1", "问答题数量不足");
        }
        // 开始随机组卷
        List<PaperQuestion> paperQuestion = getPaperQuestionByGA(paperDTO,paperDTO.getPaperId(),questionList);
        paperQuestionService.saveBatch(paperQuestion);
        return Result.success();
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值