🌞欢迎来到智能优化算法的世界
🌈博客主页:卿云阁💌欢迎关注🎉点赞👍收藏⭐️留言📝
🌟本文由卿云阁原创!
🌠本阶段属于筑基阶段之一,希望各位仙友顺利完成突破
📆首发时间:🌹2021年1月7日🌹
✉️希望可以和大家一起完成进阶之路!
🙏作者水平很有限,如果发现错误,请留言轰炸哦!万分感谢!
目录
0️⃣✨✨✨基本介绍✨✨✨
n 个不同的工件在 m 台机器上加工,其要满足工序约束 ,即每个工件包含了一道或者是多道工序 ,并且同一个工件所经过的工序有先后的顺序 ,每道工序可以在一台或者多台(这里考虑传统的车间作业,即每道工序在一台机器上加工)机器上进行加工,每个工序的加工时间由加工的机器给定。所需满足的约束条件如下:
1、同一时刻、同一机器只能加工一道工序 ;
2、一道工序同一时刻只能在一台机器上加工,且不能中途中断 ;
3、同一工件的工序先后顺序有约束 ,而不同工件加工工序之间没有先后顺序的约束;
4、不同工件之间优先级是相同的;
5、不考虑机器故障等随机性因素。
调度的目标就是确定每个机器上工序的加工顺序和每个工序的开工时间,使得最大完工时间最小或者其他指标达到最优。
1️⃣✨✨✨代码部分✨✨✨
(1)main.m function [ bestgen,bestfitness,bestchrom]=main clc clear all close all %% 定义工件信息 %操作对应时间 可从txt文件中拷贝替换 W=[29 78 9 36 49 11 62 56 44 21; 43 90 75 11 69 28 46 46 72 30; 91 85 39 74 90 10 12 89 45 33; 81 95 71 99 9 52 85 98 22 43; 14 6 22 61 26 69 21 49 72 53; 84 2 52 95 48 72 47 65 6 25; 46 37 61 13 32 21 32 89 30 55; 31 86 46 74 32 88 19 48 36 79; 76 69 76 51 85 11 40 89 26 74; 85 13 61 7 64 76 47 52 90 45]; %机器工艺约束 可从txt文件中拷贝替换 Q=[1 2 3 4 5 6 7 8 9 10; 1 3 5 10 4 2 7 6 8 9; 2 1 4 3 9 6 8 7 10 5; 2 3 1 5 7 9 8 4 10 6; 3 1 2 6 4 5 9 8 10 7; 3 2 6 4 9 10 1 7 5 8; 2 1 4 3 7 6 10 9 8 5; 3 1 2 6 5 7 9 10 8 4; 1 2 4 6 3 10 7 8 5 9; 2 1 3 7 9 10 6 4 5 8]; n=size(W,1); %工件数 m=size(Q,1); %机器总数 k=m*n; %工序总数 %% 定义遗传算法参数 sizepop=20; %种群大小 maxgen=300; %最大遗传代数 lenchrom=k; %个体长度 GGAP=0.95; %代沟 px=0.8; %交叉概率 pm=0.01; %变异概率 trace=zeros(2,maxgen); %寻优结果的初始值 gen=0; %代计数器 %% 个体初始化 individuals=struct('fitness',zeros(1,sizepop),'chrom',[]); bestgen=1; %适应度最好的染色体出现的代数 trace1=zeros(1,maxgen);%寻优结果的初始值,画图用 %% 初始化种群 ChromTemp=zeros(1,k); p=k/n; %每个工件的操作数 for i=1:n for j=1:p t=(i-1)*n+j; ChromTemp(1,t)=i; end end %% 原始工件序列 for i=1:sizepop %随机产生一个种群 individuals.chrom(i,:)=ChromTemp(randperm(length(ChromTemp))); %随机产生染色体 x=individuals.chrom(i,:); individuals.fitness(i)=makespan(x,n,k,m,W,Q); end fprintf('初始化种群') individuals.chrom individuals.fitness %% 找最好的染色体 [bestfitness, bestindex]=min(individuals.fitness); bestchrom=individuals.chrom(bestindex,:); %最好的染色体 avgfitness=sum(individuals.fitness)/sizepop; %染色体的平均适应度 trace=[]; %% 进化开始 for i=1:maxgen temp1=individuals; %选择 temp2=Select(individuals,sizepop,GGAP); %交叉 temp2.chrom=Cross(px,temp2.chrom,lenchrom,n); %变异 temp2.chrom=Mutation(pm,temp2.chrom,lenchrom); %保优 individuals.chrom=remain(temp1,temp2,sizepop,GGAP); %计算适应度 for j=1:sizepop x=individuals.chrom(j,:); individuals.fitness(j)=makespan(x,n,k,m,W,Q); end %找到最优染色体及它们在种群中的位置 [newbestfitness,newbestindex]=min(individuals.fitness); trace1(1,i)=newbestfitness; %记下每代的最优值 %代替上一次进化中最好的染色体 if bestfitness>newbestfitness bestfitness=newbestfitness; bestchrom=individuals.chrom(newbestindex,:); bestgen=i; end avgfitness=sum(individuals.fitness)/sizepop; trace=[trace;avgfitness bestfitness]; %记录每一代最好的适应度和平均适应度 end %进化结束 %% 画甘特图 gant(bestchrom,n,k,m,W,Q); %% 画进化图 figure(1); plot(1:maxgen,trace(:,1),1:maxgen,trace(:,2)); grid on; legend('适应度平均值','适应度最优值') xlabel('遗传代数') ylabel('适应度值') title('进化过程') end (2)Cross.m function [ ret ] = Cross( px,chrom,lenchrom,n ) %本函数完成交叉操作 %px input:交叉概率 %chrom input:染色体群 %sizepop input:种群规模 %lenchrom input:染色体长度 %ret output:交叉后的染色体 sizepop=size(chrom,1); i=1; randjob=4; %变动工件数 while i<=sizepop %是否进行交叉操作由交叉概率决定(continue控制) %随机选择两个染色体进行交叉 temp=randperm(sizepop); index=temp(1:2); s1=chrom(index(1),:); s2=chrom(index(2),:); %初始化中间片段 s11=zeros(1,n*2); s22=zeros(1,n*2); index=zeros(2,n*2); %交叉概率决定是否交叉 pick=rand; while pick==0 pick=rand; end id1=1; id2=1; if pick<px %随机选择两个工件 tempjob=randperm(n); job1=tempjob(1,1); job2=tempjob(1,2); job3=tempjob(1,3); job4=tempjob(1,4); %job5=tempjob(1,5); for j=1:lenchrom if s1(1,j)==job1 index(1,id1)=j; s11(1,id1)=job1; id1=id1+1; end if s1(1,j)==job2 index(1,id1)=j; s11(1,id1)=job2; id1=id1+1; end if s1(1,j)==job3 index(1,id1)=j; s11(1,id1)=job3; id1=id1+1; end if s1(1,j)==job4 index(1,id1)=j; s11(1,id1)=job4; id1=id1+1; end end for j=1:lenchrom if s2(1,j)==job1 index(2,id2)=j; s22(1,id2)=job1; id2=id2+1; end if s2(1,j)==job2 index(2,id2)=j; s22(1,id2)=job2; id2=id2+1; end if s2(1,j)==job3 index(2,id2)=j; s22(1,id2)=job3; id2=id2+1; end if s2(1,j)==job4 index(2,id2)=j; s22(1,id2)=job4; id2=id2+1; end end %交叉后的第一条染色体 for j=1:(randjob*n) id1=index(1,j); s1(1,id1)=s22(1,j); end %交叉后的第二条染色体 for j=1:(randjob*n) id2=index(2,j); s2(1,id2)=s11(1,j); end; end if i==sizepop %调整ret染色体数目,控制为奇数 ret(i,:)=s1; else ret(i,:)=s1; ret((i+1),:)=s2; end i=i+2; end end (3)gant.m %% 本函数用来画出最优值的甘特图 % n input: 工件总数 % m input: 机器总数 % k input: 工序总数 % Q input: 机器序号矩阵,n*k % W input: 操作时间矩阵,n*k % Y1p output: 各工件各工序的开始时刻,可根据它绘出甘特图 % Y2p output: 各工件各工序的结束时刻,可根据它绘出甘特图 % M0 机器空闲时间段的计数 % M1 机器空闲时间矩阵 % op_num 工件工序号 op_num=ones(1,n);%三个工件待加工的工序号,初始化为1,即从第一道工序开始加工 %初始化各工序在机床上对应的开始时间和结束时间 Y1p=zeros(n,m); Y2p=zeros(n,m); M0=ones(1,m); %计数用,表示各机床的整空闲时间段数量 M1=zeros(m,80); %初始化机床空闲时间矩阵,三行分别对应三台机床。每一行20个数,分为10组,相邻两个数为一组,表示一个空闲时间段的起止时间 for machine_num=1:m M1(machine_num,1:2)=[0 2000]; end %M1(1:m,1:2)=[0 2000;0 2000;0 2000;0 2000;0 2000;0 2000;0 2000;0 2000;0 2000;0 2000]; %设置各个机床的初始空闲时间段 %%活动化安排工序 for r=1:k job=chrom(r);%确定当前调度工件 operation=op_num(1,job);%确定当前调度工序 machine=Q(job,operation);%确定当前调度机床 operation_time=W(job,operation);%确定调度工序的加工时间 %安排当前调度工件的首道工序 if operation==1 p=M0(1,machine);%当前调度机床的空闲时间段数量 for i=1:p %该机床第i个空闲时间段的起止时间a和b a=M1(machine,i*2-1); b=M1(machine,i*2); %该工序是否能插入该空闲时间段 if a+operation_time==b %空闲时间长度等于加工时长 %确定当前调度工件的起止时间 Y1p(job,operation)=a; Y2p(job,operation)=b; %更新第i个空闲时间段的起止时间 M1(machine,i*2-1)=0; M1(machine,i*2)=0; break; end; if a+operation_time<b %空闲时间长度大于加工时长 Y1p(job,operation)=a; Y2p(job,operation)=a+operation_time; M1(machine,i*2-1)=a+operation_time; M1(machine,i*2)=b; break; end; end %不是第一道工序 else p=M0(1,machine); for i=1:p a=M1(machine,i*2-1); b=M1(machine,i*2); early=Y2p(job,operation-1); %获取当前调度工序的最早允许加工时间 if early<=a %最早允许加工时间早于或等于空闲时间段起始点 if a+operation_time==b %从空闲时间段起始点开始加工,空闲时间段结束点正好完成加工 Y1p(job,operation)=a; Y2p(job,operation)=b; M1(machine,i*2-1)=0; M1(machine,i*2)=0; break; end; if a+operation_time<b %从空闲时间段起始点开始加工,空闲时间段结束点前完成加工 Y1p(job,operation)=a; Y2p(job,operation)=a+operation_time; M1(machine,i*2-1)=a+operation_time; M1(machine,i*2)=b; break; end; end; if early>a %最早允许加工时间迟于空闲时间段起始点 if early+operation_time==b %从最早允许加工时间开始加工,空闲时间段结束点正好完成加工 Y1p(job,operation)=early; Y2p(job,operation)=b; M1(machine,i*2-1)=a; M1(machine,i*2)=early; break; end; if early+operation_time<b %从最早允许加工时间开始加工,空闲时间段结束点前完成加工 Y1p(job,operation)=early; Y2p(job,operation)=early+operation_time; M1(machine,i*2-1)=a; M1(machine,i*2)=early; %该空闲被一分为二,更新第二个子空闲时间段,将其排在原空闲时间矩阵最后一个时间段之后 M1(machine,p*2+1)=early+operation_time; M1(machine,p*2+2)=b; M0(machine)=M0(machine)+1;%更新该机器的空闲阶段数 break; end; end; end end; op_num(1,job)=op_num(1,job)+1;%更新工件的待加工工序号 finish=max(Y2p); minf=finish(1,m); end; %画甘特图 figure(2); w=0.5; %横条宽度 set(gcf,'color','w'); color=[0.97 0.97 1; 1 0.93 0.83; 0.69 0.87 0.90; 1.00 0.84 0; 0.91 0.58 0.47; 0.48 0.98 0; 0.23 0.70 0.44; 1 0.85 0.72; 0.61 0.43 1.00; 0.21 0.35 0.80; 0.38 0.45 0; 1 0 0.25; 0.8 0.25 0.3; 0.8 0.8 0.1; 0.75 0.64 0.81]; for job=1:n for operation=1:(k/n) operation_time=W(job,operation); machine=Q(job,operation); x=[Y1p(job,operation) Y1p(job,operation) Y2p(job,operation) Y2p(job,operation)]; y=machine+[-w/2 w/2 w/2 -w/2]; p=patch('xdata',x,'ydata',y,'facecolor',color(job,:),'edgecolor','k'); txt=sprintf('J%d',job);%将工序号和加工时间连成字符串 text(Y1p(job,operation)+0.2,machine,txt); end; end; xlabel('processing time(s)'); ylabel('Machine'); axis([0 minf 0 m+1]); set(gca,'Box','on'); set(gca,'YTick',0:10); set(gca,'YTickLabel',{'';num2str((1:m)','M%d');''}); end (4)makespan.m function [ minf ] = makespan( chrom,n,k,m,W,Q ) %% 本函数用来计算最小化的最大完工时间 % n input: 工件总数 % m input: 机器总数 % k input: 工序总数 % Q input: 机器序号矩阵,n*k % W input: 操作时间矩阵,n*k % Y1p output: 各工件各工序的开始时刻,可根据它绘出甘特图 % Y2p output: 各工件各工序的结束时刻,可根据它绘出甘特图 % M0 机器空闲时间段的计数 % M1 机器空闲时间矩阵 % op_num 工件工序号 op_num=ones(1,n);%n个工件待加工的工序号,初始化为1,即从第一道工序开始加工 %初始化各工序在机床上对应的开始时间和结束时间 Y1p=zeros(n,m); Y2p=zeros(n,m); M0=ones(1,m); %计数用,表示各机床的整空闲时间段数量 M1=zeros(m,100); %初始化机床空闲时间矩阵,三行分别对应三台机床。每一行20个数,分为10组,相邻两个数为一组,表示一个空闲时间段的起止时间 for machine_num=1:m M1(machine_num,1:2)=[0 2000]; end %%活动化安排工序 for r=1:k job=chrom(r);%确定当前调度工件 operation=op_num(1,job);%确定当前调度工序 machine=Q(job,operation);%确定当前调度机床 operation_time=W(job,operation);%确定调度工序的加工时间 %安排当前调度工件的首道工序 if operation==1 p=M0(1,machine);%当前调度机床的空闲时间段数量 for i=1:p %该机床第i个空闲时间段的起止时间a和b a=M1(machine,i*2-1); b=M1(machine,i*2); %该工序是否能插入该空闲时间段 if a+operation_time==b %空闲时间长度等于加工时长 %确定当前调度工件的起止时间 Y1p(job,operation)=a; Y2p(job,operation)=b; %更新第i个空闲时间段的起止时间 M1(machine,i*2-1)=0; M1(machine,i*2)=0; break; end; if a+operation_time<b %空闲时间长度大于加工时长 Y1p(job,operation)=a; Y2p(job,operation)=a+operation_time; M1(machine,i*2-1)=a+operation_time; M1(machine,i*2)=b; break; end; end %不是第一道工序 else p=M0(1,machine); for i=1:p a=M1(machine,i*2-1); b=M1(machine,i*2); early=Y2p(job,operation-1); %获取当前调度工序的最早允许加工时间 if early<=a %最早允许加工时间早于或等于空闲时间段起始点 if a+operation_time==b %从空闲时间段起始点开始加工,空闲时间段结束点正好完成加工 Y1p(job,operation)=a; Y2p(job,operation)=b; M1(machine,i*2-1)=0; M1(machine,i*2)=0; break; end; if a+operation_time<b %从空闲时间段起始点开始加工,空闲时间段结束点前完成加工 Y1p(job,operation)=a; Y2p(job,operation)=a+operation_time; M1(machine,i*2-1)=a+operation_time; M1(machine,i*2)=b; break; end; end; if early>a %最早允许加工时间迟于空闲时间段起始点 if early+operation_time==b %从最早允许加工时间开始加工,空闲时间段结束点正好完成加工 Y1p(job,operation)=early; Y2p(job,operation)=b; M1(machine,i*2-1)=a; M1(machine,i*2)=early; break; end; if early+operation_time<b %从最早允许加工时间开始加工,空闲时间段结束点前完成加工 Y1p(job,operation)=early; Y2p(job,operation)=early+operation_time; M1(machine,i*2-1)=a; M1(machine,i*2)=early; %该空闲被一分为二,更新第二个子空闲时间段,将其排在原空闲时间矩阵最后一个时间段之后 M1(machine,p*2+1)=early+operation_time; M1(machine,p*2+2)=b; M0(machine)=M0(machine)+1;%更新该机器的空闲阶段数 break; end; end; end end; op_num(1,job)=op_num(1,job)+1;%更新工件的待加工工序号 end; finish=max(Y2p); minf=finish(1,m); end (5)Mutation.m function [ ret ] = Mutation( pm,chrom,lenchrom ) %本函数完成变异操作 %pm input:变异概率 %chrom input:染色体群 %sizepop input:种群规模 %lenchrom input:染色体长度 %pop input:当前种群的进化代数和最大的进化代数信息 %ret output:变异后的染色体 sizepop=size(chrom,1); for i=1:sizepop %随机选择一个染色体进行变异 index=randi(sizepop,1,1); %变异概率决定该轮循环是否进行变异 pick=rand; if pick<pm %选随机选择两个位置交换基因 pos=randperm(lenchrom); pos1=pos(1,1); pos2=pos(1,2); v=chrom(index,:); gene1=v(1,pos1); gene2=v(1,pos2); v(1,pos1)=gene2; v(1,pos2)=gene1; chrom(index,:)=v; end end ret=chrom; end (6)Select.m function [ ret ] = Select( individuals,sizepop,GGAP ) %本函数对每一代种群中的染色体进行选择,以进行后面的交叉和变异。选择方法采用轮盘赌方式 %individuals input:种群信息 %sizepop input:种群规模 %ret output:经过选择后的种群 goodpop=ceil(sizepop*(1-GGAP)); %计算保优个体数 temppop=sizepop-goodpop; %计算临时子代个体数 %% 计算累计概率 individuals.fitness=1./(individuals.fitness); %求最小值,函数值小的适应度大 sumfitness=sum(individuals.fitness); %求总适应度 p=individuals.fitness./sumfitness; %归一化 sump=zeros(1,sizepop); %初始化累计概率 %计算累计概率 for i=1:sizepop for j=1:i sump(1,i)=sump(1,i)+p(j); end end %% 轮盘赌选择 index=zeros(1,temppop); %记录每次轮盘赌被选择个体的索引号 for i=1:temppop %转temppop次轮盘 pick=rand; while pick==0 pick=rand; end for j=1:sizepop if pick<=sump(1,j) %寻找到落入区间的染色体,其索引号为j index(1,i)=j; break; end end end %% 产生子种群 ret=struct('fitness',zeros(1,temppop),'chrom',[]); ret.chrom=individuals.chrom(index,:); ret.fitness=individuals.fitness(index); end
2️⃣✨✨✨结果✨✨✨
算例
10*10 %工件操作对应时间 W=[29 78 9 36 49 11 62 56 44 21; 43 90 75 11 69 28 46 46 72 30; 91 85 39 74 90 10 12 89 45 33; 81 95 71 99 9 52 85 98 22 43; 14 6 22 61 26 69 21 49 72 53; 84 2 52 95 48 72 47 65 6 25; 46 37 61 13 32 21 32 89 30 55; 31 86 46 74 32 88 19 48 36 79; 76 69 76 51 85 11 40 89 26 74; 85 13 61 7 64 76 47 52 90 45]; %机器工艺约束 Q=[1 2 3 4 5 6 7 8 9 10; 1 3 5 10 4 2 7 6 8 9; 2 1 4 3 9 6 8 7 10 5; 2 3 1 5 7 9 8 4 10 6; 3 1 2 6 4 5 9 8 10 7; 3 2 6 4 9 10 1 7 5 8; 2 1 4 3 7 6 10 9 8 5; 3 1 2 6 5 7 9 10 8 4; 1 2 4 6 3 10 7 8 5 9; 2 1 3 7 9 10 6 4 5 8]; 6*6 %操作对应时间 W=[1 3 6 7 3 6; 8 5 10 10 10 4; 5 4 8 9 1 7; 5 5 5 3 8 9; 9 3 5 4 3 1; 3 3 9 10 4 1]; %机器工艺约束 Q=[3 1 2 4 6 5; 2 3 5 6 1 4; 3 4 6 1 2 5; 2 1 3 4 5 6; 3 2 5 6 1 4; 2 4 6 1 5 3]; 3*3 %操作对应时间 W=[5 1 3; 2 1 4; 3 6 8]; %机器工艺约束 Q=[1 3 2; 2 1 3; 3 1 2];