最近導導讓牛牛改篇論文,牛牛在她的指導下把非線性問題化成了線性。然鵝,化成線性後的模型決策變數和約束條件均達到上百甚至上千個,這讓牛牛犯了難,以下方法或許能為這樣大規模模型的變數和約束輸入提供思路(๑•́₃ •̀๑)
一、問題描述及模型建立
-
指派問題: 分配\(n\)人去做\(n\)項工作;每人做且只做一項工作;若分配第\(i\)人去做第\(j\)項工作,需花費\(c_{ij}\)成本。問應如何分配工作使總成本最小?
-
模型建立:
\(s.t.\)
二、模型引數變數及對應OPL語法
-
引數(已知量):\(n\)、\(c_{ij}\),均為整型;
-
OPL定義引數(int/float):
int n=5
或者n=...
,如果為連續型變數,int
改為float
即可定義小數; -
n=...
允許n先不定義它是多少,用三個點表示。這種寫法實現的是模型檔案和資料檔案分開寫。
-
-
集合(已知量):\([1..n]\);
-
OPL定義集合(range):語法為
range 變數名=1..n
-
上面問題可以寫成
range worker=1..n
;1..n中的兩個點也是CPLEX語法,指從1到n,注意一定是兩個點。 -
定義好了集合,模型中再遇到i∈1..n時,就可以簡寫成
i in worker
了;也可以直觀寫成i in 1..n
,看個人程式設計喜好。
-
-
變數:\(x_{ij}\),為0-1布林型;
-
OPL定義變數(dvar):語法為
dvar 資料型別 變數
-
上面問題可定義為
dvar boolean x[Worker][Job]
或dvar bool x[1..n][1..n]
-
此外,資料型別除了boolean,還有整型(int),正整數(int+)、連續型(float)等。如定義一個正整數x則為:
dvar int+ x
-
-
定義模型時的符號有
-
最小化問題(minmize)
- OPL語法:
minimize
- OPL語法:
-
約束(subject to)
- OPL語法:
subject to
- OPL語法:
-
-
定義約束時的符號有:
-
求和號\(\sum\),OPL中可表示為
sum
-
任意號\(\forall\),OPL中可表示為
forall
,如\(\forall{i}\in{1,..n}\)可表示為forall(i in 1..n)
-
CPLEX程式設計思想: 先定義已知量(引數、集合),再定義未知量(決策變數),然後採用"集合語言"寫目標和約束。
三、Cplex求解(OPL語言)
基於上面的模型進行求解:
- 首先編寫已知量\(n,c_{ij}、1..n\):
int n=...; // 引數n
range Worker=1..n; // 工人集合
range Job=1..n; // 任務集合
int c[Worker][Job]=...; // 成本引數
- 編寫決策變數\(x_{ij}\)
dvar boolean x[Worker][Job]; // 決策變數
- 編寫目標函式
minimize sum(i in Worker)sum(j in Job) c[i][j]*x[i][j];
- 編寫約束條件
subject to
{ forall(i in 1..n)
sum(j in 1..n) x[i][j] == 1;//約束1
forall(j in 1..n)
sum(i in 1..n) x[i][j] == 1;//約束2
}
- 寫入Cplex中.mod檔案如下:
再將引數\(n,c_{ij}\)資料寫入.dat檔案:
n=3; // 3個工人去完成3個任務
c=[[3,8,2],[8,4,6],[6,2,10]]; // 成本矩陣
最後將這兩個檔案放入同一個執行配置中進行求解。本文分別將上面兩個檔案命名為demo1.mod和demo1_data.dat,執行配置命名為confit_demo(執行配置必須為英文名,不能為中文)
得出的求解結果如下:
即\(x_{11},x_{22},x_{33}=1\),其餘決策變數為\(0\)