遗传算法中通过模仿生物界生物遗传方式,实现的算法,本次组卷生成案例主要使用到了其中一部分概念:染色体编码传递、初始化种群、杂交、变异、进化、最优适应者。
个人理解上可能有些地方比较欠缺,望大家指正!!
采用实数编码作为替代,将试题id作为基因,试卷和染色体建立映射关系,由于题库仅仅含有选择题,
则没有题型分类一说,则当组套10道题的试卷,组卷结果:
(2,33,22,11,44,35,6,4,445,29)【注:如果有题型一说,则可以分类[1,2,44 | 3,33| 12 ,44 ]】
for (SubjectInfo subjectInfo : subjectInfoList) {
list.add(subjectInfo.getSubjectId());
}
对应方法:GeneticUtil.init();
初始化试卷时不采取完全随机的方式.通过分析不难发现,组卷主要有题型、数量、总分和难度这四个约束条件,在初始化种群的时候,我们可以根据组卷规则随机产生指定数量的题型。
其中:
组卷规则:(注:可自行定义规则)
设置种群大小,由于题库量太大,适当取出一部分题型,优化检索速度。
//种群大小(当种群量大于40倍我们取的题量时,我们可以将题量缩小至20倍)
public static int INIT_SIZE=40;
//取得种群量
public static int GET_SIZE=20;
// 改造种群大小
if(list.size()>INIT_SIZE*subjectSum)
{
for(int i=0;i
对应方法:GeneticUtil. hybrid ();
正常逻辑杂交需要有双亲,但是由于我们是基于题库为种群,所以双亲指定为取出的种群。当杂交时出现重复,我们可以在大种群下取出新的数据
//两点交叉(start,end)
// 上一代start至end的基因会遗传到下一代
for (int i = start; i < end; i++) {
list.set(i, subjectIds.get(i));
}
// 子代继承未继承的父代基因
// 查重
for (int j = 0; j < start; j++) {
if(!list.contains(subjectIds.get(j)))
{
list.set(j, subjectIds.get(j));
}
else{
//重复了就获取新基因
Integer newIds=getNewId(list,subjectIds);
list.set(j, newIds);
}
}
for(int i=end;i
方法:GeneticUtil .variation();
设置一定的概率使基因变异:
基因变异的出现增加了种群的多样性.在本系统中,每个个体的每个基因都有变异的机会,如果随机概率小于变异概率,那么基因就可以突变.突变基因的原则为:与原题的同题型、同分数且同知识点的试题.有研究表明,对于变异概率的选择,在0.1-0.001之间最佳,本例中选取了0.085作为变异概率
//变异概率:85%
public static int variationRate=85;
for (int i = 0; i < subjectIds.size(); i++) {
if (Math.random() * 100 > variationRate) {
//且不能获取此变异题目
Integer variationId=(int) (Math.random() * subjectIds.size());
while(i==variationId)
{
variationId=(int) (Math.random() * subjectIds.size());
}
subjectIds.set(i, subjectIds.get(variationId));
// System.out.println("发生变异!得到新基因:"+variationId);
}
}
方法:preferential()
系统缺乏缺乏精细数据来触发进化规则,目前采用10%淘汰规则,淘汰个体。
for (int i = 0; i < selectSize - 1; i++) {
for (int j = i + 1; j < selectSize; j++) {
tmp = list.get(i);
list.set(i, list.get(j));
list.set(j, tmp);
}
}
for (int i = 0; i < (selectSize - 1) / 10; i++) // 淘汰10%
{
list.set(selectSize - 1 - i, list.get(i));
}
方法:getExcellent()
根据一代代遗传,取出在每次遗传产生的后代,最终适应下来的个体存活,根据存活个体数量,取出存活前N题,组成最优试卷。
List finalResult=new ArrayList<>();
// System.out.println("获取最终代基因");
int i=0;//统计次数
TreeMap tm=new TreeMap<>();
for(int x = 0;x < newList.size();x++) {//循环遍历数组
Integer obj=newList.get(x);
if(!tm.containsKey(obj)) {//是否在tm中
tm.put(obj, 1);//不在tm中则初始化其value值为1
} else {//在tm中
int count = tm.get(obj)+1;//其出现次数增加1
tm.put(obj,count);//在tm中则其value值为count
}
}
List> list = new ArrayList>(tm.entrySet());//将map转换为list便于进行排序
//构造一个排序比较器
Collections.sort (list,new Comparator>() {//使用Collections.sort()方法对这个list进行排序
public int compare(Entry o1,Entry o2) {
//实现降序排序
int z = o2.getValue()-o1.getValue();
return z;
}
});
for(Map.Entry mapping:list) {//遍历list
if(i++
通过调用build()方法,执行遗传算法组卷。
// 生成
public static List build(List subjects, int subjectSum, int subjectEasy) {
if(subjects.size() subjectIds = (List) init(subjects,subjectSum);
// 2.适应度:试题数量、难度
int count = 0;
int easy = 0;
List newList = new ArrayList();
while (count < subjectSum && easy <=subjectEasy) {
// System.out.println("第"+(count+1)+"代");
// 3.杂交
newList = hybrid(subjectIds);
// 4.变异
newList = variation(newList);
// 5.择优
newList = preferential(newList);
count++;
subjectIds=newList;
}
//6.在最后一代获取最优基因
return getExcellent(subjectSum,newList);
}