Abstract: Genetic Algorithm (Genetic Algorithm) is an AI model that simulates biological evolution based on the natural selection process. It can search for the optimal solution generation by generation in the simulated biological evolution process. This article uses genetic algorithm to implement a simple program to schedule courses.
Share this article from Huawei cloud community " how intelligent Scheduling with genetic algorithms ", Author: jackwangcumt.
According to the definition of Baidu Encyclopedia, Genetic Algorithm (Genetic Algorithm) is an AI model that simulates biological evolution based on the process of natural selection. It can search for the optimal solution generation by generation in the simulated biological evolution process. The genetic algorithm cannot directly solve the problem. Instead, it needs to use coding rules to abstract the core elements of the problem as genes on the chromosome, and through the process of gene crossover and mutation, iteratively select good genes to reproduce and generate the next generation. Until the optimal solution or satisfactory optimal solution is obtained. At present, genetic algorithms are widely used in fields such as operations research, machine learning, and artificial intelligence.
1 Diagram of genetic algorithm process
The core task of the genetic algorithm is to give the chromosome performance rules of the solution through the coding system. First, a certain number of populations (population) need to be initialized randomly, and the population is composed of a certain number of individuals (individual). Each individual is actually a chromosome, and fitness can be calculated through rules. After the generation of the first generation population, according to the evolutionary principle of survival of the fittest, excellent offspring are evolved from generation to generation.
In the evolution of each generation, individuals are selected according to their fitness, and crossover and mutation are performed with the help of genetic operators of natural genetics to produce new populations. The optimal individual in the last-generation population is decoded and can be used as an approximate optimal solution to the problem. The exit condition is generally to reach the maximum number of iterations, such as 10,000 times. In addition, the fitness must be satisfied, such as reaching 0.99. The basic process diagram is as follows:
2 Course scheduling requirements
The actual curriculum arrangement involves a large number of elements such as teachers, classes, classrooms and courses, so it is very complicated. With the help of genetic algorithm, the optimal solution may not be found, but only the local optimal solution, but the genetic algorithm is used. Auxiliary course planning is still a very good tool. Generally speaking, several restrictions must be met during the course arrangement process, otherwise, the course arrangement given will be invalid. The specific instructions are as follows:
- At the same time, a classroom can only offer one course;
- There is a limit on the number of seats in a classroom, and the total number of students in class cannot exceed the number of seats in the classroom;
- At the same time, students of the same teacher or class can only participate in one course, but cannot participate in multiple courses;
- Classrooms are divided into multimedia classrooms and ordinary classrooms. Some courses require multimedia classrooms. Therefore, the classroom configuration must meet the course requirements;
When all the above 4 restrictions are met, the given course arrangement is effective, but please note that it is not necessarily the best. It does not consider the optimization conditions. For example, if the same teacher follows multiple courses in a day Courses are obviously a bit overloaded, or the same course is offered several times in a row on the same day, which is a bit overwhelming for teachers and students.
3 Data structure of elements in curriculum arrangement
As mentioned earlier, the course layout process involves elements such as teachers, classes (student groups), classrooms, and courses. The data structure description of each element is given below:
Course: course object is called Course, which contains two fields: Course ID and Course Name.
classroom: classroom object is named Room, which contains the 4 fields of classroom ID, classroom name, number of seats, and whether it is a multimedia classroom.
Teacher: teacher object is called Professor, which contains the three fields of teacher ID, teacher name, and all courses (CourseClass) that need to be taught in the classroom.
course class: course class object is called CourseClass, which includes the course instructor, the course taught, the class of the class, the number of seats required (the sum of the number of students in multiple classes), whether a multimedia classroom is needed, and the length of the class. 6 fields. This class also provides the method GroupsOverlap (CourseClass c) to determine whether there is a class overlap between itself and the parameter c. Similarly, the method ProfessorOverlaps (CourseClass c) can determine whether there is a teacher overlap between itself and the parameter c.
class: class object is called StudentGroup, which contains the 4 fields of class ID, class name, class size, and all courses (CourseClass) that the class needs to attend.
(Chromosome Representation): In order to apply genetic algorithms, it is important to consider how to use gene sequences to represent the solution of the problem. Generally speaking, a gene sequence is a long sequence of ordered sequences. Here you can use multi-dimensional courses Arrangement elements are compressed into a one-dimensional array through dimensionality reduction, and the length of the array (called Slots later) is:
school days of the week long hours each day in class classroom number
For example, assuming that the number of school days in a week is 5, it means that from Monday to Friday, the daily school time is 12 hours. For example, classes start at 8 in the morning and end at 20 in the evening. And for the sake of simplicity, suppose it is 2, so the total group length is 5 12 2 = 120, each element in this one-dimensional array can be placed in the course class CourseClass, and different course class combinations represent Different course scheduling schemes. The schedule plan can be represented by the following schematic diagram:
Note: The above slot represents an hour unit, and can also indicate the location index of the course, which can point to a specific course class CourseClass instance, which means that the slot has a corresponding course arrangement.
4 Fitness
Based on the above chromosome performance, we need to calculate the fitness of an individual. The calculation method is as follows:
- Traverse the information of each course in the one-dimensional array representing the performance of chromosomes. If there is no overlap of multiple courses in the classroom at the same time, increase the fitness score.
- Traverse the class information of each course in the one-dimensional array representing the performance of the chromosome. If the multimedia requirements of the course match the classroom, then increase the fitness score.
- Traverse the class information of each course in the one-dimensional array representing the performance of chromosomes. If the total number of class participants in the course is less than or equal to the number of seats in the classroom, increase the fitness score.
- Traverse the information of each class in the one-dimensional array representing the performance of chromosomes. If the teacher will not teach in multiple classrooms at the same time, increase the fitness score.
- Traverse the information of each class in the one-dimensional array representing the performance of chromosomes. If the class does not learn in multiple classes at the same time, increase the fitness score.
Whether the above five indicators for increasing fitness scores are met can be represented by an additional data structure, that is, an array of inspection rules, with an index of 0 to 4, and a total of 5 values. Courses overlap, indicated by red R, non-overlapping, indicated by green R. Whether there are enough seats in the classroom, the shortage is indicated by a red S, otherwise it is indicated by a green S. Whether the classroom matches the multimedia requirements of the course, the mismatch is indicated by a red L, otherwise it is indicated by a green L. Whether there is overlap between teachers in the course class, the overlap is indicated by red P, otherwise it is indicated by green P. Whether there is an overlap in the classes in the course class, the overlap is indicated by a red G, otherwise it is indicated by a green G. The fitness value of an individual is a float type value, which is equal to:
score/ ( number_of_classes * 5)
The range is 0 to 1. For the course scheduling scenario, the higher the fitness score, the better the solution given. Therefore, individuals with fitness scores should be preferred during the selection of individuals in the evolutionary process.
5 Genetic algorithm simulation implementation
For the course scheduling scenario, the higher the fitness score, the better the solution given. Therefore, individuals with fitness scores should be preferred during the selection of individuals in the evolutionary process. The core code snippets of the evolution process (selection, crossover, and mutation) simulated by the algorithm are given below. Examples are as follows:
List<Schedule> offspring = new List<Schedule>();
offspring.resize(_replaceByGeneration);
for (int j = 0; j < _replaceByGeneration; j++)
{
//随机选择
Schedule p1 = _chromosomes[RandomNumbers.NextNumber() % _chromosomes.Count];
Schedule p2 = _chromosomes[RandomNumbers.NextNumber() % _chromosomes.Count];
//交叉
offspring[j] = p1.Crossover(p2);
//变异
offspring[j].Mutation();
}
As can be seen from the above code, offspring offspring determines the size of the individual that needs to be evolved according to the parameter _replaceByGeneration. For each evolved offspring, first select two parents p1 and p2 through a random method, and first obtain the crossover through p1.Crossover(p2) The offspring after the operation are then mutated in offspring[j].Mutation(). The core code of cross operation is as follows:
public Schedule Crossover(Schedule parent2)
{
// 根据概率确定是否需要交叉操作
if (RandomNumbers.NextNumber() % 100 > _crossoverProbability)
//直接返回
return new Schedule(this, false);
//拷贝生成新的chromosome object
Schedule n = new Schedule(this, true);
int size = (int)_classes.Count;
//交叉点数组初始化
List<bool> cp = new List<bool>();
for (int k = 0; k < size; k++)
{
cp.Add(false);
}
// 随机确定交叉位置
for (int i = _numberOfCrossoverPoints; i > 0; i--)
{
while (true)
{
int p = RandomNumbers.NextNumber() % size;
if (!cp[p])
{
cp[p] = true;
break;
}
}
}
Dictionary<CourseClass, int>.Enumerator it1 = _classes.GetEnumerator();
Dictionary<CourseClass, int>.Enumerator it2 = parent2._classes.GetEnumerator();
//交替用父个体组合交叉生产新的个体
bool first = RandomNumbers.NextNumber() % 2 == 0;
for (int i = 0; i < size; i++)
{
it1.MoveNext();
it2.MoveNext();
if (first)
{
//添加新的课程
n._classes.Add(it1.Current.Key, it1.Current.Value);
for (int j = it1.Current.Key.GetDuration() - 1; j >= 0; j--)
n._slots[it1.Current.Value + j].AddLast(it1.Current.Key);
}
else
{
//添加新的课程
n._classes.Add(it2.Current.Key, it2.Current.Value);
for (int j = it2.Current.Key.GetDuration() - 1; j >= 0; j--)
n._slots[it2.Current.Value + j].AddLast(it2.Current.Key);
}
//在交叉位置交替进行课程更新
if (cp[i])
first = !first;
}
//计算适应度
n.CalculateFitness();
//返回更好的后代
return n;
}
It can be seen from the above code that _crossoverProbability represents a crossover probability. It is not necessary to perform a specific crossover operation every time the crossover operation is called. When the randomly generated number is greater than the set probability, the crossover specific operation is performed. The position of the crossover point is also randomly generated, and the number of crossover points is given by the parameter _numberOfCrossoverPoints. The essence of the crossover operation is to randomly combine and exchange the set of courses pointed to by the two parent classes, that is to say, what is exchanged is the course information and the index position of the course. The mutation process is relatively simple, that is, for individuals who need to perform mutation operations, when the mutation probability is met, a course is randomly selected and moved to another randomly selected slot (Slots). The core code of the mutation process is as follows:
public void Mutation()
{
//按照概率决定是否需要突变
if (RandomNumbers.NextNumber() % 100 > _mutationProbability)
return;
int numberOfClasses = (int)_classes.Count;
int size = (int)_slots.Count;
// 随机决定突变
for (int i = _mutationSize; i > 0; i--)
{
int count = _classes.Count;
int mpos = RandomNumbers.NextNumber() % numberOfClasses;
int pos1 = 0;
Dictionary<CourseClass, int>.Enumerator it = _classes.GetEnumerator();
if (mpos == 0)
{
it.MoveNext();
}
for (; mpos > 0; it.MoveNext(), mpos--)
;
pos1 = it.Current.Value;
CourseClass cc1 = it.Current.Key;
// 随机确定课程的索引位置
int nr = Configuration.GetInstance().GetNumberOfRooms();
int dur = cc1.GetDuration();
int day = RandomNumbers.NextNumber() % DefineConstantsSchedule.DAYS_NUM;
int room = RandomNumbers.NextNumber() % nr;
int time = RandomNumbers.NextNumber() % (DefineConstantsSchedule.DAY_HOURS + 1 - dur);
int pos2 = day * nr * DefineConstantsSchedule.DAY_HOURS + room * DefineConstantsSchedule.DAY_HOURS + time;
for (int k = dur - 1; k >= 0; k--)
{
//移除不需要的课程
LinkedList<CourseClass> cl = _slots[pos1 + k];
for (LinkedList<CourseClass>.Enumerator it3 = cl.GetEnumerator(); it3.MoveNext(); )
{
if (it3.Current == cc1)
{
cl.Remove(it3.Current);
break;
}
}
//移动课程到新的插槽位置
_slots[pos2 + k].AddLast(cc1);
}
// 更新课程位置
_classes[cc1] = pos2;
}
CalculateFitness();
}
Course scheduling needs to provide some basic data, such as teacher resource situation, class situation, course situation, classroom situation, etc. The resource data template is given below:
#prof
id = 1
name = 李老师
#end
#prof
id = 2
name = 张老师
#end
#prof
id = 3
name = 汪老师
#end
...
#course
id = 1
name = Web编程
#end
#course
id = 4
name = 电子商务
#end
...
#course
id = 8
name = 数据库原理
#end
#room
name = C101
lab = false
size = 80
#end
#room
name = C102
lab = true
size = 90
#end
#group
id = 1
name = 电商1班
size = 22
#end
...
#group
id = 4
name = 会计2班
size = 27
#end
#class
professor = 1
course = 1
duration = 2
group = 1
group = 2
#end
...
#class
professor = 12
course = 8
duration = 2
group = 3
group = 4
#end
The chromosome phenotypes of individuals in the initial population are given below. The specific code is as follows. The population size can be given by parameters. Different individuals can be generated by calling MakeNewFromPrototype() in a loop and added to the primary population. The core code of the MakeNewFromPrototype method is as follows:
public Schedule MakeNewFromPrototype()
{
//插槽个数
int size = (int)_slots.Count;
//生成新的个体 chromosome
Schedule newChromosome = new Schedule(this, true);
//随机获取CourseClass信息
LinkedList<CourseClass> c = Configuration.GetInstance().GetCourseClasses();
for (LinkedList<CourseClass>.Enumerator it = c.GetEnumerator(); it.MoveNext(); )
{
//随机获取课程位置
int nr = Configuration.GetInstance().GetNumberOfRooms();
int dur = (it.Current).GetDuration();
int day = RandomNumbers.NextNumber() % DefineConstantsSchedule.DAYS_NUM;
int room = RandomNumbers.NextNumber() % nr;
int time = RandomNumbers.NextNumber() % (DefineConstantsSchedule.DAY_HOURS + 1 - dur);
int pos = day * nr * DefineConstantsSchedule.DAY_HOURS + room * DefineConstantsSchedule.DAY_HOURS + time;
//将CourseClass信息放于随机的插槽位上
for (int i = dur - 1; i >= 0; i--)
newChromosome._slots[pos + i].AddLast(it.Current);
//添加课程class信息
newChromosome._classes.Add(it.Current, pos);
}
//计算适应度
newChromosome.CalculateFitness();
return newChromosome;
}
On the UI, C# GDI+ is used for drawing, an example is as follows:
protected void paint(PaintEventArgs e)
{
string baseFile = AppDomain.CurrentDomain.BaseDirectory;
string filename = baseFile + "GaSchedule.cfg";
Configuration.GetInstance().ParseFile(ref filename);
Graphics gac = e.Graphics;
Rectangle clientRect = e.ClipRectangle;
try
{
this.Invoke((MethodInvoker)delegate
{
sx = -GetScrollPos(this.Handle, SB_HORZ);
sy = -GetScrollPos(this.Handle, SB_VERT);
});
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
sx = 0;
sy = 0;
}
Color newColor = System.Drawing.Color.FromArgb(49, 147, 120);
Color bzColor = System.Drawing.Color.FromArgb(49, 147, 120);
Color errorColor = System.Drawing.Color.FromArgb(206, 0, 0);
Brush bgBrush = System.Drawing.Brushes.White;
gac.FillRectangle(bgBrush, clientRect);
Font tableHeadersFont = new Font("Microsoft YaHei", 12);
Font tableTextFont = new Font("Microsoft YaHei", 10);
Font roomDescFont = new Font("Microsoft YaHei", 10);
Font criteriaFont = new Font("Microsoft YaHei", 12);
SolidBrush classBrush = new SolidBrush(Color.DarkOrchid);
classBrush.Color = Color.FromArgb(255, 255, 245);
SolidBrush overlapBrush = new SolidBrush(Color.DarkOrchid);
overlapBrush.Color = Color.FromArgb(255, 0, 0);
HatchBrush myHatchBrush = new HatchBrush(HatchStyle.BackwardDiagonal, Color.Red,Color.Transparent);
int nr = Configuration.GetInstance().GetNumberOfRooms();
for (int k = 0; k < nr; k++)
{
for (int i = 0; i < ROOM_COLUMN_NUMBER; i++)
{
for (int j = 0; j < ROOM_ROW_NUMBER; j++)
{
int l = k % 2;
int m = k / 2;
if (i == 0 && j == 0)
{
Rectangle rect2 = new Rectangle(
sx+ROOM_MARGIN_WIDTH + ROOM_TABLE_WIDTH * l,
sy+ROOM_MARGIN_HEIGHT,
ROOM_CELL_WIDTH,
ROOM_CELL_HEIGHT);
gac.DrawRectangle(Pens.Black, rect2);
Rectangle rect3 = new Rectangle(rect2.X, rect2.Y + 8, rect2.Width, rect2.Height - 16);
string str;
str = string.Format("教室:{0}", Configuration.GetInstance().GetRoomById(k).GetName());
TextRenderer.DrawText(gac, str, roomDescFont, rect3, Color.FromArgb(0, 0, 0),
TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter);
}
if (i == 0 && j > 0)
{
string str = string.Format("{0} - {1}", 8 + j - 1, 8 + j);
Rectangle rect3 = new Rectangle(
sx + ROOM_MARGIN_WIDTH + ROOM_TABLE_WIDTH * l ,
sy + ROOM_MARGIN_HEIGHT + ROOM_CELL_HEIGHT * (j),
ROOM_CELL_WIDTH,
ROOM_CELL_HEIGHT);
TextRenderer.DrawText(gac, str, tableHeadersFont, rect3, Color.FromArgb(0, 0, 0),
TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter);
gac.DrawRectangle(Pens.Black, rect3);
}
if (j == 0 && i > 0)
{
string[] days = { "周一", "周二", "周三", "周四", "周五" };
Rectangle rect3 = new Rectangle(
sx + ROOM_MARGIN_WIDTH + ROOM_TABLE_WIDTH * l + ROOM_CELL_WIDTH * (i),
sy + ROOM_MARGIN_HEIGHT,
ROOM_CELL_WIDTH,
ROOM_CELL_HEIGHT);
TextRenderer.DrawText(gac, days[i - 1], tableHeadersFont, rect3, Color.FromArgb(0, 0, 0),
TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter);
gac.DrawRectangle(Pens.Black, rect3);
}
}
}
}
if (_schedule != null)
{
Dictionary<CourseClass, int> classes = _schedule.GetClasses();
int ci = 0;
for (Dictionary<CourseClass, int>.Enumerator it = classes.GetEnumerator(); it.MoveNext(); ci += 5)
{
CourseClass c = it.Current.Key;
int p = it.Current.Value;
int t = p % (nr * DAY_HOURS);
int d = p / (nr * DAY_HOURS) + 1;
int r = t / DAY_HOURS;
t = t % DAY_HOURS + 1;
int l = r % 2;
int m = r / 2;
Rectangle rect = new Rectangle(
sx + ROOM_TABLE_WIDTH * l + ROOM_MARGIN_WIDTH + d * ROOM_CELL_WIDTH ,
sy + ROOM_TABLE_HEIGHT * m + ROOM_MARGIN_HEIGHT + t * ROOM_CELL_HEIGHT ,
ROOM_CELL_WIDTH ,
c.GetDuration() * ROOM_CELL_HEIGHT);
string str = string.Format("{0}\n({1})\n", c.GetCourse().GetName(), c.GetProfessor().GetName());
for (LinkedList<StudentsGroup>.Enumerator it2 = c.GetGroups().GetEnumerator(); it2.MoveNext(); )
{
str += (it2.Current).GetName();
str += "/";
}
str=str.TrimEnd('/');
if (c.IsLabRequired())
str += "[多媒体]";
gac.FillRectangle(classBrush, rect);
gac.DrawRectangle(Pens.Black, rect);
TextRenderer.DrawText(gac, str, tableTextFont, rect, Color.FromArgb(0, 0, 0), TextFormatFlags.WordBreak);
if (!_schedule.GetCriteria()[ci + 0])
{
bzColor = errorColor;
TextRenderer.DrawText(gac, "R", tableTextFont, new Point(rect.Left, rect.Bottom - 20), bzColor);
gac.FillRectangle(myHatchBrush, rect);
}
else
{
TextRenderer.DrawText(gac, "R", tableTextFont, new Point(rect.Left, rect.Bottom - 20), bzColor);
}
bzColor = newColor;
if (!_schedule.GetCriteria()[ci + 1])
{
bzColor = errorColor;
TextRenderer.DrawText(gac, "S", tableTextFont, new Point(rect.Left + 10, rect.Bottom - 20), bzColor);
}
else
{
TextRenderer.DrawText(gac, "S", tableTextFont, new Point(rect.Left + 10, rect.Bottom - 20), bzColor);
}
bzColor = newColor;
if (!_schedule.GetCriteria()[ci + 2])
{
bzColor = errorColor;
TextRenderer.DrawText(gac, "L", tableTextFont, new Point(rect.Left + 20, rect.Bottom -20), bzColor);
}
else
{
TextRenderer.DrawText(gac, "L", tableTextFont, new Point(rect.Left + 20, rect.Bottom - 20), bzColor);
}
bzColor = newColor;
if (!_schedule.GetCriteria()[ci + 3])
{
bzColor = errorColor;
TextRenderer.DrawText(gac, "P", tableTextFont, new Point(rect.Left + 30, rect.Bottom -20), bzColor);
}
else
{
TextRenderer.DrawText(gac, "P", tableTextFont, new Point(rect.Left + 30, rect.Bottom -20), bzColor);
}
bzColor = newColor;
if (!_schedule.GetCriteria()[ci + 4])
{
bzColor = errorColor;
TextRenderer.DrawText(gac, "G", tableTextFont, new Point(rect.Left + 40, rect.Bottom - 20), bzColor);
}
else
{
TextRenderer.DrawText(gac, "G", tableTextFont, new Point(rect.Left + 40, rect.Bottom - 20), bzColor);
}
}
}
}
After execution, after multiple iterations of the genetic algorithm, the UI interface displayed is as follows:
In the intermediate link, the iterative process in which a feasible solution cannot be obtained, the following interface may be displayed:
Since Monday’s [8-10] and [9-11] have two courses occupying the same classroom at the same time, a red diagonal pattern will be displayed on the UI, and R (Room) will be red. So far, we have basically implemented a genetic algorithm implemented in C# language to perform simple course scheduling operations. Finally, this blog refers to the https://www.codeproject.com/articles/23111/making-a-class-schedule-using-a-genetic-algorithm .
Click to follow and learn about Huawei Cloud's fresh technology for the first time~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。