27、一维装箱
作者:互联网
from pyscipopt import Model, quicksum
from vtk import *
import vtk
import random as rd
import time
import numpy as np
#BFD(Best fit decreasing):区别于FFD,能装下则找一个合适的,合适的定义可以是装完后的那个箱子装载率最高
def BFD(boxs, goods):
L_box = []
L_goods = []
#箱子从大到小排列
boxs = sorted(boxs, reverse=True)
L_box.append(boxs[0])
L_box_unused = [boxs[0]]
L_goods.append([])
for g in goods:
flag = -1
for i in range(len(L_box_unused)):
if L_box_unused[i] >= g:
if flag == -1:
flag = i
elif L_box_unused[i] < L_box_unused[flag]:
flag = i
if flag == -1:
L_box.append(boxs[0])
L_box_unused.append(boxs[0] - g)
L_goods.append([g])
else:
L_box_unused[flag] -= g
L_goods[flag].append(g)
print([L_box, L_goods])
for i in range(len(L_goods)):
V = sum(L_goods[i])
for j in range(len(boxs)):
if boxs[len(boxs)-1-j] >= V and boxs[len(boxs)-1-j] < L_box[i]:
L_box[i] = boxs[len(boxs)-1-j]
print([L_box, L_goods])
break
return L_box, L_goods
#一维装箱整数规划
def IP_1d(boxs, goods, time_limit):
m = len(boxs)
n = len(goods)
model = Model("IP_1d")
#对X、Y,i为包裹下标,j为商品下标,x[i][j]=1代表第j件商品放进第i个包裹里,y[i]=1表示开启第i个包裹
X = [[model.addVar(vtype="B", name="x[%s,%s]" % (i, j)) for j in range(n)] for i in range(m*n)]
Y = [model.addVar(vtype="B", name="y[%s]" % i) for i in range(m*n)]
#包裹体积向量,[n个boxs[0],n个boxs[1],...,n个boxs[m-1]]
c = [boxs[i] for i in range(len(boxs)) for j in range(len(goods))]
#以总包裹体积最小(即填充率最大)为目标
model.setObjective(quicksum(Y[i]*c[i] for i in range(m*n)), "minimize")
#每个包裹内商品总体积不超过箱子体积
for i in range(m*n):
model.addCons(quicksum(goods[j]*X[i][j] for j in range(n)) - c[i]*Y[i] <= 0)
#每个商品都能被装下
for j in range(n):
model.addCons(quicksum(X[i][j] for i in range(m*n)) == 1)
#设置求解时间
model.setRealParam("limits/time", time_limit)
model.optimize()
print("\ngap:",model.getGap())
X1 = [[round(model.getVal(X[i][j])) for j in range(n)] for i in range(m*n)]
Y1 = [round(model.getVal(Y[i])) for i in range(m*n)]
print("\nX")
for i in range(len(X1)):
print(X1[i])
# print()
print("\nY")
print(Y1)
L_box = []
L_goods = []
for i in range(m*n):
goods_i = []
for j in range(n):
if X1[i][j] == 1:
goods_i.append(goods[j])
if len(goods_i) > 0:
L_box.append(c[i])
L_goods.append(goods_i)
return L_box, L_goods, model.getGap()
#检验是否每个商品都能有一个箱子放下它
def check(boxs, goods):
for good in goods:
can_put = False
for box in boxs:
if good <= box:
can_put = True
break
if not can_put:
print(good,"太大,无合适箱子")
return False
return True
#检验结果中的商品集是否和原始的商品集一致
def goods_check(goods, L_goods):
nums = 0
for gs in L_goods:
nums += len(gs)
if len(goods) == nums:
return True
return False
#任务分流汇总
#给一系列箱子和商品(箱子可用个数不限),推荐结果
def stacking_1d(boxs, goods, time_limit):
goods = sorted(goods, reverse=True)
#这里是否所有的商品均至少有一个箱子可以装下,若有商品超出规格则直接返回
if not check(boxs, goods):
return [[], []]
# 多类箱子,应用整数规划求解
L_box, L_goods, gap = IP_1d(boxs, goods, time_limit)
# gap较大时,用启发式方法比较,取优
if gap >= 0.01 and goods_check(goods, L_goods):
print("尝试采用启发式方法")
L_box1, L_goods1 = BFD(boxs, goods)
if sum(L_box) > sum(L_box1) and goods_check(goods, L_goods1):
print("采用启发式方法")
L_box, L_goods = L_box1, L_goods1
#结果检验,当求解器的结果有问题(商品数不符时)采用混合BFD求解方案
if not goods_check(goods, L_goods):
# BFD
L_box, L_goods = BFD(boxs, goods)
return L_box, L_goods
#添加商品图形
def Addcube_1d(ren, good, high, h_max, CL, CW, x_re, y_re, z_re):
cube = vtk.vtkCubeSource()
cube.SetXLength(CL)
cube.SetYLength(CW)
cube.SetZLength(good/h_max)
cube.Update()
translation = vtkTransform()
translation.Translate(CL/2.0 + x_re, CW/2.0 + y_re, (good/2.0+high)/h_max + z_re)
transformFilter = vtkTransformPolyDataFilter()
transformFilter.SetInputConnection(cube.GetOutputPort())
transformFilter.SetTransform(translation)
transformFilter.Update()
transformedMapper = vtkPolyDataMapper()
transformedMapper.SetInputConnection(transformFilter.GetOutputPort())
transformedActor = vtkActor()
transformedActor.SetMapper(transformedMapper)
transformedActor.GetProperty().SetColor((rd.uniform(0, 1), rd.uniform(0, 1), rd.uniform(0, 1)))
ren.AddActor(transformedActor)
#一维展示,输入为箱子集和商品集,包裹的箱子和商品集一一对应
def show_1d(L_box, L_goods):
nums = len(L_box)
h_max = max(L_box)
#预设参数
gap = 0.25/h_max
CL = 1/h_max
CW = 1/h_max
CL_p = 1.1*CL
CW_p = CW*nums + gap*(nums-1)
CH_p = 0.01/h_max
x_re = -0.5
y_re = -0.5
z_re = -0.5
#渲染及渲染窗口,并根据捕捉的鼠标事件执行相应的操作
ren = vtk.vtkRenderer()
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer(ren)
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
#添加轴,调整轴长、标签
# axes = vtkAxesActor()
# axes.SetTotalLength(1,1,1)
# axes.SetXAxisLabelText("Hi")
# ren.AddActor(axes)
"""画容器"""
for i in range(nums):
cube = vtk.vtkCubeSource()
cube.SetXLength(CL)
cube.SetYLength(CW)
cube.SetZLength(L_box[i]/h_max)
cube.Update()
translation = vtkTransform()
translation.Translate(CL/2.0 + x_re, CW/2.0 + CW * i + gap*i + y_re, L_box[i]/2/h_max + z_re)
transformFilter = vtkTransformPolyDataFilter()
transformFilter.SetInputConnection(cube.GetOutputPort())
transformFilter.SetTransform(translation)
transformFilter.Update()
transformedMapper = vtkPolyDataMapper()
transformedMapper.SetInputConnection(transformFilter.GetOutputPort())
transformedActor = vtkActor()
transformedActor.SetMapper(transformedMapper)
transformedActor.GetProperty().SetColor((1, 1, 1))
transformedActor.GetProperty().SetRepresentationToWireframe()
ren.AddActor(transformedActor)
"""画托盘"""
cube = vtk.vtkCubeSource()
cube.SetXLength(CL_p)
cube.SetYLength(CW_p)
cube.SetZLength(CH_p)
cube.Update()
translation = vtkTransform()
translation.Translate(CL_p/2.0 + x_re, CW_p/2.0 + y_re, -CH_p/2.0 + z_re)
transformFilter = vtkTransformPolyDataFilter()
transformFilter.SetInputConnection(cube.GetOutputPort())
transformFilter.SetTransform(translation)
transformFilter.Update()
transformedMapper = vtkPolyDataMapper()
transformedMapper.SetInputConnection(transformFilter.GetOutputPort())
transformedActor = vtkActor()
transformedActor.SetMapper(transformedMapper)
transformedActor.GetProperty().SetColor((0.2, 0.4, 0.8))
ren.AddActor(transformedActor)
for i in range(len(L_goods)):
high = 0
for j in range(len(L_goods[i])):
Addcube_1d(ren, L_goods[i][j], high, h_max, CL, CW, x_re, CW*i + gap*i + y_re, z_re)
high += L_goods[i][j]
camera = vtk.vtkCamera()
camera.SetPosition(5, -0.5, 2)
camera.SetViewUp(0, 0, 1)
ren.SetActiveCamera(camera)
iren.Initialize()
renWin.Render()
iren.Start()
#数据生成,输入为箱子种类数,箱子最大容积、最小容积、商品个数、商品最大体积、商品最小体积
#输出为生成的箱子列表和商品列表
def data_generate(nums_box, max_box, min_box, nums_good, max_good, min_good):
boxs = []
for i in range(nums_box):
boxs.append(int((max_box-min_box) * rd.random() + min_box))
boxs = list(set(boxs))
goods = []
for i in range(nums_good):
goods.append(int((max_good-min_good) * rd.random() + min_good))
return boxs,goods
# 数值实验, 输入为箱子种类数,箱子最大容积、最小容积、商品个数、商品最大体积、商品最小体积、时间限制、实验次数,
# 输出为BFD平均装载率、BFD平均时间、整数规划平均装载率、整数规划平均时间
def experiment(nums_box, max_box, min_box, nums_good, max_good, min_good, time_limit, times_experiment):
rate_BFD = []
time_BFD = []
rate_IP = []
time_IP = []
for i in range(times_experiment):
boxs, goods = data_generate(nums_box, max_box, min_box, nums_good, max_good, min_good)
t1 = time.clock()
L_box1 = BFD(boxs, goods)[0]
t2 = time.clock()
L_box2 = IP_1d(boxs, goods, time_limit)[0]
t3 = time.clock()
rate_BFD.append(sum(goods)/sum(L_box1))
time_BFD.append(t2-t1)
rate_IP.append(sum(goods)/sum(L_box2))
time_IP.append(t3-t2)
print("rate_BFD:", np.mean(rate_BFD))
print("time_BFD:", np.mean(time_BFD))
print("rate_IP:", np.mean(rate_IP))
print("time_IP:", np.mean(time_IP))
return np.mean(rate_BFD), np.mean(time_BFD), np.mean(rate_IP), np.mean(time_IP)
if __name__ == "__main__":
#生成箱子集和商品集,计算并展示
boxs,goods = data_generate(nums_box = 2, max_box = 20, min_box = 10, nums_good = 100, max_good = 10, min_good = 1)
L_box, L_goods = stacking_1d(boxs, goods, time_limit = 100)
print(L_box, L_goods)
show_1d(L_box, L_goods)
# #数值实验
# rate_BFD = []
# time_BFD = []
# rate_IP = []
# time_IP = []
# for i in range(20):
# r1,r2,r3,r4 = experiment(nums_box = 2, max_box = 20, min_box = 10, nums_good = i+1, max_good = 10, min_good = 1, time_limit = 10, times_experiment=100)
# rate_BFD.append(r1)
# time_BFD.append(r2)
# rate_IP.append(r3)
# time_IP.append(r4)
# print(rate_BFD)
# print(time_BFD)
# print(rate_IP)
# print(time_IP)
标签:box,27,BFD,good,goods,一维,time,装箱,boxs 来源: https://blog.csdn.net/chaoyuzhang/article/details/91049327