行業(yè)例:排產(chǎn)排程-采購(gòu)和生產(chǎn)規(guī)劃
排產(chǎn)排程問(wèn)題也可以用數(shù)學(xué)規(guī)劃的方式進(jìn)行建模,調(diào)用MindOpt優(yōu)化求解器進(jìn)行求解。
行業(yè)背景
排產(chǎn)排程、原料采購(gòu)、倉(cāng)儲(chǔ)存放等是制造業(yè)降本增效的關(guān)鍵問(wèn)題。如何合理安排采購(gòu)量和生產(chǎn)計(jì)劃,讓成本降低,或者利潤(rùn)更高。這個(gè)優(yōu)化問(wèn)題也可以運(yùn)用數(shù)學(xué)規(guī)劃的方法來(lái)建模和求解。
例如:某香皂制造廠要對(duì)未來(lái)半年內(nèi)的香皂生產(chǎn)和原料采買(mǎi)制定計(jì)劃。原料的部分需要提前預(yù)留,但是存儲(chǔ)也有成本,需要盡量合理安排,使得原料夠用,又能滿足使用需要。
業(yè)務(wù)調(diào)研、數(shù)據(jù)量化、數(shù)學(xué)建模
在使用優(yōu)化技術(shù)的時(shí)候,需要更詳細(xì)的調(diào)研業(yè)務(wù)的需求,整理相關(guān)的業(yè)務(wù)邏輯和數(shù)據(jù),并量化表示它。然后采用數(shù)學(xué)規(guī)劃的方法進(jìn)行數(shù)學(xué)建模。
此部分細(xì)節(jié)較多,可在案例排產(chǎn)排程03中查閱細(xì)節(jié),此處我們僅列出數(shù)學(xué)公式如下,參數(shù)部分請(qǐng)點(diǎn)擊案例鏈接查看:
集合O 是油脂,包含O1和O2, M是月份。
以上公式對(duì)應(yīng)的約束有:
假設(shè)從油脂到香皂的轉(zhuǎn)化過(guò)程中,油脂沒(méi)有任何浪費(fèi),生產(chǎn)過(guò)程中重量守恒
假設(shè)從油脂到香皂的制造過(guò)程中,硬度的轉(zhuǎn)化滿足線性關(guān)系
任意油脂上個(gè)月的儲(chǔ)存量與本月購(gòu)買(mǎi)量的總和要等于本月的使用量與該月儲(chǔ)存量的總和,需要考慮一月與其他月份,其中是1月份時(shí),各種油脂的初始儲(chǔ)備量
植物油脂與動(dòng)物油脂購(gòu)買(mǎi)量的限制
六月末每種油脂的儲(chǔ)存數(shù)量需要與儲(chǔ)存儲(chǔ)備量一致
源代碼
MindOpt支持多種編程語(yǔ)言或者建模語(yǔ)言來(lái)調(diào)用。此處僅列出一種供參考:
MindOpt APL建模語(yǔ)言調(diào)用
MindOpt APL建模語(yǔ)言的源代碼(可在排產(chǎn)排程03線上試運(yùn)行):
##====MindOpt APL 建模語(yǔ)言版本代碼====
clear model;#清除model,多次run的時(shí)候使用
option modelname manufacture_03_soap2;
#---------建模-----------------
# manufacture_03_soap2.mapl
# 聲明集合
set O1 := { "VEG1", "VEG2" };
set O2 := {"OIL1", "OIL2", "OIL3"};
set O := O1 + O2;
set M := {1, 2, 3, 4, 5, 6};
set N := {"Buy", "Use", "Store"};
# 聲明參數(shù)
param cost[O * M] :=
| 1, 2, 3, 4, 5, 6 |
|"VEG1"| 110, 130, 110, 120, 100, 90 |
|"VEG2"| 120, 130, 140, 110, 120, 100 |
|"OIL1"| 130, 110, 130, 120, 150, 140 |
|"OIL2"| 110, 90, 100, 120, 110, 80 |
|"OIL3"| 115, 115, 95, 125, 105, 135 |;
param hardness[O] := <"VEG1"> 8.0, <"VEG2"> 6.0,
<"OIL1"> 2.0, <"OIL2"> 4.0, <"OIL3"> 5.0;
param r := 150;
param b1 := 200;
param b2 := 250;
param l := 3;
param u := 6;
param s := 500;
param d := 5;
# 聲明變量
var x[O * M * N] >= 0;
var y[M] >= 0;
# 聲明目標(biāo)
maximize Reward: sum {<m> in M}(
r * y[m]
- sum {<j> in O} cost[j, m] * x[j, m, "Buy"]
- d * sum{<j> in O} x[j, m, "Store"]
);
# 聲明約束
subto Weight:
forall { <m> in M }
sum {<j> in O} x[j, m, "Use"] == y[m];
subto Hardness1:
forall { <m> in M }
sum {<j> in O} hardness[j] * x[j, m, "Use"] >= y[m] * l;
subto Hardness2:
forall {<m> in M }
sum {<j> in O} hardness[j] * x[j, m, "Use"] <= y[m] * u;
subto VEGBound:
forall {<m> in M }
sum {<j> in O1} x[j, m, "Use"] <= b1;
subto OILBound:
forall { <m> in M }
sum {<j> in O2} x[j, m, "Use"] <= b2;
subto Link_1:
forall {<j, 1> in O * M }
s + x[j,1,"Buy"] == x[j,1,"Use"] + x[j,1,"Store"];
subto Link_2to6:
forall {<j, m> in O * M with m > 1 }
x[j,m-1,"Store"] + x[j,m,"Buy"] == x[j,m,"Use"] + x[j,m,"Store"];
subto Store_June:
forall { <j> in O }
x[j,6,"Store"] == s;
#------------------------------
print "-----------------用MindOpt求解---------------";
option solver mindopt; # 指定求解用MindOpt求解器
solve; # 求解
#display; #打印求解變量值,比較長(zhǎng),請(qǐng)刪除注釋#后進(jìn)行打印
print "-----------------結(jié)果---------------";
print "最大利潤(rùn) = ", sum {<m> in M}(
r * y[m]
- sum {<j> in O} cost[j, m] * x[j, m, "Buy"]
- d * sum{<j> in O} x[j, m, "Store"]
);
結(jié)果和結(jié)果用法
不同代碼日志打印不一樣。部分結(jié)果日志打印如下所示:
...
Model summary.
- Num. variables : 96
- Num. constraints : 60
- Num. nonzeros : 253
- Bound range : [2.0e+02,5.0e+02]
- Objective range : [5.0e+00,1.5e+02]
- Matrix range : [1.0e+00,8.0e+00]
...
Simplex method terminated. Time : 0.002s
...
OPTIMAL; objective 108250.00
...
-----------------結(jié)果---------------
最大利潤(rùn) = 108250
目標(biāo)的最優(yōu)解是:108250。更多解的細(xì)節(jié),如決策變量的取值,可以通過(guò)print
命令打印來(lái)顯示,也可以從程序運(yùn)行寫(xiě)的.sol
文件里面獲取,還可以調(diào)用display
獲取所有變量的。
如運(yùn)行如下指令,驗(yàn)證“六月末每種油脂的儲(chǔ)存數(shù)量需要等于一月初的初始儲(chǔ)存數(shù)量”這個(gè)約束:
forall {<j> in O} print '六月末(',j,')的儲(chǔ)存數(shù)量= ', x[j,6,"Store"];
輸出結(jié)果是500,與1月初需求相同:
六月末(VEG1)的儲(chǔ)存數(shù)量= 500
六月末(VEG2)的儲(chǔ)存數(shù)量= 500
六月末(OIL1)的儲(chǔ)存數(shù)量= 500
六月末(OIL2)的儲(chǔ)存數(shù)量= 500
六月末(OIL3)的儲(chǔ)存數(shù)量= 500
運(yùn)行如下代碼,將輸出打印為csv格式,作為排產(chǎn)排程問(wèn)題的解決方案:
print "{}, {}, {}, {}" % "油脂", "月份", "處理方案","數(shù)量" : "每月油脂處理方式.csv";
close "每月油脂處理方式.csv";
forall {<j,m,n> in O*M*N}
print "{}, {}, {}, {}" % j, m, n, x[j,m,n] >> "每月油脂的處理方式.csv";
close "每月油脂處理方式.csv";
print "{}, {}" % "月份", "生產(chǎn)計(jì)劃" : "Results_.csv";
close "Results_.csv";
forall {<m> in M}
print "{}, {}" % m,y[m] >> "Results_.csv";
close "Results_.csv";
部分結(jié)果如下:
即,一月采購(gòu)油脂VEG1的計(jì)劃為0,使用計(jì)劃為100噸,儲(chǔ)存計(jì)劃為400噸、二月的采購(gòu)計(jì)劃為0,使用和儲(chǔ)存計(jì)劃各為200噸。