2019年第九届MathorCup高校数学建模挑战赛
C题 汽配件制造业中的生产排程问题
原题再现:
整体求解过程概述(摘要)
随着市场竞争日趋激烈,企业开始更加注重低费高效,因此生产排程问题成为众多制造企业关注的热点之一。其中,制造行业的喷漆生产排程问题是长久以来的业界难题,本文欲解决之。
针对问题一,我们以最小化换色次数以及最小化未满足产品需求零件个数为目标,引入满足支架数量限制、滑橇数量限制、面漆换色限制、颜色前后摆放限制等约束,并将各变量、约束之间的隐形限制条件也引入约束中,建立了双目标非线性整数规划模型。在算法设计中,首先设计基于规则以及颜色优先级的基本算法求得初始解,得出平均每圈的换色次数为 4,未满足生产需求的零件个数为 71。接着基于此运用遗传算法求出一组基于初始解的双目标帕累托最优解。我们以较为平衡的一组为例展示,其平均每圈的换色次数(考虑了不同圈首尾衔接的换色情况)为 3.5,未满足生产需求的零件个数为40。
针对问题二,我们在问题一已有优化目标的基础上引入最小化更换滑撬次数这一目标。为了调节各目标关系,首先引入不同圈之间产品喷涂结构相似度指标,根据问题一的解,分析发现各圈产品之间喷涂结构相似度差异不大且大多在 50%左右,同时结合生产需求和换色次数在企业制造的重要性,将支架更换次数置于较低优先级。算法设计分为两部分,首先在尽量保证问题一最优解的情况下,以相似度和首尾颜色的可衔接性作为关键因素调整各圈之间的生产顺序。接着以调整后的第一圈为基准,依序调整此后各圈的产品喷涂结构,使之与其前一圈结构尽量相似。求得平均每圈的换色次数小幅增长为 3.6 以及未满足生产需求的零件个数不变为 40,平均每圈支架更换次数由 275 优化至180。观察结果发现,支架更换次数优化的情况受限于各圈生产顺序的选取策略,因此对于该策略的改进可作为后续研究的方向。
模型假设:
假设 1:不考虑设备的故障问题
假设 2:人工更换滑橇只是人力方面的影响,不对具体生产流程产生影响。
假设 3:企业不会因为更换次数频繁而增加对人员的雇佣
问题分析(部分):
支架的更换与不同圈之间的产品结构的情况息息相关,因此我们首先定义产品喷涂结构相似度。对与不同圈之间的产品喷涂相似性,我们以其共同生产的产品种类数为标准。具体表达如下:
第m圈和第n圈的相似度为他们相同的产品种类数与他们总共生产的产品种类数之比。越接近 1 则说明他们喷涂的产品结构越类似,进行适当的圈内调整之后,对更换支架次数的优化情况也会较好
模型的建立与求解整体论文缩略图
全部论文请见下方“ 只会建模 QQ名片” 点击QQ名片即可
程序代码:(代码和文档not free)
The actual procedure is shown in the screenshot
import numpy as np
import xlwt
import math
import copy
import xlrd
'''商品是商品大类根据颜色的细分''' #商品类,原始的不同类型不同颜色的商品为一个对象
class com:
name = None
color = None
num = 0
sled_num = 0 #商品需要的总的滑橇数
circle_num = 0 #商品需要生产的圈数
#如果圈数大于 1,假设生产圈数为 n,则保证前 n-1 生产量为 6 的倍数
pro_num = 0
def __init__(self, tname, tcolor,tnum):
self.name = tname
self.color = tcolor
self.num = tnum
#商品段类,排产圈中每个不同类型不同颜色的商品段为一个对象
class com_seg:
name = None
color = None
position = 0
num = 0
sled_num = 0
circle_balance = 0 #在加入该商品段前本圈的 balance
def __init__(self,tname,tcolor,tposition,tnum,sled_num,balance):
self.name = tname
self.color = tcolor
self.position = tposition
self.num = tnum
self.sled_num = sled_num
self.circle_balance = balance
com_info_list = [] #保存各个商品大类信息的列表
#示例:[name,color,num]
com_list = [] #保存所有商品对象
bracket_num = {} #支架数目字典,由商品大类名字为键
com_seg_list = [] #保存所有的商品段对象,由排产时生成
bracket_num_copy = {} #支架数目字典副本,排产时使用
''' 颜色序列与各颜色对应的数目
4068 曜岩黑 3854 极地白 1746 钻石白 1100 光耀蓝 935 宝石红 704 铱
银
551 米兰银 429 宝石蓝 43 宇宙黑 15 牛仔蓝
''' #颜色序列与颜色商品字典
color_series = ['曜岩黑','极地白','钻石白','光耀蓝','宝石红',
'铱银' ,'米兰银','宝石蓝', '宇宙黑','牛仔蓝']
color_com_dict = {'曜岩黑':[],'极地白':[],'钻石白':[],'光耀蓝':[],'宝石红':[],
'铱银':[],'米兰银':[],'宝石蓝':[], '宇宙黑':[],'牛仔蓝':[]}
def read_com_list(com_info_list):
workbook = xlrd.open_workbook("C 题附件.xlsx")
sheet1 = workbook.sheet_by_name("生产需求量表")
for i in range(1,84):
one_com_info = []
for j in range(3):
one_cell = sheet1.cell(i,j).value
one_com_info.append(one_cell)
#print(one_com_info)
com_info_list.append(one_com_info)
def read_brackrt_info(bracket_num):
workbook = xlrd.open_workbook("C 题附件.xlsx")
sheet1 = workbook.sheet_by_name("支架数量上限")
for i in range(1, 32):
one_cell = sheet1.cell(i,0).value
two_cell = sheet1.cell(i,1).value
bracket_num[one_cell] = int(two_cell)
#根据商品信息列表,生成商品对象列表
def cre_com_list(com_info_list,com_list):
for one_com_info in com_info_list:
temp = com(one_com_info[0],one_com_info[1],int(one_com_info[2]))
com_list.append(temp)
'''按照尽量多生产原则,计算各个商品需要的总滑撬数''' def cacu_sled(com_list):
for com in com_list:
if bracket_num[com.name] >= 6:
sleds = com.num // 6
if com.num % 6 > 0:
sleds += 1
com.sled_num = sleds
else:
sleds = com.num // bracket_num[com.name]
com.sled_num = sleds
#根据 com_list 中的商品对象,计算各个商品对象要排产的圈数
def cacu_circle(com_list):
for com in com_list:
circle_num = math.ceil(com.num / bracket_num[com.name])
if circle_num > 1:
if bracket_num[com.name] > 6:
com.pro_num = (bracket_num[com.name] // 6) * 6
com.circle_num = math.ceil(com.num / com.pro_num)
else:
com.pro_num = bracket_num[com.name]
com.circle_num = math.ceil(com.num /
bracket_num[com.name])
else:
com.circle_num = 1
#根据各个商品的颜色,生成以颜色划分的商品字典
def cre_color_dict(com_list):
for com in com_list:
color_com_dict[com.color].append(com)
#生成一个排产列表
pre_series = []
def cre_one_series(pre_series,color_series,bracket_num):
'''初始化总的滑橇数目,商品排产位置,要维护的每一圈滑橇数目,支架数目字典''' balance = 2250 #总的滑橇数目
position = 1 #记录商品排产的位置
one_balance = 303 #要维护的每一圈滑橇数目
bracket_num_copy = copy.deepcopy(bracket_num) #排产过程中用到的支架
数目字典
while(balance > 0):
for color in color_series:
for com in color_com_dict[color]:
#还剩一圈的产品与多余一圈的产品之间的差别是排产的数量
与排产的滑橇数量
#print(com.name,com.color,com.circle_num)
#print(balance)
if balance < 0:
return pre_series
if com.circle_num > 1:
once_sled = com.pro_num // 6
if once_sled <= 0:
once_sled += 1
append_num = com.pro_num
#print(com.name,com.color,com.circle_num,com.pro_num)
#print("append_num",append_num)
elif com.circle_num > 0:
once_sled = com.sled_num
append_num = com.num
#print(com.name, com.color, com.circle_num, com.num)
#print("append_num", append_num)
else:
continue
#判断是否加入本商品后就超出了本圈,若是则对支架数
字典
#滑橇数目的更新方式不同,如果加入后超出了本圈则
#print(one_balance)
#print(once_sled)
if one_balance - once_sled <= 0:
#print('是末尾')
#print(one_balance,once_sled)
# 这里要改,应该改成>,把下面的放在 else
#print('是末尾')
if bracket_num_copy[com.name] >= once_sled * 6:
a = com_seg(com.name, com.color, position, append_num,once_sled,one_balance)
com.num -= append_num #无论是否在末尾,
对该商品的更新方式相同
com.circle_num -= 1 #维护余下需要的圈数
com.sled_num -= once_sled #维护余下需要的滑橇
数
position += 1 #无论是否在末尾,对
位置的更新相同
pre_series.append(a)
#在末尾对滑橇数和支架数的更新不同
ex_sled = once_sled - one_balance
ex_bracket = bracket_num_copy[com.name] - one_balance * 6
bracket_num_copy = copy.deepcopy(bracket_num)
bracket_num_copy[com.name] -= ex_bracket
one_balance = 303 - ex_sled
balance -= once_sled
else:
continue
else:
#print('append_num',append_num)
#print(bracket_num_copy[com.name])
#print(one_balance, once_sled)
#print(com.name)
if append_num <= bracket_num_copy[com.name]:
#print('不是末尾')
a =
com_seg(com.name,com.color,position,append_num,once_sled,one_balance)
com.num -= append_num
com.circle_num -= 1
com.sled_num -= once_sled
position += 1
pre_series.append(a)
balance -= once_sled
one_balance -= once_sled
bracket_num_copy[com.name] -= append_num
#print(balance)
if balance < 0:
print(com.name,'balance < 0 ')
else:
continue
#pre_series.append("换色")
#one_balance -= 1
#balance -= 1
#print(balance)
read_com_list(com_info_list) #读入商品数据
cre_com_list(com_info_list,com_list) #创建商品列表
read_brackrt_info(bracket_num) #创建支架词典
cacu_sled(com_list) #按照多生产原则计算需要的滑橇
数量
cacu_circle(com_list) #计算各商品需要的生产圈数
cre_color_dict(com_list)
#for com in com_list:
#
print(com.name,com.color,com.sled_num,com.circle_num,com.num,com.pro_num)
pre_series = cre_one_series(pre_series,color_series,bracket_num)
count = 0
all = 0
for com_seg in pre_series:
if com_seg == '换色':
count += 1
print('换色')
else:
print(com_seg.name,com_seg.color,' 商 品 数 ',com_seg.num,' 滑 橇 数
',com_seg.sled_num,'加入之前本圈剩余滑橇数',com_seg.circle_balance)
all += com_seg.sled_num
def write_solution(pre_series):
f = xlwt.Workbook(encoding='utf-8')
sheet1 = f.add_sheet(u'sheet1', cell_overwrite_ok=True) # 创建 sheet
# 将数据写入第 i 行,第 j 列
i = 0
for com_seg in pre_series:
sheet1.write(i, 1, com_seg.name)
sheet1.write(i, 2, com_seg.color)
sheet1.write(i, 3, com_seg.num)
sheet1.write(i, 4, com_seg.sled_num)
sheet1.write(i, 5, com_seg.circle_balance)
i = i + 1
f.save('初始解.xls') # 保存文件