在前两篇博客里面,我们重点讲解了利用随机搜索的方法解决车间调度问题,流程图如下:
在本篇博客中,我们将介绍如何利用遗传算法来解决车间调度问题。具体的算法流程图如下:
与上面流程图相对应的遗传算法的整体代码如下:
在上面的函数中Crossover函数就是那个对两个可行解进行交叉的函数。
2.交叉
交叉是遗传算法中的一个重要操作,它的目的是从已有的两个解Parent1和Parent2的编码中各自取出一部分来组合成两个新的解Offspring1和Offspring2,在车间调度中一种常见的交叉方法叫 Generalized Order Crossover方法(GOX),假设有三个工件A,B,C, 每个工件下面包含三道工序,根据这一信息我们可以利用上一节介绍的编码随机生成两个解如下:
我们可以用一个有序偶list(列表)来存储一个解,
这个有序偶list中的每个元素是一个有序偶,有序偶的第一个元素是工件号,第二个元素是工序号。
Parent1用列表可以表示为[(B,1),(A,1),(B,2),(B,3),(C,1),(A,2),(C,2),(C,3),(B,1),(A,3))]
有序偶的图示为
GOX交叉就是从Parent2中随机抽取其中的一部分把它移植到Parent1中生成新的染色体,比如,我们选取 “(A,2)(C,1)(A,3)(B,3)” 这个片段
我们首先随机选取Parent1中的一个位置,把这个“(A,2)(C,1)(A,3)(B,3)”片段插到该位置之后
同时,我们把原Parent1中与从Parent2中移植过来的部分中重复的有序偶删掉,即可以得到新的交叉后的染色体:
除了交叉操作外,遗传算法中的另一个重要操作就是变异了,英文名字叫Mutation.变异操作通常发生在交叉操作之后,它的操作对象是交叉之后得到的新解。在本文中我们通过随机交换染色体的两个位置上的值来得到变异后的新解,变异操作的代码如下:
变异操作的图示如下:
变异前的染色体: ABCABCABC
随机选取两个位置:ABCABCABC
变异后的染色体:AACABCBBC
在完成了遗传算法之后,我们还需要一个函数来显示最终的结果,结果显示函数如下所示:
S中存放的是每道工序开始加工的时间,它的形式为:[[a,b,c],[d,e,f],[g,h,i]],每个子list代表一个工件的信息,子list中的字母代表这个工件下面各个工序开始加工的时间。
假设我们知道每道工序开始加工的时间,同时又知道每道工序所需要的机器号,我们就可以得到每台机器上工序的加工顺序,进而可以用软件画出调度的甘特图。
最后我们来介绍一下配置文件的读取问题,在之前的博客中我们已经介绍了一个车间调度问题的基本信息可以用一个已知条件表格来表示:
表格的第一行代表每道工序所使用的机器号,表格的第二行代表每道工序的加工时间。
在编程时我们要把这个已知条件表格用一个配置文件来存储,这个配置文件可以是一个记事本文件,后缀名是.txt。
下面我们给出上面那个表格对应的配置文件:
Init.txt
可以看到这个配置文件有三行,第一行中的第一个“2”表示有2个工件,第二个“2”表示有2台机器。第二行代表工件1的信息,有五个数字,它们的含义如下:
“2”表示工件1有2道工序,
“1”表示工件1的第一道工序需要在机器1上加工,
“3”表示工件1的第一道工序在机器"1"上加工的时间为3个时间单位
“2”表示工件1的第二道工序需要在机器2上加工
“2”表示工件1的第二道工序在机器2上的加工时间为2个时间单位
我们首先要写一个函数把配置文件的信息读到一个Instance类中,最终输出的类实例 I 应该有着如下的格式:
jobs=[[(1,3),(2,2)],[(2,5),(1,1)]]
(list中又嵌套两个子list,每个子list对应一个工件的信息,子list中的一个有序偶对应表格中的一列)
n=2(工件的个数)
m=3(机器的个数)
我们还需要写一个函数loadInstance函数来把配置文件txt中的信息读到Instance对象的对应的成员变量中即
I = LoadInstance("配置文件.txt");
loadInstance的python代码如下:
民大赵老师