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 or (flag != 1 and 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)
    if(len(L_box) > 0):
        print("IP求解平均装载率(总体积/总容积):", sum([sum(L_goods[i]) for i in range(len(L_goods))])/sum(L_box))

    # 结果检验,当求解器的结果有问题(商品数不符时)采用混合BFD求解方案
    if not goods_check(goods, L_goods):
        return BFD(boxs, goods)

    # gap较大时,用启发式方法比较,取优
    if gap >= 0.01:
        print("尝试采用启发式方法")
        L_box1, L_goods1 = BFD(boxs, goods)
        if (len(L_box) > 0):
            print("启发式方法求解平均装载率(总体积/总容积):", sum([sum(L_goods1[i]) for i in range(len(L_goods1))]) / sum(L_box1))
        if sum(L_box) > sum(L_box1) and goods_check(goods, L_goods1):
            print("采用启发式方法")
            L_box, L_goods = L_box1, L_goods1

    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 png_save(renWin, name):
    windowToImageFilter = vtkWindowToImageFilter()
    windowToImageFilter.SetInput(renWin)
    windowToImageFilter.Update()
    writer = vtkPNGWriter()
    writer.SetFileName(name)
    writer.SetInputConnection(windowToImageFilter.GetOutputPort())
    writer.Write()

#一维展示,输入为箱子集和商品集,包裹的箱子和商品集一一对应
def show_1d(L_box, L_goods):

    nums = len(L_box)
    h_max = max(L_box) if max(L_box) > 0 else 1

    #预设参数
    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)
    renWin.SetSize(1200,600)
    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()
    # 保存过程
    png_save(renWin, "result_D1.png")
    #展示
    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):
        for j in range(20):
            print("--------------------------", nums_good, "----------------------", i)
        boxs, goods = data_generate(nums_box, max_box, min_box, nums_good, max_good, min_good)
        goods = sorted(goods, reverse=True)
        t1 = time.clock()
        L_box1, L_goods1 = BFD(boxs, goods)
        t2 = time.clock()
        L_box2, L_goods2, gap = IP_1d(boxs, goods, time_limit)
        t3 = time.clock()
        if sum(L_box1) > 0 and goods_check(goods, L_goods1):
            rate_BFD.append(sum(goods) / sum(L_box1))
        else:
            rate_BFD.append(0)
        time_BFD.append(t2-t1)
        if sum(L_box2) > 0 and goods_check(goods, L_goods2):
            rate_IP.append(sum(goods) / sum(L_box2))
        else:
            rate_IP.append(0)
        time_IP.append(t3-t2)

    print("rate_BFD:", np.mean(rate_BFD))
    print("rate_IP:", np.mean(rate_IP))
    print("time_BFD:", np.mean(time_BFD))
    print("time_IP:", np.mean(time_IP))

    return np.mean(rate_BFD), np.mean(rate_IP), np.mean(time_BFD), np.mean(time_IP)

if __name__ == "__main__":

    # # 生成箱子集和商品集,计算并展示
    # boxs,goods = data_generate(nums_box = 2, max_box = 20, min_box = 10, nums_good = 10, 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 = []
    rate_IP = []
    time_BFD = []
    time_IP = []
    for i in range(30):
        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 = 100, times_experiment=100)
        rate_BFD.append(r1)
        rate_IP.append(r2)
        time_BFD.append(r3)
        time_IP.append(r4)
        print("-------------------------",i+1,"个商品测试完成")
        print(rate_BFD)
        print(rate_IP)
        print(time_BFD)
        print(time_IP)


 

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值