[仅交流,有瑕疵]遗传算法求解混合流水车间调度问题(HFSP)二:算法实现一

遗传算法的设计

  • 编码:对工件进行优先级编码,编码越小,优先级越高。
  • 解码:按照工件优先级进行生产,求出整体完工时间。
  • 目标函数值:整体完工时间。
  • 适应度值:目标函数越小,适应度值越大。
  • 选择:按照轮盘赌方法进行选择。
  • 交叉:按照交叉概率,选择两个父代个体(父1和父2),交叉生成两个子代个体(子1和子2)。生成一个随机的交换空间,交换空间内,子1基因来自于父2,子2基因来自于父1;交换空间外,子1基因来自于父1,子2基因来自于父2。注意任意两个零件优先级不能相等。
  • 变异:按照变异概率,随机交换两个基因。
  • 终止条件:迭代固定次数。

计算结果

在本例中,有6个工件,有3道工序,每道工序有2台机器,下面是执行结果:

最优序列:
3 4 6 2 1 5

加工流程图

上图中,第1、2行是第1工序的2台设备,第3、4行是第2工序的2台设备,第5、6行是第3工序的两台设备,纵轴代表时间。按照最优序列3 4 6 2 1 5赋予每个零件优先级,一共用时25.

主函数

首先定义问题的参数:

piecetime = [2 4 6; ...             % 设备加工时间
    4 9 2; 4 2 8; 9 5 6; 5 2 7; 9 4 3];
equsize = [2 2 2];                  % 每个工序设备数目
piecesize = size(piecetime, 1);     % 工件数目
prosize = size(piecetime, 2);       % 工序数目

在本例中,一共有6个设备,3道工序,设备必须按照1-2-3的工序进行加工,每个工序有2台机器。一共有6个工件。piecetime中行代表工件,列代表工序,内容是加工时间,比如第1行第1列,表示工件1在工序1加工时间为2.

定义遗传算法的参数:

popsize = 20;       % 种群规模
cr = 0.6;           % 交叉概率
mr = 0.05;          % 变异概率
maxgen = 100;       % 迭代次数

进行迭代:

pop = initpop(popsize, piecesize);
for gen = 1:maxgen
    [objvalue, ptr, per] = calobjvalue(pop, piecetime, equsize);    
    fitness = calfitness(objvalue);     % 计算适应度值
    pop = selection(pop, fitness);      % 选择
    pop = crossover(pop, cr);           % 交叉
    pop = mutation(pop, mr);            % 变异
end

主函数全部代码如下:

function main()
piecetime = [2 4 6; ...             % 设备加工时间
    4 9 2; 4 2 8; 9 5 6; 5 2 7; 9 4 3];
equsize = [2 2 2];                  % 每个工序设备数目
piecesize = size(piecetime, 1);     % 工件数目
prosize = size(piecetime, 2);       % 工序数目

popsize = 20;       % 种群规模
cr = 0.6;           % 交叉概率
mr = 0.05;          % 变异概率
maxgen = 100;       % 迭代次数

bestobjvalue = zeros(1, maxgen);
bestpop = zeros(maxgen, piecesize);
avgobjvalue = zeros(1, maxgen);
bestptr = cell(1, maxgen);
bestper = cell(1, maxgen);

pop = initpop(popsize, piecesize);
for gen = 1:maxgen
    [objvalue, ptr, per] = calobjvalue(pop, piecetime, equsize);
    
    [bobjvalue, bindex] = min(objvalue);
    bestobjvalue(1, gen) = bobjvalue;
    bestpop(gen, :) = pop(bindex, :);
    avgobjvalue(1, gen) = sum(objvalue) / popsize;
    bestptr{1, gen} = ptr{1, bindex};
    bestper{1, gen} = per{1, bindex};
    
    fitness = calfitness(objvalue);     % 计算适应度值
    pop = selection(pop, fitness);      % 选择
    pop = crossover(pop, cr);           % 交叉
    pop = mutation(pop, mr);            % 变异
end

[~, finalindex] = min(bestobjvalue);
finalptr = bestptr{1, finalindex};
finalper = bestper{1, finalindex};

fprintf("最优序列:\n");
disp(bestpop(finalindex, :));

gantt = makegantt(finalptr, finalper, equsize);

figure(1);
imagesc(gantt);
colorbar;
title("加工流程图");


figure(2);
plot(1:maxgen, bestobjvalue);
title("最优时间变化图");
xlabel("代数"); ylabel("最优时间");

figure(3);
plot(1:maxgen, avgobjvalue);
title("平均时间变化图");
xlabel("代数"); ylabel("平均时间");

end

计算目标函数值函数

在第一道工序中,所有的工件同时等待被加工,则按照优先级进行加工;在第二道和之后的工序中,由于上一道工序中工件完工时间不同,上一道工序先加工完的工件先进行本工序加工。

