PS:本文章对于基于遗传算法的传统作业车间调度问题的求解(Job-shop scheduling problem based on genetic algorithm)_遗传算法求解调度问题仿真实验-CSDN博客
中提到的代码进行详细解释,仅作为学习记录
(1)编码
% 初始化一个空列表
initial = [];
% 根据作业的步骤生成初始序列
for i = 1 : num_of_jobs
for j = 1 : number_of_machines
% 将作业索引 i 添加到 initial 列表中
initial = [initial i];
end
end
% 生成包含随机基因的种群染色体
for i = 1 : PopSize
% 使用 randperm 函数对 initial 列表进行随机排列,并将结果赋值给种群矩阵的当前行
population(i, :) = initial(randperm(length(initial(:))));
end
代码解释:
-
initial=[];
:初始化一个空列表initial
,用于存储作业的步骤。 -
for i = 1:num_of_jobs
:外层循环,遍历作业的步骤。 -
for j = 1:number_of_machines
:内层循环,遍历每个作业在机器上的执行顺序。 -
initial=[initial i];
:将当前作业的索引i
添加到initial
列表中。这样做的目的是生成一个基本的作业顺序,其中每个作业在每个机器上执行一次。在每次迭代中,将作业索引添加到initial
列表。 -
外层循环结束后,
initial
列表中将包含按照作业步骤顺序生成的元素。 -
for i = 1:PopSize
:循环生成种群。 -
population(i,:) = initial(randperm(length(initial(:))));
:对initial
列表进行随机排列,并将结果赋值给种群矩阵的当前行。randperm(length(initial(:)))
生成一个长度与initial
列表相同的随机排列索引,然后使用这些索引对initial
列表进行重新排列。这样得到的结果就是一个随机的染色体,即一种作业顺序的排列。 -
最终,
population
矩阵的每一行代表一个随机生成的染色体,即一种作业顺序的排列。这个种群矩阵将作为遗传算法的初始种群,用于后续的进化和优化过程。
(2)解码
function [Jobs, Cmax, MachineList, ST, PT] = SemiActiveDecoding(T, Chromosome)
% INPUT:
% T -- 输入矩阵:
% 每个实例由一行描述组成,一行包含作业数量和机器数量,然后每个作业占用一行,
% 列出每个作业的每个步骤的机器编号和处理时间。机器从0开始编号。
% Chromosome -- 要解码的染色体
% OUTPUT:
% Jobs -- 作业序列
% Cmax -- 最大完成时间
% MachineList -- 与染色体对应的机器序列
% ST -- 染色体中每个作业步骤的开始时间
% PT -- 染色体中每个作业步骤的处理时间
% +++++++++++++++++++++++++++++
% Fisher和Thompson 6x6实例,备用名称(mt06)
% 6 6
% 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
% +++++++++++++++++++++++++++++
%% start
[num_of_jobs, number_of_machines] = size(T);
%% 在这个例子中,每个作业有6个步骤,
%% 每个步骤包含机器编号和处理时间。因此,
%%总共有12个列,其中6列是机器编号,6列是处理时间。
number_of_machines = number_of_machines / 2; % 作业和机器的数量
Jobs = unique(Chromosome);
StepList = []; % 所有基因的步骤列表
MachineList = [];
DecodedGenes = [];
ST = [];
PT = [];
len_of_chromosome = num_of_jobs * number_of_machines;
%% 计算了 MachineList 和 PT,即机器序列和处理时间。
for index = 1:len_of_chromosome
DecodedGenes = [DecodedGenes Chromosome(index)];
postion = length(find(DecodedGenes == Chromosome(index)));
StepList = [StepList postion];
pos1 = postion * 2 - 1;
pos2 = postion * 2;
MachineList = [MachineList T(Chromosome(index), pos1)];
PT = [PT T(Chromosome(index), pos2)];
end
%% 计算 ST,即作业的开始时间
Machines = unique(MachineList);
steps = unique(StepList);
job_start_time = cell(num_of_jobs, 1);
job_end_time = cell(num_of_jobs, 1);
machine_start_time = cell(number_of_machines, 1);
machine_end_time = cell(number_of_machines, 1);
machine_state = zeros(1, number_of_machines); % 0--FirstWork; 1--NotFirst
for index = 1:len_of_chromosome
job = Chromosome(index);
machine = MachineList(index);
pt = PT(index);
step = StepList(index);
pos_m = find(Machines == machine);
pos_j = find(Jobs == job);
if step == 1 % 第一个步骤,不考虑同一作业步骤之间的约束
if machine_state(pos_m) == 0 % 机器首次使用
job_start_time{pos_j} = [0, pos_m];
job_end_time{pos_j} = [job_start_time{pos_j}(1) + pt, pos_m];
else
job_start_time{pos_j} = [machine_end_time{pos_m}(1), pos_m];
job_end_time{pos_j} = [job_start_time{pos_j}(1) + pt, pos_m];
end
else
if machine_state(pos_m) == 0 % 机器首次使用
job_start_time{pos_j} = [job_end_time{pos_j}(1), pos_m];
job_end_time{pos_j} = [job_start_time{pos_j}(1) + pt, pos_m];
else
job_start_time{pos_j} = [max(machine_end_time{pos_m}(1), job_end_time{pos_j}(1)), pos_m];
job_end_time{pos_j} = [job_start_time{pos_j}(1) + pt, pos_m];
end
end
machine_start_time{pos_m} = [job_start_time{pos_j}(1)];
machine_end_time{pos_m} = [job_end_time{pos_j}(1)];
machine_state(pos_m) = 1;
ST = [ST, job_start_time{pos_j}(1)];
end
%% 计算 Cmax,即最大完成时间
end_time = cell2mat(job_end_time);
Cmax = max(end_time(:, 1));
end
-
获取输入参数:
T
:输入矩阵,描述了作业和机器的相关信息。每个实例都由一行描述组成,接下来一行包含作业数和机器数的信息,然后是每个作业的描述,包括每个步骤的机器编号和处理时间。Chromosome
:待解码的染色体。
-
初始化变量:
num_of_jobs
:作业数量number_of_machines
:机器数量Jobs
:唯一的作业列表StepList
:每个基因的步骤列表MachineList
:每个基因的机器列表DecodedGenes
:已解码的基因序列ST
:每个作业步骤的开始时间PT
:每个作业步骤的持续时间len_of_chromosome
:染色体长度,即基因数量
-
计算
MachineList
和PT
:
对于染色体中的每个基因,将其添加到DecodedGenes
中,并计算其在DecodedGenes
中的位置。然后根据位置计算步骤在T
矩阵中的索引,并将对应的机器和持续时间添加到MachineList
和PT
中。 -
计算
ST
:- 初始化变量:
Machines
:唯一的机器列表steps
:唯一的步骤列表job_start_time
和job_end_time
:用于存储每个作业的开始时间和结束时间的单元格数组machine_start_time
和machine_end_time
:用于存储每个机器的开始时间和结束时间的单元格数组machine_state
:机器状态的数组,用于跟踪机器是否是第一次使用
- 遍历染色体中的每个基因:
- 获取作业、机器、持续时间和步骤信息
- 查找机器和作业在唯一列表中的位置
- 根据步骤是否为第一步骤,计算作业的开始时间和结束时间
- 更新机器的开始时间和结束时间,以及机器状态
- 将作业的开始时间添加到
ST
中
- 初始化变量:
-
计算
Cmax
:- 将所有作业的结束时间提取到一个矩阵中
- 计算最大结束时间,即最大完成时间
-
返回结果:
Jobs
:作业序列Cmax
:最大完成时间MachineList
:机器序列ST
:作业步骤的开始时间PT
:作业步骤的持续时间
详细代码解释:
部分1:
for index = 1:len_of_chromosome
DecodedGenes = [DecodedGenes Chromosome(index)];
position = length(find(DecodedGenes == Chromosome(index)));
StepList = [StepList position];
pos1 = position * 2 - 1;
pos2 = position * 2;
MachineList = [MachineList T(Chromosome(index), pos1)];
PT = [PT T(Chromosome(index), pos2)];
end
for
循环迭代染色体中的每个基因(index
从1到len_of_chromosome
)。- 将当前基因
Chromosome(index)
添加到DecodedGenes
列表中,以构建解码后的基因序列。 - 计算当前基因在
DecodedGenes
中的位置,即该基因在已解码基因序列中的出现次数。使用find
函数找到与当前基因相等的元素,然后使用length
函数计算找到的元素数量。 - 将当前基因在已解码基因序列中的位置(即出现次数)添加到
StepList
列表中,以记录每个基因的位置信息。 - 根据基因在已解码基因序列中的位置计算机器编号在输入矩阵
T
中的索引。将pos1
设置为当前位置乘以2减去1,将pos2
设置为当前位置乘以2。 - 从输入矩阵
T
中获取对应的机器编号和处理时间。使用T(Chromosome(index), pos1)
获取机器编号,并将其添加到MachineList
列表中。使用T(Chromosome(index), pos2)
获取处理时间,并将其添加到PT
列表中。
部分2:
%% Caculate ST
Machines = unique(MachineList);
steps = unique(StepList);
- 代码中使用
unique
函数获取MachineList
中的唯一机器编号,并将结果存储在Machines
变量中。这样做是为了确定在解码后的基因序列中有多少台不同的机器参与。 - 接下来,使用
unique
函数获取StepList
中的唯一步骤编号,并将结果存储在steps
变量中。这样做是为了确定在解码后的基因序列中有多少个不同的步骤。
部分3:
job_start_time = cell(num_of_jobs,1);
job_end_time = cell(num_of_jobs,1);
machine_start_time = cell(number_of_machines,1);
machine_end_time = cell(number_of_machines,1);
machine_state = zeros(1,number_of_machines);
- 创建一个大小为
num_of_jobs
的cell数组job_start_time
,用于存储每个作业的开始时间。 - 创建一个大小为
num_of_jobs
的cell数组job_end_time
,用于存储每个作业的结束时间。 - 创建一个大小为
number_of_machines
的cell数组machine_start_time
,用于存储每台机器的开始时间。 - 创建一个大小为
number_of_machines
的cell数组machine_end_time
,用于存储每台机器的结束时间。 - 创建一个大小为1x
number_of_machines
的零矩阵machine_state
,用于记录每台机器的状态。其中0表示该机器是第一次执行作业,1表示该机器不是第一次执行作业。
部分4:
for index = 1:len_of_chromosome
job = Chromosome(index);
machine = MachineList(index);
pt = PT(index);
step = StepList(index);
pos_m = find(Machines==machine);
pos_j = find(Jobs==job);
for
循环遍历基因序列中的每个基因。job
变量存储当前基因对应的作业编号。machine
变量存储当前基因对应的机器编号。pt
变量存储当前基因对应的处理时间。step
变量存储当前基因对应的步骤编号。pos_m
变量记录机器编号在Machines
数组中的位置。pos_j
变量记录作业编号在Jobs
数组中的位置。
if step==1
if machine_state(pos_m)==0
job_start_time{pos_j}=[0,pos_m];
job_end_time{pos_j}=[job_start_time{pos_j}(1)+pt,pos_m];
else
job_start_time{pos_j}=[machine_end_time{pos_m}(1),pos_m];
job_end_time{pos_j}=[job_start_time{pos_j}(1)+pt,pos_m];
end
else
if machine_state(pos_m)==0
job_start_time{pos_j}=[job_end_time{pos_j}(1),pos_m];
job_end_time{pos_j}=[job_start_time{pos_j}(1)+pt,pos_m];
else
job_start_time{pos_j}=[max(machine_end_time{pos_m}(1),job_end_time{pos_j}(1)),pos_m];
job_end_time{pos_j}=[job_start_time{pos_j}(1)+pt,pos_m];
end
end
- 如果
step
等于1,表示当前基因是作业的第一个步骤,不考虑作业内部步骤之间的约束。- 如果机器状态
machine_state
在位置pos_m
上为0,表示该机器是第一次被使用。- 将作业的开始时间设置为
[0, pos_m]
。 - 将作业的结束时间设置为
[job_start_time{pos_j}(1) + pt, pos_m]
。
- 将作业的开始时间设置为
- 否则,表示该机器已经被使用过。
- 将作业的开始时间设置为
[machine_end_time{pos_m}(1), pos_m]
。 - 将作业的结束时间设置为
[job_start_time{pos_j}(1) + pt, pos_m]
。
- 将作业的开始时间设置为
- 如果机器状态
- 否则,表示当前基因是作业的其他步骤。
- 如果机器状态
machine_state
在位置pos_m
上为0,表示该机器是第一次被使用。- 将作业的开始时间设置为
[job_end_time{pos_j}(1), pos_m]
。 - 将作业的结束时间设置为
[job_start_time{pos_j}(1) + pt, pos_m]
。
- 将作业的开始时间设置为
- 否则,表示该机器已经被使用过。
- 将作业的开始时间设置为
[max(machine_end_time{pos_m}(1), job_end_time{pos_j}(1)), pos_m]
。 - 将作业的结束时间设置为
[job_start_time{pos_j}(1) + pt, pos_m]
。
- 将作业的开始时间设置为
- 如果机器状态
machine_start_time{pos_m}= [job_start_time{pos_j}(1)];
machine_end_time{pos_m} = [job_end_time{pos_j}(1)];
machine_state(pos_m)=1;
ST=[ST, job_start_time{pos_j}(1)];
end
- 更新机器的开始时间
machine_start_time
为对应作业的开始时间的第一个元素。 - 更新机器的结束时间
machine_end_time
为对应作业的结束时间的第一个元素。 - 将机器状态
machine_state
在位置pos_m
上设置为1,表示该机器已被使用过。 - 将作业的开始时间的第一个元素添加到
ST
数组中。 - 结束循环。
以上代码段的目的是根据基因序列中的信息计算每个作业的开始时间和结束时间,以及每台机器的开始时间和结束时间。根据不同的步骤和机器状态,更新相应的时间记录。
部分5:
end_time = cell2mat(job_end_time);
- 将
job_end_time
这个cell数组转换为矩阵end_time
。job_end_time
中存储了每个作业的结束时间,每个元素是一个包含开始时间和机器编号的向量。通过cell2mat
函数,将这些向量转换为矩阵形式。
Cmax = max(end_time(:,1));
- 从矩阵
end_time
中选择所有行的第一列,即所有作业的结束时间。 - 通过
max
函数,找到这些结束时间中的最大值。 - 将最大值赋给变量
Cmax
,表示任务调度中的最大完成时间。