参考:https://www.cnblogs.com/artwl/archive/2011/05/20/2052262.html#!comments
题库是自己用excle组了一些题目如:
代码
using Spire.Xls;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
show s = new show();
s.Show();
Console.ReadKey();
}
}
public class show
{
#region 权重
/// <summary>
/// 知识点分布权重
/// </summary>
public static double kpcoverage = 0.10;
/// <summary>
/// 试卷难度系数权重
/// </summary>
public static double difficulty = 0.90;
#endregion
/// <summary>
/// 初始种群
/// </summary>
/// <param name="count">个体数量</param>
/// <param name="paper">期望试卷</param>
/// <param name="problemList">题库</param>
/// <returns>初始种群</returns>
public List<Unit> CSZQ(int count, Paper paper, List<Problem> problemList)
{
List<Unit> unitList = new List<Unit>();
int[] eachTypeCount = paper.EachTypeCount;
Unit unit;
Random rand = new Random();
for (int i = 0; i < count; i++)
{
unit = new Unit();
unit.ID = i + 1;
unit.AdaptationDegree = 0.00;
//总分限制
while (paper.TotalScore != unit.SumScore)
{
unit.ProblemList.Clear();
//各题型题目数量限制
for (int j = 0; j < eachTypeCount.Length; j++)
{
List<Problem> oneTypeProblem = problemList
.Where(o => o.Type == (j + 1))
.Where(p => IsContain(paper, p))
.ToList();
Problem temp = new Problem();
for (int k = 0; k < eachTypeCount[j]; k++)
{
//选择不重复的题目
int index = rand.Next(0, oneTypeProblem.Count - k);
unit.ProblemList.Add(oneTypeProblem[index]);
temp = oneTypeProblem[oneTypeProblem.Count - 1 - k];
oneTypeProblem[oneTypeProblem.Count - 1 - k] = oneTypeProblem[index];
oneTypeProblem[index] = temp;
}
}
}
unitList.Add(unit);
}
//计算知识点覆盖率及适应度
// unitList = GetKPCoverage(unitList, paper);
unitList = GetAdaptationDegree(unitList, paper, kpcoverage, difficulty);
return unitList;
}
/// <summary>
/// 计算知识点覆盖率
/// </summary>
/// <param name="unitList">种群</param>
/// <param name="paper">期望试卷</param>
/// <returns>List</returns>
public List<Unit> GetKPCoverage(List<Unit> unitList, Paper paper)
{
List<string> kp;
for (int i = 0; i < unitList.Count; i++)
{
kp = new List<string>();
unitList[i].ProblemList.ForEach(delegate (Problem p)
{
kp.AddRange(p.Points);
});
//个体所有题目知识点并集跟期望试卷知识点交集
var common = kp.Intersect(paper.Points);
unitList[i].KPCoverage = common.Count() * 1.00 / paper.Points.Count;
}
return unitList;
}
/// <summary>
/// 计算种群适应度
/// </summary>
/// <param name="unitList">种群</param>
/// <param name="paper">期望试卷</param>
/// <param name="KPCoverage">知识点分布在适应度计算中所占权重</param>
/// <param name="Difficulty">试卷难度系数在适应度计算中所占权重</param>
/// <returns>List</returns>
public List<Unit> GetAdaptationDegree(List<Unit> unitList, Paper paper, double KPCoverage, double Difficulty)
{
unitList = GetKPCoverage(unitList, paper);
for (int i = 0; i < unitList.Count; i++)
{
unitList[i].AdaptationDegree = 1 - (1 - unitList[i].KPCoverage) * KPCoverage - Math.Abs(unitList[i].Difficulty - paper.Difficulty) * Difficulty;
}
return unitList;
}
/// <summary>
/// 选择算子(轮盘赌选择)
/// </summary>
/// <param name="unitList">种群</param>
/// <param name="count">选择次数</param>
/// <returns>进入下一代的种群</returns>
public List<Unit> Select(List<Unit> unitList, int count)
{
List<Unit> selectedUnitList = new List<Unit>();
//种群个体适应度和
double AllAdaptationDegree = 0;
unitList.ForEach(delegate (Unit u)
{
AllAdaptationDegree += u.AdaptationDegree;
});
Random rand = new Random();
while (selectedUnitList.Count != count)
{
//选择一个0—1的随机数字
double degree = 0.00;
double randDegree = rand.Next(1, 100) * 0.01 * AllAdaptationDegree;
//选择符合要求的个体
for (int j = 0; j < unitList.Count; j++)
{
degree += unitList[j].AdaptationDegree;
if (degree >= randDegree)
{
//不重复选择
if (!selectedUnitList.Contains(unitList[j]))
{
selectedUnitList.Add(unitList[j]);
}
break;
}
}
}
return selectedUnitList;
}
/// <summary>
/// 交叉算子
/// </summary>
/// <param name="unitList">种群</param>
/// <param name="count">交叉后产生的新种群个体数量</param>
/// <param name="paper">期望试卷</param>
/// <returns>List</returns>
public List<Unit> Cross(List<Unit> unitList, int count, Paper paper)
{
List<Unit> crossedUnitList = new List<Unit>();
Random rand = new Random();
while (crossedUnitList.Count != count)
{
//随机选择两个个体
int indexOne = rand.Next(0, unitList.Count);
int indexTwo = rand.Next(0, unitList.Count);
Unit unitOne;
Unit unitTwo;
if (indexOne != indexTwo)
{
unitOne = unitList[indexOne];
unitTwo = unitList[indexTwo];
//随机选择一个交叉位置
int crossPosition = rand.Next(0, unitOne.ProblemCount - 2);
//保证交叉的题目分数合相同
double scoreOne = unitOne.ProblemList[crossPosition].Score + unitOne.ProblemList[crossPosition + 1].Score;
double scoreTwo = unitTwo.ProblemList[crossPosition].Score + unitTwo.ProblemList[crossPosition + 1].Score;
if (scoreOne == scoreTwo)
{
//两个新个体
Unit unitNewOne = new Unit();
unitNewOne.ProblemList.AddRange(unitOne.ProblemList);
Unit unitNewTwo = new Unit();
unitNewTwo.ProblemList.AddRange(unitTwo.ProblemList);
//交换交叉位置后面两道题
for (int i = crossPosition; i < crossPosition + 2; i++)
{
unitNewOne.ProblemList[i] = new Problem(unitTwo.ProblemList[i]);
unitNewTwo.ProblemList[i] = new Problem(unitOne.ProblemList[i]);
}
//添加到新种群集合中
unitNewOne.ID = crossedUnitList.Count;
unitNewTwo.ID = unitNewOne.ID + 1;
if (crossedUnitList.Count < count)
{
crossedUnitList.Add(unitNewOne);
}
if (crossedUnitList.Count < count)
{
crossedUnitList.Add(unitNewTwo);
}
}
}
//过滤重复个体
crossedUnitList = crossedUnitList.Distinct(new ProblemComparer()).ToList();
}
//计算知识点覆盖率及适应度
crossedUnitList = GetKPCoverage(crossedUnitList, paper);
crossedUnitList = GetAdaptationDegree(crossedUnitList, paper, kpcoverage, difficulty);
return crossedUnitList;
}
/// <summary>
/// 变异算子
/// </summary>
/// <param name="unitList">种群</param>
/// <param name="problemList">题库</param>
/// <param name="paper">期望试卷</param>
/// <returns>List</returns>
public List<Unit> Change(List<Unit> unitList, List<Problem> problemList, Paper paper)
{
Random rand = new Random();
int index = 0;
//double moutation_index=0.0;
unitList.ForEach(delegate (Unit u)
{
//随机选择一道题
index = rand.Next(0, u.ProblemList.Count);
//moutation_index = rand.Next(0,1);
//if(moutation_index<=0.085)
//{
//}
Problem temp = u.ProblemList[index];
//得到这道题的知识点
Problem problem = new Problem();
for (int i = 0; i < temp.Points.Count; i++)
{
if (paper.Points.Contains(temp.Points[i]))
{
problem.Points.Add(temp.Points[i]);
}
}
//从数据库中选择包含此题有效知识点的同类型同分数不同题号试题
var otherDB = from a in problemList
where a.Points.Intersect(problem.Points).Count() > 0
select a;
List<Problem> smallDB = otherDB.Where(p => IsContain(paper, p)).Where(o => o.Score == temp.Score && o.Type == temp.Type && o.ID != temp.ID).ToList();
//从符合要求的试题中随机选一题替换
if (smallDB.Count > 0)
{
int changeIndex = rand.Next(0, smallDB.Count);
u.ProblemList[index] = smallDB[changeIndex];
}
});
//计算知识点覆盖率跟适应度
unitList = GetKPCoverage(unitList, paper);
unitList = GetAdaptationDegree(unitList, paper, kpcoverage, difficulty);
return unitList;
}
/// <summary>
/// 题目知识点是否符合试卷要求
/// </summary>
/// <param name="paper">期望试卷</param>
/// <param name="problem">一首试题</param>
/// <returns>bool</returns>
private bool IsContain(Paper paper, Problem problem)
{
for (int i = 0; i < problem.Points.Count; i++)
{
if (paper.Points.Contains(problem.Points[i]))
{
return true;
}
}
return false;
}
public void Show()
{
//题库
DB db = new DB();
//期望试卷
Paper paper = new Paper()
{
ID = 1,
TotalScore = 100,
Difficulty = 0.5,
Points = new List<string>() { "java基础", "数据库基础", "JEE基础", "开源框架", "web开发" },
EachTypeCount = new[] {5,2}
};
//迭代次数计数器
int count = 1;
//适应度期望值
double expand = 0.98;
//最大迭代次数
int runCount = 100;
//初始化种群
List<Unit> unitList = CSZQ(20, paper, db.ProblemDB);
foreach (Unit item in unitList)
{
ShowUnit(item);
}
Console.WriteLine("\n\n -------遗传算法组卷系统(http://www.cnblogs.com/durongjian/)---------\n\n");
Console.WriteLine("初始种群:");
Console.WriteLine("-----------------------迭代开始------------------------");
//开始迭代
while (!IsEnd(unitList, expand))
{
Console.WriteLine("在第 " + (count++) + " 代未得到结果");
if (count > runCount)
{
Console.WriteLine("计算 " + runCount + " 代仍没有结果,请重新设计条件!");
break;
}
//选择
unitList = Select(unitList, 10);
//交叉
unitList = Cross(unitList, 20, paper);
//是否可以结束(有符合要求试卷即可结束)
if (IsEnd(unitList, expand))
{
break;
}
//变异
unitList = Change(unitList, db.ProblemDB, paper);
}
if (count <= runCount)
{
Console.WriteLine("在第 " + count + " 代得到结果,结果为:\n");
Console.WriteLine("期望试卷难度:" + paper.Difficulty + "\n");
ShowResult(unitList, expand);
}
}
/// <summary>
/// 是否达到目标
/// </summary>
/// <param name="unitList">种群</param>
/// <param name="endcondition">结束条件(适应度要求)</param>
/// <returns>bool</returns>
public bool IsEnd(List<Unit> unitList, double endcondition)
{
if (unitList.Count > 0)
{
for (int i = 0; i < unitList.Count; i++)
{
if (unitList[i].AdaptationDegree >= endcondition)
{
return true;
}
}
}
return false;
}
/// <summary>
/// 显示结果
/// </summary>
/// <param name="unitList">种群</param>
/// <param name="expand">期望适应度</param>
public void ShowResult(List<Unit> unitList, double expand)
{
unitList.OrderBy(o => o.ID).ToList().ForEach(delegate (Unit u)
{
if (u.AdaptationDegree >= expand)
{
Console.WriteLine("第" + u.ID + "套:");
Console.WriteLine("题目数量\t知识点分布\t难度系数\t适应度");
Console.WriteLine(u.ProblemCount + "\t\t" + u.KPCoverage.ToString("f2") + "\t\t" + u.Difficulty.ToString("f2") + "\t\t" + u.AdaptationDegree.ToString("f2") + "\n");
u.ProblemList.ForEach(delegate (Problem p)
{
Console.Write(p.ID + "\t");
});
Console.WriteLine();
}
});
}
/// <summary>
/// 显示种群个体题目编号
/// </summary>
/// <param name="u">种群个体</param>
public void ShowUnit(Unit u)
{
Console.WriteLine("编号\t知识点分布\t难度系数");
Console.WriteLine(u.ID + "\t" + u.KPCoverage.ToString("f2") + "\t\t" + u.Difficulty.ToString("f2"));
u.ProblemList.ForEach(delegate (Problem p)
{
Console.Write(p.ID + "\t");
});
Console.WriteLine();
}
}
public class Problem
{
public Problem()
{
ID = 0;
Type = 0;
Score = 0;
Difficulty = 0.00;
Points = new List<string>();
}
public Problem(Problem p)
{
this.ID = p.ID;
this.Type = p.Type;
this.Score = p.Score;
this.Difficulty = p.Difficulty;
this.Points = p.Points;
}
/// <summary>
/// 编号
/// </summary>
public int ID { get; set; }
/// <summary>
/// 题型(1、2、3、4、5对应单选,多选,判断,填空,问答)
/// </summary>
public int Type { get; set; }
/// <summary>
/// 分数
/// </summary>
public int Score { get; set; }
/// <summary>
/// 难度系数
/// </summary>
public double Difficulty { get; set; }
/// <summary>
/// 知识点
/// </summary>
public List<string> Points { get; set; }
}
public class DB
{
/// <summary>
/// 题库
/// </summary>
public List<Problem> ProblemDB;
public DB()
{
Workbook workbook = new Workbook();
workbook.LoadFromFile(@"C:\Users\YingwenLian\Desktop\demo.xlsx");
Worksheet sheet = workbook.Worksheets[0];
DataTable dt = sheet.ExportDataTable();
ProblemDB = new List<Problem>();
Problem model;
Random rand = new Random();
//List<string> Points;
for (int i = 1; i < dt.Rows.Count; i++)
{
model = new Problem();
model.Type = Convert.ToInt32(dt.Rows[i][1]);
model.Difficulty = Convert.ToDouble(dt.Rows[i][4]);
model.Points = dt.Rows[i][5].ToString().Split(',').ToList();
model.Score = Convert.ToInt32(dt.Rows[i][3]);
model.ID = Convert.ToInt32(dt.Rows[i][0]);
//试题难度系数取0.3到1之间的随机值
// model.Difficulty = rand.Next(30, 100) * 0.01;
//单选题1分
//if (i < 1001)
//{
// model.Type = 1;
// model.Score = 5;
//}
////多选题单选题2分
//if (i > 1000 && i < 2001)
//{
// model.Type = 2;
// model.Score = 2;
//}
////判断题2分
//if (i > 2000 && i < 3001)
//{
// model.Type = 3;
// model.Score = 2;
//}
//填空题1—4分
//if (i > 1000 && i < 2001)
//{
// model.Type = 2;
// model.Score = 10;
// //model.Score = rand.Next(1, 5);
//}
////问答题分数为难度系数*10
//if (i > 2000 && i < 3001)
//{
// model.Type = 3;
// model.Score = model.Difficulty > 0.3 ? (int)(double.Parse(model.Difficulty.ToString("f1")) * 10) : 3;
//}
//Points = new List<string>();
////每题1到4个知识点
//int count = rand.Next(1, 5);
//for (int j = 0; j < count; j++)
//{
// Points.Add("string" + j.ToString());
//}
//model.Points = Points;
ProblemDB.Add(model);
}
//int s = 1;
}
}
public class Paper
{
/// <summary>
/// 编号
/// </summary>
public int ID { get; set; }
/// <summary>
/// 总分
/// </summary>
public int TotalScore { get; set; }
/// <summary>
/// 难度系数
/// </summary>
public double Difficulty { get; set; }
/// <summary>
/// 知识点
/// </summary>
public List<string> Points { get; set; }
/// <summary>
/// 各种题型题数
/// </summary>
public int[] EachTypeCount { get; set; }
}
public class Unit
{
public Unit()
{
ID = 0;
AdaptationDegree = 0.00;
KPCoverage = 0.00;
ProblemList = new List<Problem>();
}
/// <summary>
/// 编号
/// </summary>
public int ID { get; set; }
/// <summary>
/// 适应度
/// </summary>
public double AdaptationDegree { get; set; }
/// <summary>
/// 难度系数(本试卷所有题目分数*难度系数/总分)
/// </summary>
public double Difficulty
{
get
{
double diff = 0.00;
ProblemList.ForEach(delegate (Problem p)
{
diff += p.Difficulty * p.Score;
});
return diff / SumScore;
}
}
/// <summary>
/// 题目数量
/// </summary>
public int ProblemCount
{
get
{
return ProblemList.Count;
}
}
/// <summary>
/// 总分
/// </summary>
public int SumScore
{
get
{
int sum = 0;
ProblemList.ForEach(delegate (Problem p)
{
sum += p.Score;
});
return sum;
}
}
/// <summary>
/// 知识点分布
/// </summary>
public double KPCoverage { get; set; }
/// <summary>
/// 题目
/// </summary>
public List<Problem> ProblemList { get; set; }
}
public class ProblemComparer : IEqualityComparer<Unit>
{
public bool Equals(Unit x, Unit y)
{
bool result = true;
for (int i = 0; i < x.ProblemList.Count; i++)
{
if (x.ProblemList[i].ID != y.ProblemList[i].ID)
{
result = false;
break;
}
}
return result;
}
public int GetHashCode(Unit obj)
{
return obj.ToString().GetHashCode();
}
}
}