function [objvalue, ptr, per] = calobjvalue(pop, piecetime, equsize)
% 计算目标函数值
% pop           input  种群
% piecetime     input  工件加工时间
% equsize       input  每个工序设备数量
% objvalue      output 目标函数值(加工时间)
% ptr           output 工件加工时间记录,cell
% per           output 工件加工设备记录,cell
[popsize, piecesize] = size(pop);
prosize = size(equsize, 2);
objvalue = zeros(popsize, 1);
ptr = cell(1, popsize);
per = cell(1, popsize);
for i = 1:popsize
    pieceweight = pop(i, :);
    
    % 设备状态序列
    % [工序1设备1 工序1设备2 工序2设备1 工序2设备2 ……]
    % 记录当前设备使用结束时间,默认为0表示未开始
    equstu = zeros(1, sum(equsize));

    % 对设备状态序列的工序分隔符
    % 大于等于当前设备最小值的索引是当前设备所处的工序
    % [2 3 5] 工序1有2台设备 工序2有1台设备 工序3有2台设备
    prosep = cumsum(equsize);

    % 工件时间记录,记录每个工件每个工序的开始时间和结束时间
    % 行表示工件,相邻两列表示开始加工时间和停止加工时间
    % [1 2 2 3; 4 5 6 7]
    % 表示工件1第1工序加工时间为1-2,第2工序加工时间为2-3
    % 工件2第1工序加工时间为4-5,第2工序加工时间为6-7
    piecetimerecord = zeros(piecesize, prosize*2);

    % 工件设备记录,记录每个工件在工序中的加工设备
    % 行数表示工件,列表示该零件在每个工序加工设备
    % [1 2; 2 1]
    % 表示工件1在第1工序加工设备为1,第2工序加工设备为2
    % 工件2在第1工序加工设备为2,第2工序加工设备为1
    pieceequrecord = zeros(piecesize, prosize);
    
    % 对每一道工序
    %   如果是第1道工序,对工件按优先级排序
    %     其余工序上上一道工序完工时间对工件排序
    %   对排序后的每一件工件
    %     对该工序中可用机器按使用结束时间排序
    %     使用使用结束时间最小的机器
    %     加工开始时间为max{设备使用结束时间,零件上一工序完工时间}
    %     加工结束时间=加工开始时间+加工时间
    %     更新各个状态和记录矩阵
    for pro = 1:prosize
        if(pro == 1)
            [~, piecelist] = sort(pieceweight);
        else
            tempendtime = piecetimerecord(:, (pro-1)*2);
            tempendtime = tempendtime';
            [~, piecelist] = sort(tempendtime);
        end
        for pieceindex = 1:length(piecelist)
            piece = piecelist(pieceindex);
            equtimelist = equstu(prosep(pro)-equsize(pro)+1:prosep(pro));
            [equtime, equlist] = sort(equtimelist);
            equ = equlist(1);
            if pro == 1
                piecestarttime = 0;
            else
                piecestarttime = piecetimerecord(piece, pro*2-2);
            end
            starttime = max(equtime(1), piecestarttime) + 1;
            endtime = starttime + piecetime(piece, pro) - 1;
            equstuindex = prosep(pro)-equsize(pro)+equ;
            equstu(equstuindex) = endtime;
            piecetimerecord(piece, pro*2-1) = starttime;
            piecetimerecord(piece, pro*2) = endtime;
            pieceequrecord(piece, pro) = equ;
        end
    end
    objvalue(i, 1) = max(max(piecetimerecord));
    ptr{1, i} = piecetimerecord;
    per{1, i} = pieceequrecord;
end
end

选择函数

使用轮盘赌方法进行选择:

function spop = selection(pop, fitness)
% 轮盘赌选择
% pop       input  种群
% fitness   input  适应度值
% spop      output 选择后生成的种群
[popsize, piecesize] = size(pop);
spop = zeros(popsize, piecesize);
sumfit = sum(fitness);
fitness = fitness ./ sumfit;
fitness = cumsum(fitness);
r = rand(1, popsize);
r = sort(r);
j = 1;
for i = 1:popsize
    while fitness(j) < r(i)
        j = j + 1;
    end
    spop(i, :) = pop(j, :);
end

% 由于上面轮盘赌方法特殊性,一个个体在相邻位置多次重复,故随机排序
rr = randperm(popsize);
spop(:, :) = spop(rr, :);
end

交叉函数

按照交叉概率,选择两个父代个体(父1和父2),交叉生成两个子代个体(子1和子2)。生成一个随机的交换空间,交换空间内,子1基因来自于父2,子2基因来自于父1;交换空间外,子1基因来自于父1,子2基因来自于父2。注意任意两个零件优先级不能相等。

function cpop = crossover(pop, cr)
% 交叉
% pop       input  种群
% cr        input  交叉概率
% cpop      output 交叉后种群
[popsize, piecesize] = size(pop);
cpop = pop;

if mod(popsize,2) ~= 0
    nn = popsize - 1;
else
    nn = popsize;
end

% 父代father mother, 子代son daughter
% 在rl:ru中,son继承mother,daughter继承father
% 其余位置son继承father,daughter继承mother
for i = 1:2:nn
    if rand > cr
        continue;
    end
    [rl, ru] = makerlru(piecesize);
    
    father = pop(i, :);
    mother = pop(i+1, :);
    if father == mother
        continue;
    end
    son = zeros(1, piecesize);
    daughter = zeros(1, piecesize);
    son(rl:ru) = mother(rl:ru);
    daughter(rl:ru) = father(rl:ru);
    
    j = 1;
    for k = 1:piecesize
        if k >= rl && k <= ru
            continue;
        end
        while ~isempty(find(son == father(j), 1))
            j = j + 1;
        end
        son(k) = father(j);
    end
    
    j = 1;
    for k = 1:piecesize
        if k >= rl && k <= ru
            continue;
        end
        while ~isempty(find(daughter == mother(j), 1))
            j = j + 1;
        end
        daughter(k) = mother(j);
    end
    
    cpop(i, :) = son;
    cpop(i+1, :) = daughter;
end
end

变异函数

随机交换染色体中两个位置的基因:

function mpop = mutation(pop, mr)
% 变异,交换两个随即位置的基因
% pop       input  种群
% mr        input  变异概率
% mpop      output 变异后种群
[popsize, piecesize] = size(pop);
mpop = pop;
for i = 1:popsize
    if rand > mr
        continue;
    end
    r1 = randi(piecesize);
    r2 = randi(piecesize);
    temp  = mpop(i, r1);
    mpop(i, r1) = mpop(i, r2);
    mpop(i, r2) = temp;
end
end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容