簡體   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