基于 python pulp 库求解船舶泊位调度线性规划问题
生活随笔
收集整理的這篇文章主要介紹了
基于 python pulp 库求解船舶泊位调度线性规划问题
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
目錄
- 基于 python pulp 庫求解船舶泊位調度線性規劃問題
- 泊位調度問題建模
- 代碼實現
- 準備包
- 代碼講解
- 繪制圖像
- 完整代碼
- 題外話
基于 python pulp 庫求解船舶泊位調度線性規劃問題
泊位調度問題建模
- 船舶泊位調度問題指將進港的船舶有序規劃, 使各個船舶進港, 離港時間和位置都不沖突
- 由于是幫朋友做的, 他直接把模型給了我, 建模本人也不太了解, 下面就直接放一下模型.
min?f(x,y,z)=∑i=1T{c1i∣xi?pi∣+c2i(yi+bi?di)+}s.t.{xi+li≤L,i=1,2,?,Lxi+li≤xj+M(1?Zijx),i=1,?,l;j=1,?,l;i≠jyi+bi≤yj+M(1?Zijy),i=1,?,l;j=1,?,l;i≠jZijx+Zijy+Zjix+Zjiy≥1,i=1,?,l;j=1,?,l;i<jyi≥ai,i=1,?,lxi≥0,i=1,?,lZijx,Zijy=0,1,i=1,?,l;j=1,?,l;i≠j\min f(x, y, z)=\sum_{i=1}^{T}\left\{c_{1 i}\left|x_{i}-p_{i}\right|+c_{2 i}\left(y_{i}+b_{i}-d_{i}\right)^{+}\right\} \\ s.t. \left\{ \begin{array}{l} x_{i}+l_{i} \leq L, i=1,2, \cdots, L \\ x_{i}+l_{i} \leq x_{j}+M\left(1-Z_{i j}^{x}\right), i=1, \cdots, l ; j=1, \cdots, l ; i \neq j \\ y_{i}+b_{i} \leq y_{j}+M\left(1-Z_{i j}^{y}\right), i=1, \cdots, l ; j=1, \cdots, l ; i \neq j \\ Z_{i j}^{x}+Z_{i j}^{y}+Z_{j i}^{x}+Z_{j i}^{y} \geq 1, i=1, \cdots, l ; j=1, \cdots, l ; i<j \\ y_{i} \geq a_{i}, i=1, \cdots, l \\ x_{i} \geq 0, i=1, \cdots, l \\ Z_{i j}^{x}, Z_{i j}^{y}=0,1, i=1, \cdots, l ; j=1, \cdots, l ; i \neq j \\ \end{array} \right. minf(x,y,z)=i=1∑T?{c1i?∣xi??pi?∣+c2i?(yi?+bi??di?)+}s.t.????xi?+li?≤L,i=1,2,?,Lxi?+li?≤xj?+M(1?Zijx?),i=1,?,l;j=1,?,l;i=jyi?+bi?≤yj?+M(1?Zijy?),i=1,?,l;j=1,?,l;i=jZijx?+Zijy?+Zjix?+Zjiy?≥1,i=1,?,l;j=1,?,l;i<jyi?≥ai?,i=1,?,lxi?≥0,i=1,?,lZijx?,Zijy?=0,1,i=1,?,l;j=1,?,l;i=j?- 其中pi,ai,bi,di,li,c1i,c2ip_i,a_i,b_i,d_i,l_i,c_{1i},c_{2i}pi?,ai?,bi?,di?,li?,c1i?,c2i?均為隨機生成的參數
代碼實現
準備包
- Pulp 庫安裝: pip install PuLP(anaconda 不能使用conda install PuLP, conda 庫里沒有 Pulp 包)
- Pulp 庫的使用可以看看 YouCans 大大的文章, 這里就不過多介紹了
- matplotlib: conda install matplotlib
- pandas: conda install pandas
代碼講解
- createPara(l)隨機生成模型中的參數并存儲到 dataframe 中
- createVar(l)函數根據船只數生成對應變量(根據模型,船只數變化時,變量數量是要變化的)
- createPulpVar(x_variables, y_variables, zx_variables, zy_variables, abs_variables)函數將createVar(l)函數生成的變量轉換為pulp庫中的變量PulpVar
- main(l)主函數, 輸入船只數, 調用上述函數, 生成模型并進行求解
繪制圖像
- getColor()函數以及draw(model, l, df)用于可視化結果.
- 單純看結果的'Optimal'(有最優解)或'Infeasible'(無解)不夠直觀
- 設計了兩個函數, 用于在有最優解時, 繪制出最優解對應的船舶調度方案和目標值, 給出更直觀的圖像
- getColor()函數用以給各個船舶隨機生成顏色
- draw(model, l, df)用于繪制結果
- 生成圖像展示
- 五輛船舶時有最優解
- 輸出結果--------------------船舶數量為5-------------------- 參數列表:vessel p_i a_i b_i d_i l_i c_{1i} c_{2i} 0 0 520 73 8 2 276 5 9 1 1 323 21 8 55 199 9 9 2 2 297 0 6 0 287 5 5 3 3 174 13 11 34 159 5 5 4 4 38 23 11 29 236 5 7 求解狀態: Optimal BerthLocation: [520.0, 323.0, 297.0, 174.0, 38.0] BerthTime: [73.0, 47.0, 0.0, 34.0, 23.0]
- 結果圖像
- 25輛船時無解--------------------船舶數量為25-------------------- 參數列表:vessel p_i a_i b_i d_i l_i c_{1i} c_{2i} 0 0 535 11 11 87 198 8 6 1 1 537 73 7 68 278 6 5 2 2 103 7 8 34 171 5 7 3 3 19 17 7 33 199 5 5 4 4 138 64 7 21 208 5 9 5 5 455 74 10 54 198 8 6 6 6 543 45 9 7 247 9 7 7 7 479 33 9 71 268 9 8 8 8 176 20 8 40 284 8 8 9 9 364 59 8 100 179 7 9 10 10 367 29 10 56 275 7 6 11 11 280 39 8 88 286 9 7 12 12 411 63 8 38 280 9 7 13 13 475 71 11 96 205 8 9 14 14 154 29 6 88 174 7 8 15 15 98 8 11 78 258 7 9 16 16 100 62 9 64 238 7 5 17 17 303 45 9 97 265 6 6 18 18 407 50 9 34 232 9 7 19 19 113 55 6 75 286 7 7 20 20 538 3 10 99 176 5 7 21 21 309 46 8 72 223 9 7 22 22 358 26 11 22 208 8 5 23 23 456 61 9 63 172 6 7 24 24 33 11 8 69 259 5 8 求解狀態: Infeasible
完整代碼
# -*- encoding: utf-8 -*-import random import pulp import pandas as pd import matplotlib.pyplot as plt# 常量 L = 900 # 總泊位長度 M = 9999999 # 大M法def createPara(l):'''用df存儲隨機生成的參數Args:l (int): number of vesselsReturns:dataframe: 參數表格'''df = pd.DataFrame() # 創建一個空dataframe(空表格)df['vessel'] = list(range(l)) # 船只的編號:0-l-1, 共l搜船只# random.randint(x,y): 生成一個x到y之間的隨機整數# [random.randint(x,y) for i in range(l)]: 生成一個長度為l的列表,列表中的元素均為x到y之間的隨機整數# df['列名'] = 列表: 將列表賦值給df的列df['p_i'] = [random.randint(0, 550) for i in range(l)] # best berth location of vessel idf['a_i'] = [random.randint(0, 75) for i in range(l)] # expected arrival time of vessel idf['b_i'] = [random.randint(6, 11) for i in range(l)] # ship operational time required for vessel idf['d_i'] = [random.randint(0, 100) for i in range(l)] # requested departure time of vessel idf['l_i'] = [random.randint(150, 300) for i in range(l)] # length of vessel idf['c_{1i}'] = [random.randint(5, 9) for i in range(l)] # additonal travel cost of vessel idf['c_{2i}'] = [random.randint(5, 9) for i in range(l)] # penalty cost of vessel ireturn dfdef createVar(l):'''創建變量Args:l (int): number of vesselsReturns:list: 每個列表存儲不同種類的變量'''x_variables = ['x_' + str(i) for i in range(l)] # berth location of vessel iy_variables = ['y_' + str(i) for i in range(l)] # berthing time of vessel izx_variables = [[f'zx_{i}{j}' for j in range(l)] for i in range(l)] # zx_{ij} = 1 if vessel i located to the left_hand of vessel jzy_variables = [[f'zy_{i}{j}' for j in range(l)] for i in range(l)] # zy_{ij} = 1 if vessel i located below vessel jabs_variables = [f'abs_{i}' for i in range(l)] # 目標函數中有絕對值, 然而線性規劃中是不支持絕對值的, 所以需要新增一個變量來代替絕對值return x_variables, y_variables, zx_variables, zy_variables, abs_variablesdef createPulpVar(x_variables, y_variables, zx_variables, zy_variables, abs_variables):'''將變量轉換為pulp變量直接創建變量是不能用在pulp中的, 需要將變量轉換為pulp的變量, 在目標函數和約束條件中必須使用pulp變量Args:x_variables (lsit): berth locationy_variables (list): berthing timezx_variables (list): if vessel i located to the left_hand of vessel jzy_variables (list): if vessel i located below vessel jabs_variables (list): a variable to replace the absolute value'''# lowBound=0: 變量的下界為0, cat='Integer': 變量的類型為整數, cat='Binary': 變量值為0或1BerthLocation = pulp.LpVariable.dicts('BerthLocation', x_variables, lowBound=0, cat='Integer') # 將x_variables轉換為pulp變量BerthTime = pulp.LpVariable.dicts('BerthTime', y_variables, lowBound=0, cat='Integer') # 將y_variables轉換為pulp變量LeftSide = pulp.LpVariable.dicts('LeftSide',[item for row in zx_variables for item in row],lowBound=0,cat='Binary',) # 將zx_variables轉換為pulp變量Below = pulp.LpVariable.dicts('Below',[item for row in zy_variables for item in row],lowBound=0,cat='Binary',) # 將zy_variables轉換為pulp變量Abs = pulp.LpVariable.dicts('Abs', abs_variables, lowBound=0, cat='Integer') # 將abs_variables轉換為pulp變量return BerthLocation, BerthTime, LeftSide, Below, Absdef getColor():'''生成隨機顏色以上色'''color1 = random.randint(16, 255)color2 = random.randint(16, 255)color3 = random.randint(16, 255)color1 = hex(color1)color2 = hex(color2)color3 = hex(color3)ans = "#" + color1[2:] + color2[2:] + color3[2:]return ansdef draw(model, l, df):'''繪制圖像直觀顯示結果'''x_list, y_list = [0 for i in range(l)], [0 for i in range(l)] # 初始化兩個列表分別用以存儲x,y的值for v in model.variables():if 'BerthLocation' in v.name:x_list[int(v.name[-1])] = v.varValue # 將變量的值按順序存儲if 'BerthTime' in v.name:y_list[int(v.name[-1])] = v.varValueprint('BerthLocation:', x_list)print('BerthTime:', y_list)plt.figure()for i in range(l):# 繪制每個船只的矩形color = getColor() # 隨機生成顏色rect = plt.Rectangle((x_list[i], y_list[i]), # 矩形左下角坐標為(BerthLocation, BerthTime)df.iloc[i, 5], # 矩形的寬度為length of vesseldf.iloc[i, 3], # 矩形的高度為ship operational timecolor=color,fill=False,)plt.gca().add_patch(rect)plt.scatter(df.iloc[i, 1], df.iloc[i, 4], color=color) # 在圖上標出該船只的(best location, required departure time)plt.title(f'BerthAllocation of {l} vessels') # 標題plt.legend(['vessel', 'best location and required departure time']) # 圖例plt.grid(True) # 顯示網格plt.axis('auto') # 使x,y軸比例相同plt.xlabel('Berths')plt.ylabel('Date')plt.show()def main(l):'''線性規劃模型Args:l (int): number of vessels'''model = pulp.LpProblem('BerthAllocation', pulp.LpMinimize)# 用dataframe存儲隨機生成的系數df = createPara(l) # 隨機生成參數print('參數列表:')print(df) # 打印隨機生成的參數x_variables, y_variables, zx_variables, zy_variables, abs_variables = createVar(l) # 創建變量BerthLocation, BerthTime, LeftSide, Below, Abs = createPulpVar(x_variables, y_variables, zx_variables, zy_variables, abs_variables) # 將變量轉換為pulp變量# 創建目標函數model += pulp.lpSum([df.iloc[i, 6]* (Abs[abs_variables[i]]) # 使用絕對值變量代替|x_i-p_i|, 并在約束中增加對絕對值變量的約束保證取到了絕對值+ df.iloc[i, 7]* (BerthTime[y_variables[i]] + df.iloc[i, 3] - df.iloc[i, 4])for i in range(l)])# 增加約束for i in range(l):# 絕對值變量約束: 絕對值變量要大于等于(x_i-p_i)和(p_i-x_i), 從而保證取到了絕對值model += (Abs[abs_variables[i]] >= BerthLocation[x_variables[i]] - df.iloc[i, 1]) # 絕對值變量要大于等于(x_i-p_i)model += (Abs[abs_variables[i]] >= -BerthLocation[x_variables[i]] + df.iloc[i, 1]) # 絕對值變量要大于等于(p_i-x_i)# 其他約束# x_i >= 0 和 zx_ij, zy_ij 只能為0或1 在定義變量時已經限制了, 約束中不需要寫了model += BerthLocation[x_variables[i]] + df.iloc[i, 5] <= L # x_i + l_i <= Lmodel += BerthTime[y_variables[i]] >= df.iloc[i, 2] # y_i >= a_imodel += (BerthTime[y_variables[i]] + df.iloc[i, 3] - df.iloc[i, 4] >= 0) # y_i + b_i - d_i >= 0 (目標函數中的加號項)for j in range(l):if i < j:model += (LeftSide[zx_variables[i][j]]+ Below[zy_variables[i][j]]+ LeftSide[zx_variables[j][i]]+ Below[zy_variables[j][i]]>= 1) # zx_ij + zy_ij + zx_ji + zy_ji >= 1if i != j:model += (BerthLocation[x_variables[i]] + df.iloc[i, 5]<= BerthLocation[x_variables[j]]+ (1 - LeftSide[zx_variables[i][j]]) * M) # x_i + l_i <= x_j + M * (1-zx_ij)model += (BerthTime[y_variables[i]] + df.iloc[i, 3]<= BerthTime[y_variables[j]] + (1 - Below[zy_variables[i][j]]) * M) # y_i + t_i <= y_j + M * (1-zy_ij)model.solve(pulp.apis.PULP_CBC_CMD(msg=False)) # 求解線性規劃模型(pulp.apis.PULP_CBC_CMD(msg=False)的作用為不顯示log)print("求解狀態:", pulp.LpStatus[model.status]) # 打印求解狀態, Optimal: 存在最優解; Infeasible: 無解if pulp.LpStatus[model.status] == 'Optimal':# 若有最優解, 則打印最優解并繪制圖像draw(model, l, df)print("最優總成本 = ", pulp.value(model.objective))if __name__ == '__main__':for number in range(5, 30, 5): # 分別生成船只數量為5,10,15,20,25時的最優解print(f'--------------------船舶數量為{number}--------------------')main(number)題外話
- 幫學弟做的一門課程的大作業, 我自認為做的不錯, 然而他老師給他大作業打了 0 分, 不知道什么原因, 可能是因為他老師調研過班里并沒有人會 python, 覺得第一次用 python 不可能寫成這樣? 還是確實哪里做錯了, 請大家指正.
總結
以上是生活随笔為你收集整理的基于 python pulp 库求解船舶泊位调度线性规划问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: unittest测试mms医药系统登录模
- 下一篇: 最佳实践 | 如何基于GitHub Ac