繁体   English   中英

如何使用python/PuLp建立线性规划model(运输问题)

[英]How to set up a linear programming model (transportation problem) using python/PuLp

我正在处理运输/补货 model,其中我需要以最低成本解决问题。 变量是:

  • 仓库 - 货物的几个可能的起点。
  • 项目 - 在这个例子中我只使用了两个项目。 每个 Item-Store 组合都有一个独特的需求值。
  • 库存 - 每个“仓库”中每个“项目”的可用库存
  • 商店 - 每批货物的目的地点。 在这个例子中,我只使用了两个商店。
  • 成本 - 每个仓库-物料-商店组合的独特成本,将用于解决最低成本问题。
  • 需求 - 每个“商店”想要接收的每个“项目”的数量; model 应履行 100%,除非没有库存。

我对 Python 不是很有经验。似乎我有点接近,但是,我有一个问题我还没有解决:如果库存太低无法满足所有需求,model 将中断并返回一个“不可行”的结果。 而不是这个,我希望 model 满足需求,直到库存达到零,然后返回到那个点的优化结果。 我知道我现在得到的结果是因为我在我的一个约束中将已完成的数量设置为等于需求,但我不确定如何修改/修复它。

这是到目前为止的代码 - 这是大量谷歌搜索的结果,有点像弗兰肯斯坦博士一样将代码的点点滴滴组合在一起 - 如果这里有任何东西看起来很愚蠢,请告诉我。 使用当前输入,这将不起作用,因为库存不能满足需求,但如果库存更高,它似乎可以工作(例如,将 Store1-SKU_B 需求从 250 更改为 50)

from pulp import *
import pandas as pd

# Creates a list of all the supply nodes 
warehouses = ["WHS_1","WHS_2","WHS_3"]

# Creates a dictionary for Inventory by Node-SKU
inventory = {"WHS_1": {"SKU_A":50,"SKU_B":100},
             "WHS_2": {"SKU_A":50,"SKU_B":75} , 
             "WHS_3": {"SKU_A":150,"SKU_B":25} ,
            }

# Store list
stores = ["Store1","Store2"]

# SKU list
items = ["SKU_A","SKU_B"]

# Creates a dictionary for the number of units of demand for each Store-SKU
demand = {
    "Store1": {"SKU_A":100,"SKU_B":250},
    "Store2": {"SKU_A":100,"SKU_B":50},
    }

# Creates a dictionary for the lane cost for each Node-Store-SKU
costs =  {
          "WHS_1": {"Store1": {"SKU_A":10.50,"SKU_B":3.75},
                 "Store2": {"SKU_A":15.01,"SKU_B":5.15}},
          "WHS_2": {"Store1": {"SKU_A":9.69,"SKU_B":3.45},
                 "Store2": {"SKU_A":17.50,"SKU_B":6.06}},
          "WHS_3": {"Store1": {"SKU_A":12.12,"SKU_B":5.15},
                 "Store2": {"SKU_A":16.16,"SKU_B":7.07}},
            }

# Creates the 'prob' variable to contain the problem data 
prob = LpProblem("StoreAllocation", LpMinimize)

# Creates a list of tuples containing all the possible routes for transport 
routes = [(w, s, i) for w in warehouses for s in stores for i in items]
 
# A dictionary called 'Vars' is created to contain the referenced variables(the routes) 
vars = LpVariable.dicts("Route", (warehouses, stores, items), 0, None, LpInteger) 
 
# The objective function is added to 'prob' first 
prob += (
    lpSum([vars[w][s][i] * costs[w][s][i] for (w, s, i) in routes]),
    "Sum_of_Transporting_Costs",
)

# Supply constraint, must not exceed Node Inventory
for w in warehouses:
    for i in items:
        prob += (
            lpSum([vars[w][s][i] for s in stores]) <= inventory[w][i],
            f"Sum_of_Products_out_of_Warehouse_{w}{i}",
        )

# Supply constraint, supply to equal demand
for s in stores:
    for i in items:
        prob += (
            lpSum([vars[w][s][i] for w in warehouses]) == demand[s][i],
            f"Sum_of_Products_into_Store{s}{i}",
        ) 

        
# The problem data is written to an .lp file
prob.writeLP("TestProblem.lp")

prob.solve()
# The status of the solution is printed to the screen 
print("Status:", LpStatus[prob.status])
# Each of the variables is printed with it's resolved optimum value 
for v in prob.variables():
    print(v.name, "=", v.varValue)
# The optimised objective function value is printed to the screen 
print("Total Cost of Fulfillment = ", value(prob.objective))  
 

这很好。 您的 model 已设置好。 让我们谈谈供应...

所以这是一个常见的转运 model 并且你想最小化成本,但默认答案是零成本不运送任何东西,这不好。 如您所知,您需要对交货施加上行压力以满足需求,或者至少在需求 > 库存的情况下尽最大努力处理现有库存。

第一件“便宜又容易”的事情是将每种产品的总交付量减少到可用的……所有商店。 在您当前的代码中,您试图强制交付 == 需求,这可能是不可能的。 因此,您可以退后一步,只说“提供总需求,或至少提供所有库存”。 在伪代码中类似于:

total_delivery[sku] = min(all inventory, demand)

您可以对其他 SKU 执行相同的操作,然后仅将 SKU 在所有仓库和目的地的所有交付相加并强制执行:

for SKU in SKUs:
  sum(deliver[w, s, sku] for w in warehouses for s in stores) >= total_delivery[sku]

意识到参数total_delivery不是一个变量,在做任何事情之前它可以从数据中辨别出来。

以上将使 model 运行,但存在问题。 model 可能会“超额投放”到某些站点,因为我们正在汇总需求。 因此,如果您有 100 个东西,并且将 50/50 的需求分成 2 个站点,它将向最便宜的站点提供 100 个……不好。 因此,您需要添加一个约束来限制对每个站点的交付需求,而不管来源如何。 就像是:

for s in stores:
  for sku in skus:
    sum(deliver[w, s, sku] for w in warehouses) <= demand[s, sku]

添加这些应该使您的 model 运行。 结果(如果库存不足)将不成比例地交付到廉价站点。 也许这没关系。 平衡它有点复杂。

...

关于您的 model,您将变量构造为嵌套列表......这就是为什么需要将其索引为vars[w][s][i]原因。 这很好,但我发现对变量进行元组索引要容易得多,而且你已经有了可以使用的基组routes 所以我会:

deliver = LpVariable.dicts("deliver", routes, 0, None, LpInteger)

然后你可以像我在上面的例子中那样索引它......

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM