基于上一篇(228条消息) 基于遗传算法的排课系统C#代码实现_恋try的博客-CSDN博客的优化
将教学时间进行了优化,将全部教学时间段用T1-Tn,如果周一有4个时间段,则其中T1-T4为星期一的教学时间段,T5-T8为星期二的教学时间段,其他的教学时间段依次类推。
代码在最后,在此对排课系统的一些思考
一个系统是可以同时有多个排课方案的,可能根据不同的情况、不同的学期、不同的月份都会有相对独立的排课方案;
用户在排完课后,需要输入此次排课的主体,然后将主体和排课方案信息一起保持;
在进行排课前应该对教学任务的规模和教室及教学时间之间的比例关系有一定的限制,如果超出限制则能产生有效课表的几率很小甚至没有,那么在这种情况下,就不应该进行排课,应该给出相应的提示信息;
在排课前,对这些信息进行比对
查看课表的维度有很多种:班级学生、教师、教室。应该从不同维度可以查看。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Education.Service
{
public class Schedule
{
public int etaskid;//任务id
public string roomid;//教室id
public string etimeid;//上课时间id,将时间拉开,将教学时间用时间段来表示
//将全部教学时间段用T1-Tn,如果周一有4个时间段,则其中T1-T4为星期一的教学时间段,T5-T8为星期二的教学时间段
public Schedule()
{ }
public Schedule(int etaskid)
{
this.etaskid = etaskid;
}
public void random_init(List<string> rooms,List<string> etimes, int i,out int a,out int b)
{
Random random = new Random(int.Parse(DateTime.Now.ToString("HHmmssfff")) + i);//利用提高随机种子进行随机生成
a = random.Next(rooms.Count());
this.roomid = rooms[a];
b = random.Next(etimes.Count);
this.etimeid = etimes[b];
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
namespace Education.Service
{
public class MyGa
{
private int popsize =100;//种群规模
private double mutprob = 0.3;//种群的变异概率
private int elite = 15;//精英个数
private int maxiter = 500;//进化代数(100-500)
private List<List<Schedule>> population;
public List<Schedule> Education(List<Schedule> schedules, List<string> roomRang,List<string> etimes)
{
int bestScore = 0;
List<Schedule> bestschedule = new List<Schedule>();
// 初始化种群
init_population(schedules, roomRang,etimes);
for (int i = 0; i < this.maxiter; i++)
{
List<List<Schedule>> newPopulation = new List<List<Schedule>>();
//获取冲突结果
Dictionary<List<Schedule>, int> cost_map = schedule_cost(this.population, this.elite);
foreach (List<Schedule> key in cost_map.Keys)
{
//若发现冲突结果为0,则说明咳将其当做最优排课结果返回
bestScore = cost_map[key];
if (bestScore == 0)
{
bestschedule = key;
return bestschedule;
}
}
//精英种群
newPopulation = cost_map.Keys.ToList();
while (newPopulation.Count < this.popsize)
{
List<Schedule> temp = new List<Schedule>();
if ((new Random().Next(0, 10) / 10.0) < this.mutprob)
{
//落在变异概率内,变异
temp = Mutate(newPopulation, roomRang,etimes);
}
else
{
//交叉
temp = Crossover(newPopulation);
}
newPopulation.Add(temp);
}
this.population = newPopulation;
}
return bestschedule;
}
/// <summary>
/// 变异 根据精英种群集合 在将其中随机一个染色体变异后 返回变异后的染色体
/// </summary>
/// <param name="elitePopulation"></param>
/// <param name="roomRange"></param>
/// <returns></returns>
List<Schedule> Mutate(List<List<Schedule>> elitePopulation, List<string> roomRange,List<string> etimes)
{
Random random = new Random();
int getIndex = random.Next(elitePopulation.Count);
List<Schedule> ep = elitePopulation[getIndex];
foreach (Schedule s in ep)
{
int pos = random.Next(3);
if (pos == 0)
{
s.roomid = roomRange[random.Next(roomRange.Count)];
}
else if (pos == 1)
{
s.etimeid = etimes[random.Next(etimes.Count)] ;
}
}
return ep;
}
/// <summary>
/// 交叉
/// </summary>
/// <param name="elitePopulation"></param>
/// <returns></returns>
List<Schedule> Crossover(List<List<Schedule>> elitePopulation)
{
Random random = new Random();
int getIndex1 = random.Next(elitePopulation.Count);
int getIndex2 = random.Next(elitePopulation.Count);
List<Schedule> e1 = elitePopulation[getIndex1];
List<Schedule> e2 = elitePopulation[getIndex2];
int pos = random.Next(2);
for (int i = 0; i < e1.Count; i++)
{
if (pos == 0)
{
e1[i].roomid = e2[i].roomid;
}
else if (pos == 1)
{
e1[i].etimeid = e2[i].etimeid;
}
}
return e1;
}
/**
* 计算课表种群的冲突。
* 返回:精英种群--精英种群中排名第一的染色体若冲突值为0则说明是可以作为最优解返回
* 当被测试课表冲突为0的时候,这个课表就是个符合规定的课表。
* 冲突检测遵循下面几条规则:
* 同一个教室在同一个时间只能有一门课。
* 同一个班级在同一个时间只能有一门课。
* 同一个教师在同一个时间只能有一门课。
* 但是对于目前系统中已经将班级、教师、课程拼成了一条教学任务
* 故只需要满足 同一个教室在同一个时间 只能有一各教学任务
* @param population
* @param elite
* @return
*/
Dictionary<List<Schedule>, int> schedule_cost(List<List<Schedule>> population, int elit)
{
// Hashtable utilMap = new Hashtable();
Dictionary<List<Schedule>, int> utilMap = new Dictionary<List<Schedule>, int>();
Dictionary<List<Schedule>, int> resMap = new Dictionary<List<Schedule>, int>();
List<int> conflicts = new List<int>();
//一个染色体有多长==有多少课程需要安排
int n = population[0].Count;
foreach (List<Schedule> p in population)
{
int conflict = 0;
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
//check course in same time and same room
//检查冲突 需保证 在同一天 同一节课 下的 同一个教室中没有两个课程
if (p[i].roomid.Equals(p[j].roomid) &&
p[i].etimeid == p[j].etimeid)
conflict += 1;
}
}
if (!utilMap.ContainsKey(p))
utilMap.Add(p, conflict);
}
//根据冲突值排序,升序排序,并只取精英个数的量
resMap = utilMap.OrderBy(s => s.Value).Take(elit).ToDictionary(d => d.Key, d => d.Value);
return resMap;
}
public void init_population(List<Schedule> schedules, List<String> roomRange,List<string> etimes)
{
string path = $@"D:\education";
TextWriter tw = new StreamWriter(Path.Combine(path, "test.txt"), false);
this.population = new List<List<Schedule>>();
for (int i = 0; i < this.popsize; i++)
{
List<Schedule> entity = new List<Schedule>();
for (int j = 0; j < schedules.Count; j++)
{
Schedule temp = schedules.ElementAt(j);
int a;
int b;
temp.random_init(roomRange, etimes,i*popsize + j,out a,out b);
tw.WriteLine(a + "," + b + "," + temp.etaskid + "," + temp.roomid + "," + temp.etimeid);
entity.Add(new Schedule()
{
etaskid = temp.etaskid,
roomid = temp.roomid,
etimeid = temp.etimeid
});
}
this.population.Add(entity);
}
tw.Close();
}
}
}