具有附加约束的运输问题

问题描述

我正在尝试使用 PuLP 包来实现运输问题。在我的情况下,它略有修改并有额外的限制。 假设有 7 个供应商 [s1,s2,s3,s4,s5,s6,s7] 和 6 个目的地 [d1,d2,d3,d4,d5,d6]。 每个目的地的需求为 1。每个供应商的供应为 3。

到目前为止的实现:

capacity=3
suppliers,targets = cost.shape
    objective = LpProblem("Optimize_allocation",LpMinimize)
    x = LpVariable.dicts("assignment",product(range(suppliers),range(targets)),1)
    objective += lpSum(cost[i,j] * x[i,j] for i in range(suppliers) for j in range(targets))
    
    for j in range(targets):
        condition1 = lpSum( x[i,j] for i in range(suppliers)) == 1
        objective+= condition1
    for i in range(suppliers):
        condition2 = lpSum(x[i,j] for j in range(targets)) <= capacity
        objective+= condition2  
    
    objective.solve(PULP_CBC_CMD(msg=0))

我的其他限制是,目的地 (d3,d4) 只接受来自一个供应商(可以是其中任何一个)的货物,并且该供应商应该只为它们提供服务,而不能向任何其他目的地提供服务。 如果我将它们组合到一个具有需求 2 的目的地并平均成本可行,但我如何确保约束的后期部分?这只是一个示例,但在我的实际问题中,可能有多个目的地相互分组并要求相同的约束示例:(d1,d2) 和 (d5,d6,d3)。

编辑: 根据@kabdulla 的回答更新为以下工作代码,但更改了最后一个约束。

suppliers = 10
targets = ['A','B','C','D','E','F','G','H','I']
grps = [['A','C'],['D'],['E'],['F','G'],['H','I']] #constraint groups
groups = [[0,1,2],[3],[4],[5,6],[7,8]] #constraint group ids
capacities = [3,2,2]
capacity = 3
cost = np.random.rand(suppliers,len(targets))
objective = LpProblem("Optimize_allocation",LpMinimize)
x = LpVariable.dicts("assignment1",range(len(targets))),'Integer')
y = LpVariable.dicts("assignment2",range(len(groups))),'Integer')
objective += lpSum(cost[i,j] for i in range(suppliers) for j in range(len(targets)))

    
for j in range(len(targets)):
    objective+= lpSum( x[i,j] for i in range(suppliers)) == 1
     
    
for i in range(suppliers):
    objective+= lpSum(x[i,j] for j in range(len(targets))) <= capacity
     

for i in range(suppliers):
    for k in range(len(groups)):
        objective += lpSum(x[i,j] for j in groups[k]) <= y[i,k]*capacities[k]
        
    
for k in range(len(groups)):
    objective += lpSum(y[i,k] for i in range(suppliers)) == 1


for i in range(suppliers):
    objective+= lpSum(y[i,j] for j in range(len(groups))) <= 1

objective.solve()

解决方法

首先观察 - 您的 x[i,j] 变量(从供应商 i 到目标 j 的供应数量)是连续的 - 表明可以运输一小部分单位。如果这不是本意,则需要将这些 x 设为整数(cat='Integer' 调用 LpVariable.dicts())。

如果我正确理解您的附加约束,则存在一些目标节点的“独家供应”集 E_k。在其中一组中:

  • 所有供应都必须来自单个供应商节点
  • 该供应商不得向集合之外的任何节点提供

要强制执行这种逻辑,您将需要一些二进制变量(如果您的 x 变量是非连续的并且限制在 [0,1] 范围内(即它们是二进制的),您可能能够直接使用它们)。我假设您的 x 是连续的。

解决此问题的一种方法是引入新的二元变量 y[i,k],其中 1 个当条件是供应商 i 是选择的排他供应商以设置 k

假设您还声明了属于同一排他供应集 E 的目标节点的列表列表,您可以添加如下所需的约束:

for i in suppliers:
    for k in range(len(E)):
        objective += lpSum(x[i,j] for j in E[k]) <= y[i,k]*M[k]

其中 M[k] 足够大以至于当 y[i,k] 为 1 时它们不会添加额外的约束(例如,这可能只是第 k 个独占节点中节点的需求总和供应集。

然后您可以对 y[i,k] 变量强制执行所需的约束 - 即每个集合 k 必须只有一个供应商:

for k in range(len(E)):
    objective += lpSum(y[i,k] for i in suppliers) == 1

最后,您需要添加约束条件,即如果供应商 i 服务于排他集 k(即 y[i,k] == 1),则不允许他们提供不在该集合中的任何节点:

for i in suppliers:
    for k in range(len(E)):
        objective += lpSum(x[i,j] for j in targets if j not in E[k]) <= \
                       (1 - y[i,k])*N[k]

其中 N[k] 足够大以至于当 y[i,j] == 0 时它们不会添加额外的约束(例如可能是不在第 k 个排他供应集中的节点的需求总和。