根据编码生成具体调度方案
这里完成获得随机一种种群即排产方案得到的排序方式
%算法主程序
function GA4JSP204()
dt=initData();%调用函数dt=initData(),从而获得设备工时数据,这里末尾用了分号,则程序运行时不显示
[rows,cols]=size(dt);%获得工时设备数据的行与列
jobQty=rows;machQty=cols/2;%确定工件数,机器数
pop=6;
chromes=createChromes(jobQty,machQty,pop);%创建chromes群体/解集,这个函数得到的每一行都是一个可行解,一共有pop行,构成一个种群
chrome1=chromes(1,:)%从chromes群体中找出一个群体,即为某一个编码或者某一个解,且这里是基于操作的编码,该群体或该行数字的意义为某一个排产的方案,它的所有数值
%都代表着工件号即jobQty,,而相同的工件号所排的位置即为其工序号,我们需要通过工件号及其所对应的序号来回到原始工时数据里,找到对应的行从而确定对应的机器编码
%即比如[2 1 1 1 2 2 3 3 3]为先排工件2的工序1,再排工件1的工序1,再排工件1的工序2,以此类推.......那么后面的nowJobId、nowProcId、nowMachId的作用我们就知道了,
%我们知道了排产的顺序,就需要知道对应工件的对应工序号及其对应的机器号以及开工时间完工时间等信息
sch=createSchedule(dt,chrome1)%其中一种调度方案的生成
end
%将编码转换成具体调度方案
function schedule=createSchedule(data,chrome)%生成调度方案
jobQty=size(data,1);%确定data数据有几行,进而确定工件数
machQty=size(data,2)/2;%确定data数据有几列,进而确定机器数
schedule=zeros(jobQty*machQty,5)%定义一个 jobQty*machQty 行,5列的二维数组【schedule】,用于存储编码对应的调度方案,生成这么多行这么多列的0矩阵
%现在我们胚子有了,那么怎么填数据呢
%定义中间数组
jobCanStartTime=zeros(1,jobQty);%定义长度为 jobQty 一维数组,用于存储各个作业当前工序最早可开工时间,或者当前作业前一工序的完工时间,初始时可开工时间都是0
jobProcessId=ones(1,jobQty);%定义长度为 jobQty 一维数组,用于存储各个作业当前拟排产的工序号,用ones是因为开工工序肯定从1开始
%循环对 chrome 中每个字码【对应特定作业】,完成工序号、设备号和开工时间、完工时间的生成
for i=1:jobQty*machQty%jobQty*machQty为chrome的总长度
%获取编码中当前作业号,i位置的数字
%比如nowJobId=2,nowProcId=1,如何获得对应的机器编码,查看ft06数据可得机器号为1
%工序值 设备列
%1 1
%2 3
%3 5
%4 7
%5 9
%6 11
%规律: 设备列=2*工序值‐1
nowJobId=chrome(i);%如i=1,那么nowJobId=2(这个2是每次随机生成的)
nowProcId=jobProcessId(nowJobId);%前面已经设定了jobProcessId=ones(1,jobQty),那么nowJobId=2,jobProcessId(2)=1,nowProcId=1
nowMachId=data(nowJobId,2*nowProcId-1);%设备列=2*工序值‐1 设备列=2*1-1=1,即设备列为第一列,则nowMachId=data(2,1),nowMachId=1
% schedule 数组中安排 chrome 对应的具体的开工和完工时间
% 当获得了当前 jobId、machId、procId,从 schedule 中提取第 2 列数值为 machId 值的全部行,
%[1]如果没找到对应的行,表示这个 machId 值的设备还没有工作‐作业;
%直接向 schedule 中添加一条数据,第四列 startTime 就是 jobId 的可开工时间,第五列 endTime 为第四列数据+data 的工时数据;
%[2]如果找到了至少 1 行数据,则表示这个 machId 值对应设备已经安排了作业;
%就需要判断具体将开工时间和完工时间安排在什么时段,首先将新的数组按照开工时间升序排序;然后考虑三种情况,进行作业安排:
%(1) 是否能排到该设备的第一个作业之前;
%(2) 是否能排到其他作业之前;
%(3) 是否需要排到最后一个作业之后;
nowProcTime=data(nowJobId,2*nowProcId);%这个就是每个工件工序的工时,它的数据位置就在machid的下一位
%a=round(10*rand(6,4))
%a=[1 1 9 1
% 4 3 2 4
% 0 2 5 2
% 2 1 4 0
% 0 6 5 10
% 2 9 3 4]
%那么我们要提取第二列中数值为1的行,编程为:b=a(a(:,2)==1,:)
%结果为:b=[1 1 9 1
% 2 1 4 0]
%即如何根据某一列的数据提取出特定的值
machSch=schedule(schedule(:,2)==nowMachId,:);%当获得了当前 jobId、machId、procId,从 schedule 中提取第 2 列数值为 machId 值的全部行
%那么对应的行数肯定是有值的,究竟是几行呢需要判断一下
jobCanST=jobCanStartTime(nowJobId);%用一个短字量来表示
if size(machSch,1)==0 %设备还没有安排作业
startTime=jobCanStartTime(nowJobId);%没安排作业的话,那么开始时间就是jobCanStartTime
endTime=startTime+nowProcTime;%这两条就确定了开工时间和完工时间
else %设备已经安排了作业
machSch=sortrows(machSch,4);%如果机器安排了作业就需要考虑顺序,比如把机器3所加工的工序都抓出来就形成一个二维数组,需要将开工时间排个序,按照第四列升序排序,
%确定两件事,一是你开工时间是不是比上一道工序完工时间大,二是我这项加工时间是不是比我之前工序完工时间以及之后工序开工时间间隔小。
rows=size(machSch,1);
%比如:可开工时间 工时
% ? 22 33
% 1 3 5
% 2 15 26
% 3 47 54
%就看3是不是比22大,如果3比22大再看3和上一项即0的数值是不是比33大还是小
%处理第一行已排作业,检查是否能将当前作业排到该作业之前
done=0;
if jobCanST<machSch(1,4)%比如把22换成1,33换成5,1<3,接下来就看3-1与5的大小
if machSch(1,4)-jobCanST>=nowProcTime%即如果3-1>=5,nowProcTime表示作业时间
startTime=jobCanST;
endTime=startTime+nowProcTime;
done=1;%用于将异地行作业排完
end
end
if done==0%接下来进行其他行的循环判断,每次判断准则都一样,从这里开始表示没排到第一行前面,那么我们从第二行开始排
for j=2:rows%这里表示我们从第二行开始排,比如把22换成12,33换成5,此时第一个作业没法排,再看看第二个作业,12<15需要开始判断
if jobCanST<machSch(j,4)%即12<15(12为jobCanST,15为machSch(j,4))
if machSch(j,4)-max(jobCanST,machSch(j-1,5))>=nowProcTime%同时要判断待定的这个可开工时间和这个机器前一个作业的完工时间的最大值(重要)和下一道工序的开工时间之差
%比如22换成8,33换成6,5换成10。那么15大于8好像可以往第二个作业前面安排,15-8=7个间隔也比工时6大好像可以排,但是你不能排到8这里,因为上一项的完工时间是10,所以应该选8和10之间的最大值与15的差值要比工时6大才行
%上一行代码就是判断8和10之间的最大值,此行代码表示如果15-10>=6,那么就可以往这两个工序之间排
startTime=max(jobCanST,machSch(j-1,5));
endTime=startTime+nowProcTime;
done=1;
break;%for循环强制跳出循环
%那么以上几段代码表示在中间能够排进去
end
end
end
end
if done==0 %表示该作业不能排到该设备已有作业之前,只能排到已有作业之后即排到最后一个作业的后面
startTime=max(jobCanST,machSch(rows,5));
endTime=startTime+nowProcTime;
end
end
%把schedule的五列数值都定好,这是我们自己定的
schedule(i,1)=nowJobId;
schedule(i,2)=nowMachId;
schedule(i,3)=nowProcId;
schedule(i,4)=startTime;
schedule(i,5)=endTime;
jobCanStartTime(nowJobId)=endTime;
jobProcessId(nowJobId)=jobProcessId(nowJobId)+1;%一次循环所有对应的数据计算得到结束后要开始变成后面一个工序了
end
end
%初始化工时设备数据
function data=initData()%就是把initData的值赋值给左边的值
data=[ 2 1 0 3 1 6 3 7 5 3 4 6;
1 8 2 5 4 10 5 10 0 10 3 4;
2 5 3 4 5 8 0 9 1 1 4 7;
1 5 0 5 2 5 3 3 4 8 5 9;
2 9 1 3 4 5 5 4 0 3 3 1;
1 3 3 3 5 9 0 10 4 4 2 1];
cols=1:2:11;%确定奇数列
data(:,cols)=data(:,cols)+1;%将奇数列的数据加1转换得到机器序号
end
%根据种群数量和作业数量和设备数量生成初始种群
function chromes=createChromes(jobQty,machQty,pop)
chromes=zeros(pop,jobQty*machQty);
for i=1:pop
chromes(i,:)=createChrome(jobQty,machQty);
end
end
%生成编码:传两个参数【1】jobQty,【2】machQty
function chrome=createChrome(jobQty,machQty)
a=1:jobQty;
chrome=a;
for i=2:machQty
chrome=[chrome a];
end
%将编码乱序排列-生成作业码的乱序
b=randperm(jobQty*machQty);
chrome=chrome(b);
end
运行一次得到的随机结果
》sch =
2 2 1 0 8
4 2 1 8 13
4 1 2 13 18
2 3 2 8 13
2 5 3 13 23
5 3 1 13 22
5 2 2 22 25
4 3 3 22 27
3 3 1 0 5
3 4 2 5 9
5 5 3 25 30
1 3 1 5 6
3 6 3 9 17
1 1 2 6 9
6 2 1 13 16
4 4 4 27 30
5 6 4 30 34
3 1 4 18 27
3 2 5 27 28
1 2 3 16 22
5 1 5 34 37
4 5 5 30 38
1 4 4 30 37
5 4 6 37 38
6 4 2 16 19
3 5 6 38 45
4 6 6 38 47
1 6 5 47 50
6 6 3 19 28
6 1 4 37 47
6 5 5 47 51
2 6 4 50 60
2 1 5 60 70
6 3 6 51 52
1 5 6 51 57
2 4 6 70 74
最后得到的结果我们来看看对不对,下面进行验证:
先按作业升序排序同时按照工序升序排序
再看设备的升序排序,开工完工时间有没有不合理的
这就是我们得到的一种可行的排产方案。