Cplex求解教程(基於OPL語言,可作為大規模運算輸入參考)

碼頭牛牛發表於2023-09-21

最近導導讓牛牛改篇論文,牛牛在她的指導下把非線性問題化成了線性。然鵝,化成線性後的模型決策變數和約束條件均達到上百甚至上千個,這讓牛牛犯了難,以下方法或許能為這樣大規模模型的變數和約束輸入提供思路(๑•́₃ •̀๑)

一、問題描述及模型建立

  • 指派問題: 分配\(n\)人去做\(n\)項工作;每人做且只做一項工作;若分配第\(i\)人去做第\(j\)項工作,需花費\(c_{ij}\)成本。問應如何分配工作使總成本最小?

  • 模型建立:

\[min\quad\sum^n_{i=1}\sum^n_{j=1}c_{ij}x_{ij} \]

\(s.t.\)

\[\sum^n_{j=1}x_{ij}=1,\forall{i=1,2,\dots,n} \]

\[\sum^n_{i=1}x_{ij}=1,\forall{j=1,2,\dots,n} \]

二、模型引數變數及對應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..n1..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
    • 約束(subject to)

      • OPL語法:subject to
  • 定義約束時的符號有:

    • 求和號\(\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.moddemo1_data.dat,執行配置命名為confit_demo(執行配置必須為英文名,不能為中文)

得出的求解結果如下:

\(x_{11},x_{23},x_{32}=1\),其餘決策變數為\(0\)

相關文章