基于MATLAB的遗传算法自动组卷问题实现

介绍

  • 去年年底写遗传算法组卷问题时,网上也看到了不少资料,但是大部分都是讲理论,少见有讲具体实现的,本文主要介绍自己当时完成遗传算法自动组卷问题编程的实现思路,SGA,组卷实数编码、轮盘赌等基础知识在此不做赘述,用作个人回顾,分享,同时如有错误或更好的思路希望指出

实现说明

  • 采用基础遗传算法(SGA)
  • 选择算子:轮盘赌
  • 交叉算子:随机单点交叉
  • 变异算子:随机单点不重复变异
  • 语言:Matlab
  • 结果:实现 难度、知识点、曝光时间 三个维度满足条件的自动组卷,并未着手去解决早熟等问题,题库通过随机数模拟

建模

  • 自动组卷问题数学模型即为多维约束条件下求最优解,即从题库抽取满足诸如:题型、难度、知识点章节、曝光时间、总分 等多个条件的试题组卷,因此题目必须具备以上属性用以判断,可用使用对象或者数组实现,本人选择偷懒,通过一维数组来描述题目,那么题库即为二维数组,组卷问题即为从二维数组抽取部分数组(初始种群)对他们进行打分(计算适应度),选取高分试卷的过程;
    基于MATLAB的遗传算法自动组卷问题实现_第1张图片

  • 具体实现

  • 题目:一维数组,[id、type、score、difficult、section、interval],id为题目编号,用于区分题型确定题目分数,difficult为题目难度系数,section为题目所在章节,interval为题目曝光时间,即最近一次被抽到是几次前,避免高频题出现;
  • 题库:通过for循环生成题库,其中id即为index,为简化问题,题型和分数通过判断index所在区间即可,例如生成2000题目数量,4个题型的题库,1-500为选择,501-1000为判断以此递推,分数绑定题型,这样即可不用考虑题型,分数,题目数量等约束条件;而difficult、section、interval通过随机数产生,模拟真实题目情况,实现如下,problemDB为数据库,i为循环下标
     problemDB(i,4) = randperm(4,1)/5; %难度系数,题目划分为正确率0.2/0.4/0.6/0.8四档
     problemDB(i,5) = randperm(10,1); %知识点,即章节,随机指定,划分十个章节
     problemDB(i,6) = randperm(5,1); %曝光时间,1-5,曝光时间>3视为近期未出现
    
    基于MATLAB的遗传算法自动组卷问题实现_第2张图片
  • 初始种群:一套试题即为一个基因链,以试题id编码,本文试卷选取20个题目,四个题型,每个题型均为5个,初始种群选择20套试卷为例,即为20*20的二维数组;
    • 其中要注意的问题即为重复抽题,randperm虽为生成随机不重复的随机数,但是逐次生成试卷时还是会产生与之前产生试卷抽到相同题目的情况,每次抽取时遍历之前的基因链又很麻烦,此处可再次偷懒,通过randperm在每个题型一次性抽100个,然后按顺序(由于已经是随机数,不需要再随机抽取)每个题型截取5个组成一套试卷;结合前文题型可由题目id来判断,因此抽第一个题型题目即为随机产生100个1-500之间的数字,抽第二个题型即为随机产生100个501-1000之间的数字,以此类推;
      基因链
    • 可以看到每一条基因链均为5个选择(1-500),5个多选(501-1000)以此递推组成,无重复试题,20条基因链组成初始种群
  • 计算适应度:计算各约束条件的满足情况打分,通过双重for循环,遍历种群基因链每个基因,通过基因即题目id,传入题库获取该题难度、章节、曝光间隔等信息
    • 难度:整条基因链即整套题难度取均值,计算与预期难度差值打分;
    • 章节:满足预期章节的才计分;
    • 曝光时间:曝光时间长于一定程度的才计分;
    • 适应度:取上述三个约束条件,以一定的比例求加权和;
    for i = 1:population_size
        for k = 1: chromosome_size
            id = population(i,k);
            difficult(i) = difficult(i) + problemDB(id,4)/chromosome_size;  %计算每条基因链平均难度        
            if problemDB(id,5) == 4 %预设要考第四章
                points(i) = points(i) + 1; %计算要考察的章节知识点覆盖率
            end          
            if problemDB(id,6) > 3
               interval(i) = interval(i) + 1; %计算每条基因链曝光时间>3的个数
            end        
        end
    % 计算每条基因链适应度,预设难度0.4,难度、章节、曝光时间比例0.4 0.3 0.3    
    adapt(i) = -0.4*abs(difficult(i) - 0.4) + 0.3*points(i) + 0.3*interval(i);
    [best_adp, best_indiv] = max(adapt); %记录精英个体
    end
    
    • 计算可以得到1*20的数组,即对应20条基因链(试卷)的适应度,如果要采用精英策略,可保存最大适应度个体
  • 选择算子:采用轮盘赌,轮盘赌的算法实现网上很对,此处就不贴了,需要注意的是:初始种群数量为20,则要进行20次轮盘赌选择出20个基因链,因此会有大量重复个体;
  • 交叉算子:由于选择群体已经是轮盘赌随机选取的,此处父类个体和母类个体不再通过随机抽取,而是将前十条基因链作为父类,后十条基因链作为母类,通过MATLAB的 rand 产生随机数,小于等于设定的交叉概率即父母类进行单点交叉,否则不变
    father = population_sel(1:population_size/2,:);
    mother = population_sel(population_size/2+1:population_size,:);
    population_new = zeros(population_size,chromosome_size);
    for i = 1:population_size/2
    	change_pos = randperm(chromosome_size,1); %随机交叉位置
    	if rand <= 0.6  % 0.6概率交叉    
        	population_new(i*2-1,:) = [father(i,1:change_pos),mother(i,change_pos+1:chromosome_size)];
        	population_new(i*2,:) = [mother(i,1:change_pos),father(i,change_pos+1:chromosome_size)];
    	else %直接遗传
        	population_new(i*2-1,:) = father(i,:);
        	population_new(i*2,:) = mother(i,:);
    	end
    end
    
  • 变异算子:同交叉算子,通过随机数跟预设变异概率比较进行判断是否需要变异;较为麻烦的是由于题型绑定了基因链位置,即当随机变异位置发生在基因链1-5时,需要选择1-500的题目进行变异,发生在6-10位时需要选择501-1000的题目,且新变异产生的题目不可是基因链上已有的;
    for i = 1:population_size
        mutation_pos = randperm(chromosome_size,1); %随机变异位置
        % 判断随机位置的题型
        if mutation_pos<=5
            type = 0;
        elseif (5
  • 将以上各函数封装,按照SGA流程图执行即可,进化200代,记录历代最优,结果如下
    基于MATLAB的遗传算法自动组卷问题实现_第3张图片

关于代码

  • 代码贴github上了,有疑问可以直接留言
  • https://github.com/hxy941025/GA-/tree/master

总结

  • 关于上述结果的两点问题:
    • 最大适应度下降:由于未采取精英策略保留每一代的最优个体,会导致最优个体在轮盘赌中存在没被选中的可能;
    • 适应度长期平稳不变:由于记录的为种群最优个体,当交叉变异未产生更加优秀的个体时,最大适应度就不会有变化,且最优个体有更大的概率被选择进行繁殖,故后期种群中会存在严重同化现象,此时产生优秀个体主要靠变异
  • 个人认为SGA不加精英策略的遗传算法基本没法用,精英策略的实现很简单,只需要将历代最优个体人为不经过选择放入下一代即可;但是会导致更快的进入早熟收敛陷入局部最优;

你可能感兴趣的:(基于MATLAB的遗传算法自动组卷问题实现)