遗传算法求解车间调度问题(附python代码)

背景介绍

车间调度问题(Job Shop Scheduling Problem, JSSP)是一类经典的组合优化问题,它在制造业和生产管理中有着广泛的应用。JSSP 的目标是对车间中的一系列作业进行排程,以使得作业在不同机器上的加工顺序是最优的,达到某种特定的优化目标,比如最小化总的作业完成时间(即使得总完工时间尽可能短),或者最小化延迟时间、最大化产量等。车间调度问题通常被描述为一系列作业集合 J 和一系列机器集合 M,每个作业由一系列操作组成,每个操作需要在特定的机器上加工一定时间。作业调度的规则如下:
(1)每个作业的操作需要按照特定的顺序进行。
(2)同一时刻,一台机器只能加工一个操作。
(3)操作一旦开始,在完成之前不能中断。
(4)所有作业的开始加工时间不得早于零。
(5)同一时刻,一个工件只能在一台机器上进行加工

车间调度问题可以被更进一步分类,如:
(a)开放式车间调度(Open Shop Scheduling):作业的操作可以在任意顺序进行。
(b)流水式车间调度(Flow Shop Scheduling):所有作业的操作必须按照相同的顺序进行。
(c)柔性车间调度(Flexible Job Shop Scheduling):每个操作可以在多台不同但功能相似的机器上加工。

车间调度问题是 NP-难问题,这意味着没有已知的多项式时间的算法可以保证找到每个实例的最优解。因此,对于实际应用中的车间调度问题,通常会运用启发式算法和元启发式算法来寻求近似的最优解,比如遗传算法、粒子群优化、蚁群算法和禁忌搜索等。

车间调度问题的研究对于提高生产效率、降低成本、提高企业的市场竞争力等都具有重要意义。在实际应用中,调度方案通常还需要考虑各种现实限制,例如机器故障、交货期限、工人的工作时间等。因此,车间调度问题在研究和工业领域都是一个非常活跃和挑战性的领域。
在这里插入图片描述

问题简介

如下表所示为某车间调度问题数据,该问题工包含5个工件,每个工件4道工序,共有6台机器,其中工序1只能由机器1完成,工序2可由机器2或机器4完成,工序3可由机器3或者机器5完成,工序4只能由机器6完成,表中数据为各工件的各项工序在机器上加工所需的时间。
在这里插入图片描述
上述问题可以描述成一个简单的柔性车间调度问题,问题包含以下基本假设:
(1) 同一时刻同一台机器只能加工一个工件。
(2) 每个工件在某一时刻只能在一台机器上加工,不能中断操作。
(3) 同一工件有先后顺序之分,不同工件则没有。
(4) 不同的工件具有相同的优先级。
(5)T=0时刻是开始时刻,此时所有工件尚未开始加工。
(6)所有工件的所有工序都要完成。

问题的目标函数是最小化完工时间,我们需要通过算法手段决策工件的工序在哪台机器上执行以及机器的具体执行顺序,下图为上述问题工序的加工顺序图。
在这里插入图片描述

算法介绍

本文拟采用遗传算法对该问题进行求解,实际上只要定义了编码、解码以及适应度函数,基本所有启发算法都可以代入使用,方便起见,本文选择相对简单的遗传算法来对该问题进行求解,着重介绍下编码、解码、适应度的计算以及交叉和变异。方便起见,下文提到的所有索引都从0开始,机器0代表机器1,机器1代表机器2,以此类推。
编码方式
通常来说,遗传算法中每个个体代表一个完整的解决方案,对于车间调度问题而言,若要表达完整的解决方案,编码中最少应该包含每个机器的加工工件以及加工顺序两部分信息。简单起见,本文的编码方式如下,工包含两部分信息,其中order部分为各机器的加工顺序信息,machine为各工件每道工序选择的加工机器信息。

								{'order': [[2, 1, 4, 0, 3],[4, 3, 1, 0, 2],[0, 1, 4, 2, 3],[1, 2, 0, 4, 3],[2, 1, 4, 0, 3],[1, 2, 4, 0, 3]],'machine': [[0, 1, 4, 5],[0, 1, 4, 5],[0, 3, 4, 5],[0, 3, 4, 5],[0, 3, 2, 5]]}

