使用 Pulp

问题描述

我必须在工作日安排商店,但当然,一条路线有容量,商店有不同的下降天数。

更明确地说,有一辆卡车每天都要搬家,但对于商店来说,有些卡车应该整整 6 天都停运,有些应该只停放两天,以此类推。

Key 代表店铺名称value 代表天数

Days = {
    "S1":6,"S2":6,"S3":6,"S4":3,"S5":3,"S6":3,"S7":2,"S8":2,"S9":2,"S10":1,"S11":1,"S12":1,"S13":1,"S14":1,"S15":1 
}

需求(Volume)是每家商店所有六天的纸箱总数。

我尝试将需求除以天数,以求出每天的大致纸箱数量。我不确定这是否是一个方法

Demand = {
    "S1":400/6,"S2":300/6,"S3":250/6,"S4":200/3,"S5":300/3,"S6":200/3,"S7":300/2,"S8":200/2,"S9":300/2,"S10":50/1,"S11":40/1,"S12":45/1,"S13":30/1,"S13":44/1,"S14":47/1,"S15":60/1,}

容量是所有商店的总纸箱容量,我像处理商店需求一样将需求除以可用的六天

Capacity = 2736/6

我想根据它们的数量在工作日均匀分布商店,所以我使用这个约束来实现它:

for d in no_days_list:
    prob += pulp.lpSum([Demand[s] * storeVars[d][s] for s in Store]) <= Capacity

不幸的是,这个约束有两个问题:

  • 如果商店需求除以天数后大于容量,我将得到一个非最佳解决方案 (-1) - (我正在寻找一个灵活的约束,如果它大约等于容量)
  • 如果商店需求远小于容量,我会得到一个最优解(1) 但它不会平均分配某些日子的商店数量数量比其他日子多 - (我想我必须添加更多约束来解决这种情况以尽可能均匀地分配它们)

如果我找到了第二点的解决方案,我可以取消这个限制。

拜托,我需要您的建议来增强代码

StoreSched = pd.DataFrame(columns = ["Store_Code","Route","Demand"])
Capacity = 2736/6

route="R1"
days_list=["SAT","SUN","MON","TUE","WED","THU"]
no_days_list = range(1,7)
Store = ["S1","S2","S3","S4","S5","S6","S7","S8","S9","S10","S11","S12","S13","S14","S15"]
Demand = {
    "S1":400/6,}

Days = {
    "S1":6,"S15":1,}
    
prob = LpProblem("store_schedule",LpMaximize)
storeVars = LpVariable.dicts("Days",(no_days_list,Store),1,LpInteger)
    
for d in no_days_list:
    prob += pulp.lpSum([Demand[s] * storeVars[d][s] for s in Store]) <= Capacity
for s in Store:
    # Every store should be assigned based on its DayNo.
    prob += pulp.lpSum(storeVars[d][s] for d in no_days_list) == Days[s]

prob.solve()
print(prob.solve())
for vi in prob.variables():
    if vi.varValue == 1:
        code= vi.name.split("_")[2];
        day = days_list[int(vi.name.split("_")[1])-1];
        if ((StoreSched['Store_Code'] == code).any() == False):
            StoreSched = StoreSched.append({'Store_Code': code,"Route":route,"Days":Days[code],"Demand":Demand[code]},ignore_index=True)
        for index in StoreSched.index:    
            if StoreSched.loc[index,'Store_Code']== code:                    
                StoreSched.loc[index,day] = 1                    
StoreSched.fillna(0,inplace=True)
StoreSched

解决方法

您可以尝试尽量减少一天的最大负载和一天的最小负载之间的差异。更改为 LpMinimize 问题:

prob = LpProblem("store_schedule",LpMinimize)

引入两个变量minLoadmaxLoad

minLoad = LpVariable("minLoad",Capacity,cat=LpContinuous)
maxLoad = LpVariable("maxLoad",cat=LpContinuous)

添加为目标:

prob += maxLoad - minLoad

添加约束以设置新变量:

for d in no_days_list:
    prob += minLoad <= pulp.lpSum([Demand[s] * storeVars[d][s] for s in Store])
    prob += maxLoad >= pulp.lpSum([Demand[s] * storeVars[d][s] for s in Store])

为了使问题可行,您需要增加 Capacity,例如:

Capacity = 2836/6

使用 CBC MILP 求解器的运行时间非常糟糕。您可以通过对 storeVars 的附加限制来改进:

for s in Store:
    if Days[s] == 2:
        for d in range(1,5):
            prob += pulp.lpSum(storeVars[d][s] + storeVars[d+1][s] + storeVars[d+2][s]) <= 1
    if Days[s] == 3:
        for d in range(1,6):
            prob += pulp.lpSum(storeVars[d][s] + storeVars[d+1][s]) <= 1

每天跑步给一个负荷:

d = 1,load = 465.0
d = 2,load = 464.0
d = 3,load = 455.33333333333337
d = 4,load = 458.33333333333337
d = 5,load = 441.6666666666667
d = 6,load = 451.6666666666667