赞
踩
作者简介:本人擅长运筹优化建模及算法设计,包括各类车辆路径问题、生产车间调度、二三维装箱问题,熟悉CPLEX和gurobi求解器
微信公众号:运筹优化与学习
如有运筹优化相关建模或代码定制需求,可通过微信公众号联系我们
之前的推文都是介绍车辆路径问题以及装箱问题,我们以这篇为引子,开始作业车间调度问题及其求解算法学习之路。本推文将展示遗传算法求解作业车间调度问题的求解思路以及python代码。
排产调度(scheduling)是生产制造业和服务业的重要决策对象,旨在目标明确的前提下,合理地将资源分配给任务。作业车间调度问题(Job-Shop Scheduling Problem,简称 JSSP)就是典型的排产调度优化问题,也正是这篇推文的主题。
作业车间调度问题可以描述为:一个车间中有 m m m台功能不同的机器(machine),机器集合用 I = { 1 , 2 , ⋯ , m } I=\left\{1,2,\cdots,m\right\} I={1,2,⋯,m}表示。有 n n n个工件(job)需要加工,工件集合用 J = { 1 , 2 , ⋯ , n } J=\left\{1,2,\cdots,n\right\} J={1,2,⋯,n}表示。每台机器不能同时处理两个及以上工件。每个工件需要以特定的顺序在特定的机器上进行加工,若工件 j j j需要在机器 m m m上加工,则称 o i j o_{ij} oij为一个作业(operation),作业集合用 O O O表示,每个作业 o i j o_{ij} oij的加工时长为 t i j t_{ij} tij。特定的加工顺序意味着作业之间存在前后依赖关系,若一个工件的前继作业没有完成,该工件的后续作业就不能开始,且作业一旦开始就不能中断。作业车间调度问题的目标就是制定出一个满足所有约束的生产方案,使得所需的完工时间(makespan, 即所有工件加工完成所需要的时间)最小。
参数及集合
I = { 1 , 2 , ⋯ , m } I=\left\{1,2,\cdots,m\right\} I={1,2,⋯,m}:机器集合
J = { 1 , 2 , ⋯ , n } J=\left\{1,2,\cdots,n\right\} J={1,2,⋯,n}:工件集合
O O O:作业集合
δ j \delta_j δj:工件 j j j的机器加工顺序
t i j t_{ij} tij:作业 o i j o_{ij} oij的加工时间
变量
C m a x C_{max} Cmax:最大完工时间
s i j s_{ij} sij:作业 o i j o_{ij} oij的开始时间
约束条件
采用基于工序编码全排列的编码方式进行编码,染色体的长度等于所有工件的工序之和。先将工序按照 1 , 2 , ⋯ , O 1,2,\cdots,O 1,2,⋯,O的顺序编码,其中O代表总的工序数。编码顺序代表了工序被加工的优先级。
初始种群生成方式采用将工序编码随机全排列的方式随机生成,代码如下:
def generateRandomIndividual(population, job_number, machine_number):
ind = list(range(job_number*machine_number))
random.shuffle(ind)
population.append([ind, None])
应用于作业车间调度问题方面,常见的遗传算法交叉算子包括单点交叉,多点交叉,均匀交叉,基于工件顺序的交叉和基于工件优先顺序的交叉等。
本文采用多点交叉方式,即随机产生两个交叉点位,交换两个点位之间的基因,代码如下:
def crossover(father, mother):
indexes = range(len(father[0]))
# 随机产生交叉点位
start_index = random.choice(indexes)
end_index = random.choice(indexes[start_index:])
father_gen = father[0][start_index:end_index]
fetus = removeFromList(mother[0], father_gen)
result = []
result.extend(fetus[:start_index])
result.extend(father_gen)
result.extend(fetus[start_index:])
return [result, None]
作业车间调度问题的遗传算法变异算子包括交换变异、插入变异和逆转变异等。
本文采用了交换变异的方式,即随机产生两个变异点,将两个变异点位的基因编码交换,以产生新的个体。
def mutation(population, mutation_rate):
if(random.random() > mutation_rate):
candidate = random.choice(population[1:])
swap_rnd(candidate[0])
candidate[1] = None
def swap_rnd(candidate):
id1 = random.choice(range(len(candidate)))
id2 = random.choice(range(len(candidate)))
tmp = candidate[id1]
candidate[id1] = candidate[id2]
candidate[id2] = tmp
return candidate
def genetic(times, machines, n, population_number, iterations, rate): machine_number = len(machines[0]) start_time = time.time() population = generate_population(population_number, n, machine_number) global_best_ind, global_best = sortAndGetBestIndividual(population, times, machines, n) for i in range(iterations): population = evolve(population, rate) best_ind, best_result = sortAndGetBestIndividual(population, times, machines, n) total_fitness, diffPercentage = getFitness(population) if(not global_best or best_result < global_best): global_best = best_result global_best_ind = copy.deepcopy(best_ind) printProgress(best_result, i, time.time() - start_time) checkDiversity(population, diffPercentage, n, machine_number) best_result, best_table = calculateMakespan(times, machines, global_best_ind[0], n) print("\nOVERALL RESULT") print("RESULT: %s" %best_result) # 目标函数值 print('the elapsed time:%ss'% (int(time.time() - start_time))) # 求解时间 print("Permutation: ") print(fromPermutation(global_best_ind[0], n)) # 输出最终解基因编码 printTable(best_table) # 输出结果 plotResult(best_table, best_result) # 绘制甘特图
第一行两个数分别代表工件数(n=10)和机器数(m=5)
每一行代表每个工件不同工序的加工时间,每个工序都用一对数字(x, y)来表示,其中x是处理该工序的机器编号,y是该工序的加工时间。
甘特图
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。