使用 Pulp 在工作日平均分配数量(需求)

Distribute volume (demands) equally cross the weekdays with 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 ,"S13":1,
    "S14":1, "S15":1 
}

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

我试着用需求量除以天数,求出大概每天的纸箱数量。我不确定这是否是一个好方法。

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

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

如果我找到第二点的解决方案,我可以删除此约束。

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

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, "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,
}

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, "S13":1,
    "S14":1, "S15":1,
}
    
prob = LpProblem("store_schedule",LpMaximize)
storeVars = LpVariable.dicts("Days",(no_days_list,Store),0,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", 0, Capacity, cat=LpContinuous)
maxLoad = LpVariable("maxLoad", 0, Capacity, cat=LpContinuous)

添加为objective:

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