遗传算法应用自动组卷

参考: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();
        }
    }
}

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值