基于遗传算法的护士排班问题(含Matlab源码)

目录

前言

一、排班问题

二、遗传算法

1.背景介绍

2.编码

总结




前言

        对于遗传算法的学习,相信很多和我一样的初学者都是从最简单的二元函数求极值开始的。一步步的理解去理解种群数量、基因长度、迭代次数、交叉变异概率、初始种群的生成以及二进制编码转换为十进制编码。针对上述二元函数求极值问题经过自己的学习调试之后,本人针对护理人员排班问题进行了算法设计。

        护士排班问题是能够影响一个科室能否满足运转需求以及需要保证24小时有人工作情况下护士的休息以及工作效率,一个良好的排班必须要兼顾岗位需求以及满足人员需求。本文采用遗传算法建立护士排班表,并将实际的约束条件进行输入,能够提供满足需求的排班表。




一、排班问题

        在护理人员排班中,最为理想的方案是对于所有的约束条件,每天的在岗人员以及每个护理人员一周的工作时间都满足要求,因此排班方案的依据要看约束条件的满足情况。护理人员排班中的约束条件可以分为两大类:一类是硬约束,如果不满足条件,则为无效的排班表,如一个上完夜班的护理人员第二天必须休息,否则将过度劳累;另一类则为软约束,这类约束条件尽量满足即可,例如一次16小时的夜班后保证至少休息一天的前提下尽量休息两天。在考虑排班的约束条件时,还要尽量兼顾到每个护士的满意度,如一周40小时的工时,尽量少调休,保证每周的休息均衡。

        虽然理想方案是满足所有的硬约束与软约束,但是在实际的约束条件适应值函数设置中,可能由于本人才疏学浅,函数设计的不恰当,导致无法达到理想值,只能追求尽量的优化。实际排班过程中,本方案设置了两个约束条件的适应值函数y1、y2。


二、遗传算法

1.背景介绍

        某医院的急诊科有16名护理人员,前12名为老员工,后4名为新员工,每天有六种班型:早班(8:00 - 16:30)4种:A1(导诊台)、A2(抢救室)、A3(留观室)、A4(外围);晚班(16:30 - 24:00)1种:P帮;夜班(16:30 - 8:00)1种:P+N。

        早班A1-A4需要至少4名护理人员工作,其中A2与A4最好有两名护理人员在岗,晚班护理人员只是帮助夜班人员做辅助工作,夜班需要两名护理人员。确定约束条件如下:①某天一名护理人员上完夜班后,第二天一定要休息;②每天的晚班仅由一名新人来上,慢慢熟悉夜班内容;③每天仅需两名老员工上夜班;④A1-A4均至少一人在岗,其中A2和A4是较为重要的岗位最好有两名员工在岗;⑤夜班后至少休息一天,最好休息两天;⑥尽量少调休,标准班为每人每周上5次班,一个夜班算2次班

2.编码

        在采用遗传算法时,先确定染色体的编码,这里一个染色体代表一个排班表,染色体中每三位二进制编码代表一个班型:000与111表示休息日,001、010、011、100分别表示A1-A4,101代表晚班,110代表夜班。(此处休息日用两个基因表现是为了尽量满足双休)

        因此,对于一周7天,每天16名护理人员,一个染色体由7*16 = 112个护理人员基因组成,每一个基因均采用复合基因,再由3位的班期基因组成。因此,一个排班表中周一的基因构成如图1所示,由16*3=48位二进制编码组成。以此类推,一周的排班表由一个7*16*3 = 336位编码的染色的组成。

       

         废话少说,直接上代码:

clear all
close all

pop = 50; %初始种群数量
length = 336; %种群基因编码长度,一周七天,每天16个排班人员,总共7种班型(用3位二进制编码表示)
gen = 1000; %迭代次数
crossover_probablity = 0.9; %交叉概率
variation_probablity = 0.8; %变异概率
initial_pop = round(rand(pop,length)); %生成初始种群

