【问题标题】:Mixed Integer Programming - Warehouse Location (Python + GLPK)混合整数编程 - 仓库位置(Python + GLPK)
【发布时间】:2015-01-08 20:19:27
【问题描述】:

我在优化方面相对较新,我正在尝试优化一个关于仓库位置的问题(来自 Coursera 的课程,2 年前)。问题是,它已经 6 个多小时了,它仍然在一个有 100 个仓库和 1000 个客户的实例上运行。

问题如下。我有一组可以打开或不打开的仓库。打开它们每个都有成本 s_w。此外,它们都有一个最大容量 cap_w。 另一方面,有一堆客户端,它们都必须连接到一个(并且只有一个)开放仓库。他们每个人都有一个需求 d_c,对于每个客户,每个仓库都有一个运输成本 t_wc。 我想要的,显然是最小化总成本。

所以,我有一个大小等于仓库总数的数组,称为 x。每个 x[w] 是一个整数 {0,1},定义仓库 w 是否打开。 我还有一个由 0 和 1 组成的矩阵,用于定义为每个客户提供哪个仓库。因此,行数与客户数相同,列数与仓库数相同。该矩阵称为 y。 y[c][w] 如果 waregouse w 交付客户 c,则为 1,否则为 0。

到目前为止一切顺利。这应该是一个 MIP 问题。 为了编写代码,我使用 PuPL lib(https://pythonhosted.org/PuLP/pulp.html) 和 GLPK 来解决它。

现在,这是我的模型:

#!/usr/bin/python
# -*- coding: utf-8 -*-

from pulp import *

def solveIt(inputData):

# parse the input
lines = inputData.split('\n')

parts = lines[0].split()
warehouseCount = int(parts[0])
customerCount = int(parts[1])

warehouses = []
for i in range(1, warehouseCount+1):
    line = lines[i]
    parts = line.split()
    warehouses.append((int(parts[0]), float(parts[1])))

customerDemands = []
customerCosts = []

lineIndex = warehouseCount+1
for i in range(0, customerCount):
    customerDemand = int(lines[lineIndex+2*i])
    customerCost = map(float, lines[lineIndex+2*i+1].split())
    customerDemands.append(customerDemand)
    customerCosts.append(customerCost)



x = [LpVariable("x"+str(w),0,1,cat='Integer') for w in range(0,warehouseCount)]
y = [[LpVariable("y"+str(c)+","+str(w),0,1,cat='Integer') for w in range(0,warehouseCount)] for c in range(0,customerCount)]

prob = LpProblem("Warehouse Location",LpMinimize)

#Constraints
# y_cw <= x_w makes sure that no client is delivered by a closed warehouse
for w in range(0,warehouseCount):
    for c in range(0,customerCount):
        prob += y[c][w] <= x[w]

#A client is served by exactly one warehouse
for c in range(0,customerCount):
    affineExpression = []
    for w in range(0,warehouseCount):
        affineExpression.append((y[c][w],1))
    prob += LpAffineExpression(affineExpression) == 1

#For each warehouse, the sum of demand of all the clients it serves is lower than its capacity
for w in range(0,warehouseCount):
    affineExpression = []
    for c in range(0,customerCount):
        affineExpression.append((y[c][w],customerDemands[c]))
    prob += LpAffineExpression(affineExpression) <= warehouses[w][0]

#Objective
#The sum of all the warehouses opening plus the transportation costs has to be minimal
affineExpression = []
for w in range(0,warehouseCount):
    affineExpression.append((x[w],warehouses[w][1]))
    for c in range(0,customerCount):
        affineExpression.append((y[c][w],customerCosts[c][w]))

prob += LpAffineExpression(affineExpression)

print "#######################START SOLVING"
status = prob.solve(GLPK(mip=1,msg = 1))
print LpStatus[status]
for w in range(0,warehouseCount):
    print value(x[w])

solution = []
for c in range(0,customerCount):
    string = ""
    whichOne = -1
    for w in range(0,warehouseCount):
        string += str(value(y[c][w])) + " "
        if value(y[c][w]) == 1:
            whichOne = w
            solution.append(w)
    print string+ "   "+str(whichOne)


# calculate the cost of the solution
obj = sum([warehouses[x][1]*x[w] for x in range(0,warehouseCount)])
for c in range(0, customerCount):
    obj += customerCosts[c][solution[c]]

# prepare the solution in the specified output format
outputData = str(obj) + ' ' + str(0) + '\n'
outputData += ' '.join(map(str, solution))

return outputData

我知道我构建矩阵的方式不是最优的,但它确实不会花费太长时间。 它开始解决,在某个时候我达到了 GLPK 所说的点:

OPTIMAL SOLUTION FOUND
Integer optimization begins...

我相信这意味着它解决了 LP,现在它得到整数......但它已经像 6 小时左右并且它一直在进步,并且仍然是,但没有完成。 在较小的情况下,它工作得很好。

我想我的问题是……我的模型有问题吗?我忘记了一些优化?或者这个问题有那么大吗?

另外,关于计算机,它的性能很差:Intel Atom 和 1GB 的 RAM...

感谢您的帮助!

编辑: 这是日期:https://github.com/ddeunagomez/DiscreteOptimization/blob/master/04_warehouse_location/warehouse/data/wl_100_1 格式为:

NumberOfWarehouses NumberOfCustomers
CapacityWarehouse1 OpeningCostWarehouse1
CapacityWarehouse2 OpeningCostWarehouse2
.....
CapacityWarehouseN OpeningCostWarehouseN
DemandCustomer1
TransportCostW1_C1 TransportCostW2_C1 ....... TransportCostWN_C1
DemandCustomer2
TransportCostW1_C2 TransportCostW2_C2 ....... TransportCostWN_C2
.....
DemandCustomerN
TransportCostW1_CM TransportCostW2_CM ....... TransportCostWN_CM

【问题讨论】:

  • 输入数据是什么?
  • 这实际上是一个大问题(就变量的数量而言),我怀疑 glpk 在这里是否有效。您可以尝试的一件事是选择一组仓库来打开,看看 glpk 需要多长时间才能找到客户到仓库的最佳分配。对于这种规模的问题,我也会尝试本地搜索。
  • 您好,感谢您的回答。实际上数据文件太大了,我不能在 Stackoverlow 上发布它......有没有办法在这里上传文件?谢谢
  • 哦,我也考虑过本地搜索...但我不确定该怎么做。尝试所有仓库组合(这里是 100 个!),并为每个尝试分配客户并在本地尝试改进每个客户或类似的东西?容量限制实际上是对我个人而言最困难的限制。再次感谢!!
  • 我见过的一个求解器使用满足的 MIP 约束的数量作为评分函数。通过选择一个约束来执行局部搜索,考虑该约束中的每个变量,并确定如果该变量被翻转,分数将如何变化。然后你做你的基本爬山——也许随机倒退以摆脱局部最大值等。或者也许使用 obj.函数 + c*作为评分函数满足的约束数。

标签: python optimization linear-programming glpk


【解决方案1】:

这在方案中并不是一个真正的大问题——有专门的代码来解决比这个更大的仓库位置实例,而且像 CPLEX 这样的现成解决方案也可以很容易地解决它。我不知道 GLPK/PuPL 的效率如何,但很可能是他们使用简单的 LP/分支绑定(这就是他们正在做的事情)花费了太长时间。

您可以尝试的一件事是允许 y 变量是连续的 (0

【讨论】:

  • 您好,感谢您的回答。那么,你会说这更像是求解器的错而不是模型的错吗?我只能尝试 GLPK...这是我安装的唯一一个...我尝试使 y variales 成为真实的,但它似乎没有改善,它已经工作了 2 个小时多一点,还没有结束然而。我也认为它应该让它更快,但实际上似乎并没有太大改善......谢谢!
  • 这个模型对我来说似乎是正确的(虽然我没有用细齿梳子检查它)。至于是否是求解器的“故障”,我想这是主观的——求解器没有做错任何事情,对于这样的求解器来说只是一个难题。如果你愿意接受好的但不是最优的解决方案,那么这个问题有很好的启发式方法。如果你愿意做一些编码,还有其他很好的精确算法。
猜你喜欢
  • 1970-01-01
  • 2017-02-02
  • 2018-02-13
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 2019-09-09
相关资源
最近更新 更多