例如其中[2, 1, 4, 0, 3]表示第一台机器的加工顺序为2->1->4->0->3,以此类推,后面的5个list分别为其余5台机器的加工顺序。看到这里可能会有疑问,前面不是提到了某些工序只需要在多台机器中的任意一台进行加工即可吗?order中的信息看起来所有工件都会在所有机器上进行加工?machine中的信息就是用于解决这个问题的,machine的长度为5,代表5个工件的使用机器情况,例如[0,1,4,5]表示工件0的四道工序分别在机器0、1、4、5上进行加工。

解码方式
以编码方式中提到的个体为例,由于工序0只有一台机器,因此机器0的加工顺序为[[2, 1, 4, 0, 3],工序2的可加工机器为1和3,从machine信息中可以看出来,使用机器1加工的是[0,1],使用机器3加工的是[2,3,4],再结合order中的顺序,可以得到每台机器上加工的工件及其加工顺序为:

			[[2, 1, 4, 0, 3], [1, 0], [4], [2, 4, 3], [2, 1, 0, 3], [1, 2, 4, 0, 3]]

以第一台机器为例,可以计算出每个工件的开始及结束加工时间如下,其中t1为开始加工时间,t2为结束加工时间。

							{2: {'t1': 0, 't2': 5},1: {'t1': 5, 't2': 14},4: {'t1': 14, 't2': 20},0: {'t1': 20, 't2': 27},3: {'t1': 27, 't2': 38}}

同理,可以获得其余每台机器上的加工时间,用如下所示的字典表示,其中第一个key为工序编号,第2个key为机器编号,第3个key为工件编号,例如time_table[0][0][2]={‘t1’: 0, ‘t2’: 5}表示第一道工序,工件2在机器0上进行加工,开始时间是0,结束时间是5。

								{0: {0: {2: {'t1': 0, 't2': 5},1: {'t1': 5, 't2': 14},4: {'t1': 14, 't2': 20},0: {'t1': 20, 't2': 27},3: {'t1': 27, 't2': 38}}},1: {1: {1: {'t1': 14, 't2': 20}, 0: {'t1': 27, 't2': 32}},3: {2: {'t1': 5, 't2': 11},4: {'t1': 20, 't2': 33},3: {'t1': 38, 't2': 48}}},2: {2: {4: {'t1': 33, 't2': 44}},4: {2: {'t1': 11, 't2': 20},1: {'t1': 20, 't2': 31},0: {'t1': 32, 't2': 39},3: {'t1': 48, 't2': 56}}},3: {5: {1: {'t1': 31, 't2': 37},2: {'t1': 37, 't2': 45},4: {'t1': 45, 't2': 54},0: {'t1': 54, 't2': 61},3: {'t1': 61, 't2': 71}}}}

适应度计算
在上一节解码过程中,我们首先根据order和machine的信息获得了每台机器上加工的工件以及具体的加工顺序,然后按照离散时间仿真的思路(略复杂)能够获得每个工件的在每道工序的具体加工时间以及结束时间及其对应的机器,那么最大完工时间只需要在上述time table取所有工件的最大t2即可,我们定义适应度为最大完工时间的倒数,这样适应度永远大于0,且适应度越大,最大完工时间越短。

种群交叉
交叉方式分为两部分,第一部分是order部分的交叉,第二部分是machine部分的交叉将父代和母代的加工顺序组合分配给子代,例如假设父代和母代的order信息如下:

									父代:[[2, 1, 4, 0, 3],[4, 3, 1, 0, 2],[0, 1, 4, 2, 3],[1, 2, 0, 4, 3],[2, 1, 4, 0, 3],[1, 2, 4, 0, 3]]母代:[[1, 2, 3, 0, 4],[3, 4, 2, 0, 1],[1, 0, 2, 4, 3],[2, 1, 3, 0, 4],[2, 1, 4, 3, 0],[3, 1, 4, 0, 2]]

相当于父代和母代总共有12个排列顺序,随机选择6个分配给孩子1,随机选择6个分配给孩子2,可以获得如下两个子代,第二部分是machine的交叉,过程与第一部分类似。

									子代1:[[2, 1, 4, 0, 3],[4, 3, 1, 0, 2],[0, 1, 4, 2, 3],[2, 1, 3, 0, 4],[2, 1, 4, 3, 0],[3, 1, 4, 0, 2]]子代2:[[1, 2, 3, 0, 4],[3, 4, 2, 0, 1],[1, 0, 2, 4, 3],[1, 2, 0, 4, 3],[2, 1, 4, 0, 3],[1, 2, 4, 0, 3]]

个体变异
变异也有两种情况,第一种是order部分的变异,一部分是machine部分的变异。首先并非所有个体都要参与变异,一般有一个变异参数,对于交叉生成的个体,如果生成的随机数小于这个参数则进行变异。order部分的变异首先生成一个随机数n,该随机数指定order要变异的次数,其次生成3n个随机数,每3个为一组,例如生成的随机数是1,2,3表示对机器1的加工顺序2和加工顺序3进行调换,例如n=1,且生成的随机数为0、0、1,那么子代1将变为:

									子代1:[[1, 2, 4, 0, 3],[4, 3, 1, 0, 2],[0, 1, 4, 2, 3],[2, 1, 3, 0, 4],[2, 1, 4, 3, 0],[3, 1, 4, 0, 2]]

接下来是machine的变异,machine的变异首先生成一个随机数m,表示machine部分进行变异的次数,其次生成2m个随机数,每2为一组,例如生成的随机数2,1,子代的machine从子代a变成子代b,工件2的工序1使用的机器由3变为1。

							子代a: [[0, 1, 4, 5],[0, 1, 4, 5],[0, 3, 4, 5],[0, 3, 4, 5],[0, 3, 2, 5]]子代b:[[0, 1, 4, 5],[0, 1, 4, 5],[0, 1, 4, 5],[0, 3, 4, 5],[0, 3, 2, 5]]

代码实现

数据处理

# 工件数量
workpiece_num=5# 工序数量
process_num=4# 机器数量
machine_num=6# 加工时间
process_time_dict={}
process_time_dict[0]=[7,5,9,9,7,7]
process_time_dict[1]=[9,6,8,8,11,6]
process_time_dict[2]=[5,8,6,6,9,8]
process_time_dict[3]=[11,12,12,10,8,10]
process_time_dict[4]=[6,9,11,13,9,9]# 工序对应的机器
process_machin_dict={0:[0],1:[1,3],2:[2,4],3:[5]}

种群交叉

'''
种群交叉
'''
def cross(father,mother):child1={}child2={}order1=[]order2=[]for i in range(machine_num):if randint(0,1):order1.append(father['order'][i])order2.append(mother['order'][i])else:order1.append(mother['order'][i])order2.append(father['order'][i])child1['order']=order1child2['order']=order2machine1=[]machine2=[]for j in range(workpiece_num):if randint(0,1):machine1.append(father['machine'][j])machine2.append(mother['machine'][j])else:machine1.append(mother['machine'][j])machine2.append(father['machine'][j])child1['machine']=machine1child2['machine']=machine2return child1,child2

个体变异

'''
个体变异
'''
def mutate(solution):solution_copy=deepcopy(solution)# order调整swap_num=randint(0,machine_num)for i in range(swap_num):idx=randint(0,machine_num-1)s1,s2=randint(0,workpiece_num-1),randint(0,workpiece_num-1)temp=solution_copy['order'][idx][s1]solution_copy['order'][idx][s1]=solution_copy['order'][idx][s2]solution_copy['order'][idx][s2]=temp# machine调整change_num=randint(0,workpiece_num)for j in range(change_num):idx=randint(0,process_num-1)solution_copy['machine'][j][idx]=random.choice(process_machin_dict[idx])if get_fitness(solution_copy)>get_fitness(solution):return solution_copyreturn solution

可视化

'''
绘制甘特图
'''
def plot_gantt(solution):# 给定的数据time_table,machine_order=resolve_solution(deepcopy(solution))data={}for k in time_table:for i in time_table[k]:data[i]=time_table[k][i]# 创建颜色绘图colors = list(mcolors.TABLEAU_COLORS.keys())  # 预定义的颜色color_map = {}fig, ax = plt.subplots(figsize=[20,10])for machine, jobs in data.items():for job, times in jobs.items():start_time = times['t1']end_time = times['t2']duration = end_time - start_time# 检查工件是否已经分配了颜色if job not in color_map:color_map[job] = colors.pop(0)# 绘制甘特图的条形块ax.broken_barh([(start_time, duration)], (machine - 0.4, 0.8), facecolors=color_map[job])# 设置y轴ax.set_yticks(range(len(data)))ax.set_yticklabels(['Machine ' + str(i) for i in range(machine_num)])# 设置x轴ax.set_xlabel('Time')ax.set_xlim(0, max(time['t2'] for machine in data.values() for time in machine.values()))# 绘制图例patches = [plt.Rectangle((0,0),1,1, color=mcolors.TABLEAU_COLORS[color]) for color in color_map.values()]labels = [f'Workpiece {i}' for i in color_map.keys()]ax.legend(patches, labels, loc='upper right')# 在条形上添加任务编号for machine, jobs in data.items():for job, times in jobs.items():start_time = times['t1']duration = times['t2'] - times['t1']ax.text(x=start_time + duration/2, y=machine, s=f'{job}', color='white', va='center', ha='center',bbox=dict(boxstyle='round,pad=0.1', facecolor=color_map[job], edgecolor='none'))# 显示网格ax.grid(False)# 展示图表plt.show()

结果展示

收敛过程
在这里插入图片描述

甘特图
在这里插入图片描述

本文小结

本文对车间调度问题进行了简单介绍,并以一个简单柔性车间调度问题为例,使用遗传算法对该问题进行了求解,并且详细描述了使用遗传算法求解过程中的编码、解码、交叉以及变异等操作。另外,本文给出了遗传算法求解车间调度问题的主体python代码,最后展示了算法求解的结果,包括结果收敛过程以及甘特图,验证了算法的有效性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/349444.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么是场外期权?场外期权有几种做法?

今天带你了解什么是场外期权?场外期权有几种做法?期权分为场内期权,场外期权。场内期权我们都知道,是在期货盘里购买的期权,但场外期权呢? 什么是场外期权? 场外期权是一种在交易所之外进行交易…

WinForm之TCP服务端

目录 一 原型 二 源码 一 原型 二 源码 using System.Net; using System.Net.Sockets; using System.Text;namespace TCP网络服务端通讯 {public partial class Form1 : Form{public Form1(){InitializeComponent();}TcpListener listener null;TcpClient handler null;Ne…

【Linux】从零开始配置新的服务器的机器学习环境

终端远程登录 ssh -p [端口号] [服务器用户名][服务器IP]或者 ssh [用户名][主机地址]第二种的前提是在.ssh\config中配置了host 安装文本编辑器vim 主要用于后续的文本编辑,个人比较习惯用vim,根据自己喜好选择 更新apt sudo apt update安装文本编辑…

陪诊小程序开发,陪诊师在线接单

近几年,陪诊师成为了一个新兴行业,在科技时代中,陪诊小程序作为互联网下的产物,为陪诊市场带来了更多的便利。 当下生活压力大,老龄化逐渐严重,年轻人很难做到陪同家属看病。此外,就诊中出现了…

【npm】安装报错(大多是版本冲突)

1.eslint版本太高,Vuex报错 我使用的是vue2.x,npm 安装Vuex3版本,控制台报错说eslint版本太高 解决办法:Vue2项目安装 vuex3 出现版本报错解决方法_安装 vuex3版本 eslint 报错-CSDN博客

在Lua解释器中注册自定义函数库

本文目录 1、引言2、注册原理3、实例4、程序验证 文章对应视频教程: 暂无,可以关注我的B站账号等待更新。 点击图片或链接访问我的B站主页~~~ 1、引言 在现代软件开发中,Lua因其轻量级、高效和可嵌入性而被广泛使用。作为一种灵活的脚本语言…

MyBatis使用 PageHelper 分页查询插件的详细配置

1. MyBatis使用 PageHelper 分页查询插件的详细配置 文章目录 1. MyBatis使用 PageHelper 分页查询插件的详细配置2. 准备工作3. 使用传统的 limit 关键字进行分页4. PageHelper 插件(配置步骤)4.1 第一步:引入依赖4.2 第二步:在m…

【SpringBoot整合系列】SpringBoot整合kinfe4j

目录 kinfe4j与Swagger的区别 SpringBoot2.x整合kinfe4j1.添加依赖2.启动类注解3.创建Knife4J配置类4.实体类5.接口admin访问 api访问 常用注解汇总SpringBoot3.x整合Kinfe4j启动报错解决1.更换依赖2.启动类3.配置4.配置类5.参数实体类6.接口admin访问 api访问 各版本注解参照 …

视频美颜工具技术探秘:直播美颜SDK的应用与发展

今天,笔者将深入探讨直播美颜SDK的应用场景和发展趋势,揭示其背后的技术奥秘和潜力。 一、直播美颜SDK的基本原理 直播美颜SDK其基本原理包括以下几个方面: 人脸检测与特征定位 肤色分析与调整 瑕疵修复与细节增强 滤镜和特效应用 二、…

【算法】Graham 凸包扫描算法 ( 凸包概念 | 常用的凸包算法 | 角排序 | 叉积 | Python 代码示例 )

文章目录 一、Graham 凸包扫描算法1、凸包概念2、常用的凸包算法3、Graham 凸包扫描算法 二、Graham 算法前置知识点1、角排序2、叉积3、算法过程分析 三、代码示例1、完整代码示例2、执行结果 使用 Graham 算法绘制的凸包效果 : 博客代码下载 : https://download.csdn.net/d…

算法刷题【二分法】

题目: 注意题目中说明了数据时非递减的,那么这样就存在二分性,能够实现logn的复杂度。二分法每次只能取寻找特定的某一个值,所以我们要分别求左端点和有端点。 分析第一组用例得到结果如下: 成功找到左端点8 由此可知&#xff0…

Spring Boot集成 Spring Retry 实现容错重试机制并附源码

😄 19年之后由于某些原因断更了三年,23年重新扬帆起航,推出更多优质博文,希望大家多多支持~ 🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志 🎐 个人CSND主页——Mi…

verilog阻塞和非阻塞语法

阻塞和非阻塞是FPGA硬件编程中需要了解的一个概念,绝大部分时候,因为非阻塞的方式更加符合时序逻辑设计的思想,有利于时钟和信号的同步,更加有利于时序收敛,所以除非特殊情况,尽量采用非阻塞方式。 1,非阻塞代码 非阻塞赋值,A和B是同时被赋值的,具体是说在时钟的上升…

论文阅读:H-ViT,一种用于医学图像配准的层级化ViT

来自CVPR的一篇文章,https://openaccess.thecvf.com/content/CVPR2024/papers/Ghahremani_H-ViT_A_Hierarchical_Vision_Transformer_for_Deformable_Image_Registration_CVPR_2024_paper.pdf 用CNNTransformer混合模型做图像配准。可变形图像配准是一种在相同视场…

基于单片机的机械手臂控制系统设计

摘 要: 应用单片机 、 Arduino 及机械臂的有关知识,设计一款基于单片机的六自由度机械手臂,并详述其控制系统的软、 硬件设计 。 该机械手臂能够模仿人的上肢完成简单的动作,因此在实验教学演示平台 、 生产或生活中都极具应用价…

Dubbo 3.x源码(20)—Dubbo服务引用源码(3)

基于Dubbo 3.1,详细介绍了Dubbo服务的发布与引用的源码。 此前我们学习了调用createProxy方法,根据服务引用参数map创建服务接口代理引用对象的整体流程,我们知道会调用createInvokerForRemote方法创建远程引用Invoker,这是Dubbo …

Linux文件系统

目录 1.磁盘的结构 1.1磁盘的物理结构 1.2 磁盘的存储结构 1.3 磁盘的逻辑结构 2.文件系统 在上一篇文章基础IO中,我们主要是讲了被打开的文件与进程的关系,以及操作系统是如何管理这些被打开的文件的,但是磁盘有这么多文件,被打…

QT--DAY1

不使用图形化界面实现一个登陆界面 #include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent) {//设置窗口标题this->setWindowTitle("登录界面");//设置窗口大小this->resize(535,410);//固定窗口大小this->setFixedSize(535,410)…

windows 环境下使用git命令导出差异化文件及目录

一、找出差异化的版本(再此使用idea的show history) 找到两个提交记录的id 分别为: 二、使用git bash执行命令(主要使用 tar命令压缩文件) 输出结果:

上心师傅的思路分享(三)--Nacos渗透

目录 1. 前言 2. Nacos 2.1 Nacos介绍 2.2 鹰图语法 2.3 fofa语法 2.3 漏洞列表 未授权API接口漏洞 3 环境搭建 3.1 方式一: 3.2 方式二: 3.3 访问方式 4. 工具监测 5. 漏洞复现 5.1 弱口令 5.2 未授权接口 5.3.1 用户信息 API 5.3.2 集群信息 API 5.3.3 配置…