%算法迭代m次
for m=1:gen
    %将每一代 染色体长度为336位的二进制种群 转化为16*7的矩阵(16个护理人员,一周7天)
    x = zeros(16,7,pop)
    for i = 1:size(initial_pop,1) %分别遍历50个排班表
        for j = 1:7  %遍历一周七天
            for k = 1:16 %遍历16个排班人员
                for l = 1:3 %二进制班型转换为十进制
                    x(k,j,i) = initial_pop ( i,48*(j-1)+3*(k-1)+l ) * 2^(3-l) + x(k,j,i);
                end
            end      
            % 基因111与000的表现型均为0 (增加休息的概率)
            x(x==7) = 0;
        end
        
        %加入夜班后休息约束
        for j = 1:6  %遍历一周七天
            for k = 1:16 %遍历16个排班人员
                if x(k,j,i)==6
                    x(k,j+1,i)=0;
                end
            end
        end
        
    end
 
    %每日排班需求,适应值y1_day计算:
      %硬约束y11:仅新人里面1个5、仅老人里面2个6
      %软约束y12:1、2、3、4均至少一个,2,4最佳情况为2个
    y1_day = zeros(pop,7);%每天的适应值
    y11_day = zeros(pop,7);
    y12_day = zeros(pop,7);
    for i=1:size(initial_pop,1)
        for j=1:7
            %硬约束y11,设置较高权重
            y11_day(i,j) = 1/( (sum(x(13:16,j,i)==5) - 1)^2 + ( sum(x(1:12,j,i)==6) - 2 )^2 + ( sum(x(13:16,j,i)==6) )^2 + ( sum(x(1:12,j,i)==5) )^2 + 1);  
            %软约束12,设置较低的权重
            y12_day(i,j) = 1/( ( sum(x(:,j,i)==1) - 1 )^2 + ( sum (x(:,j,i)==2) - 2 )^2 + ( sum (x(:,j,i)==3) -1 )^2 + ( sum (x(:,j,i)==4) - 2 )^2 + 1 );
            y1_day(i,j) = y11_day(i,j)*0.8 + y12_day(i,j)*0.2;
        end
    end
    
    %y1_day为每天的适应值(最大值为1),y1为每周的适应值(理论最大值为7)
    for i=1:size(initial_pop,1)
        y1(i,1) = sum(y1_day(i,:));
    end
    
    % 夜班后休息一天,适应值y2计算:
    % 软约束:夜班后至少休息一天,最佳休息两天
    y2_person = ones(pop,k);
    for i = 1:size(initial_pop,1) 
        for k = 1:16
            for j = 1:5  %遍历周一至周五的夜班
                if x(k,j,i) == 6 
                    % 若夜班为周一至周五后继续工作的惩罚函数
                    y2_person(i,k) = y2_person(i,k) - 1/5 *( x(k,j+2,i)~=0 );
                end
            end
        end
    end
    
    %y2_person为每人的适应值(最大值为1),y2为整个排班表的适应值(理论最大值为16)
    for i=1:size(initial_pop,1)
        y2(i,1) = sum( y2_person(pop,:) );
    end
    
    % y1很容易达到最大值,y2比较难,故给予较大权重
    y = 0.1*y1 + 0.9*y2;
    
    %找到种群中的最优基因
    [a,b] = max(y);
    
    fit1=y/sum(y); %?计算每个种群的适应度在总适应度里所占的比例
    fit2=cumsum(fit1); %?每个位置都是都是之前所有位置占比的累加(可以理解为在总长度为1的线段里面每个种群都占据了一段位置,适应度大的种群占据的位置长)
    
    %基因选择
    choose=sort(rand(pop,1)); %有序随机数序列
    k=1;
    i=1;
    while k<=pop
        if choose(k)<fit2(i) %随机数小于种群
            choosen_population(k,:)=initial_pop(i,:);
            k=k+1;
        else
            i=i+1;
        end
    end
    
    %基因交叉
    for i=1:2:pop-1
        if rand<crossover_probablity
            % 选择基因交叉长度
            crossover_length=round(rand*(length-1))+1;
            % 例:随机生成length长度为19,即第一行前19个与第二行后一个交叉生成新的子代,结果放在第一行
            crossover_population(i,:)=[choosen_population(i,1:crossover_length),choosen_population(i+1,crossover_length+1:end)];
            % 第二行前19个与第一行后一个交叉生成新的子代,结果放在第二行
            crossover_population(i+1,:)=[choosen_population(i+1,1:crossover_length),choosen_population(i,crossover_length+1:end)];
        end
    end
    
    %基因变异
    variation_population=crossover_population; 
    for i=1:pop
        if rand<variation_probablity
            variation_location=round(rand*(length-1))+1;
            variation_population(i,variation_location)=1-variation_population(i,variation_location);
        end
    end
    
    variation_population(end,:)=initial_pop(b,:); %?保留该次迭代中的最优种群
    initial_pop=variation_population; %经选择、交叉、变异后的种群作为下一代的初始种群,从而完成迭代
    best(m,1)=y(b); % 记录下第m代的最优函数值
    best(m,2) = y1(b);
    best(m,3) = y2(b);
    
end



plot(1:size(best,1),best(:,1)) 



总结

        代码注释很详细了,算法运行结果呈现大部分约束条件达到理想值,部分约束条件可能由于适应值函数设置的不恰当或者与其他约束条件有冲突导致无法达到最优,只能取一个尽量大的结果

评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值