2.1 目的関数・変数・制約
次のような生産計画問題を考えます.
2つの油田X, Yが存在し,それぞれ一日あたり重油・ガスを次の量だけ生産する.
生産量/日 | ||
---|---|---|
重油 | ガス | |
X | 6t | 4t |
Y | 1t | 6t |
また,重油・ガスの週あたりの生産ノルマが,次のように定められている.
ノルマ/週 | |
---|---|
重油 | 12t |
ガス | 24t |
油田X, Yの日あたりの運転コストは,次のとおりである.
運転コスト/日 | |
---|---|
X | 180 |
Y | 160 |
油田X, Yともに,最大で週5日まで運転可能である.ノルマを満たしながら運転コストを最小化するためには,それぞれの油田を週あたり何日運転すれば良いだろうか?
この問題を定式化すると,以下のようになります.
変数 | |
油田Xの運転日数/週 | |
油田Yの運転日数/週 | |
目的関数(最小化) | |
運転コスト/週 | |
制約条件 | |
重油ノルマ/週 | |
ガスノルマ/週 | |
油田Xの週あたりの運転日数制約 | |
油田Yの週あたりの運転日数制約 |
それでは,この問題をC++SIMPLEで記述した例を見てみましょう.
// 油田X,Yの運転日数/週(変数) Variable x(name = "油田Xの運転日数"); Variable y(name = "油田Yの運転日数"); // 運転コスト(目的関数) Objective cost(name = "全運転コスト", type = minimize); cost = 180 * x + 160 * y; // 製品ノルマ 6 * x + y >= 12; // 重油ノルマ/週 4 * x + 6 * y >= 24; // ガスノルマ/週 // 各油田の日数制約 0 <= x <= 5; // 油田Xの週あたりの運転日数制約 0 <= y <= 5; // 油田Yの週あたりの運転日数制約 // 求解 solve(); // 結果出力 x.val.print(); y.val.print(); cost.val.print();
このC++SIMPLEによる記述を上から順に見ていきましょう.
// 油田X,Yの運転日数/週(変数) Variable x(name = "油田Xの運転日数"); Variable y(name = "油田Yの運転日数");
この部分は変数(油田の運転日数)の宣言です.モデル中で使用する変数は,使用する前に宣言する必要があります.name = "..."
の部分には変数の名前を指定します.name = "..."
は省略可能ですが,出力などで使用されますので,なるべく記述した方が良いでしょう.
"//
"から行の終わりまではコメントです.
// 運転コスト(目的関数) Objective cost(name = "全運転コスト", type = minimize);
この部分は目的関数(運転コスト)の宣言です.目的関数の内容を定義する前に,宣言する必要があります.name = "..."
の部分には目的関数の名前を指定します.変数の宣言同様,name = "..."
は省略可能ですが,出力などに利用されますので,なるべく記述した方が良いでしょう.type = minimize
で目的関数が最小化されるべきことを指示します.type = maximize
とすれば,目的関数を最大化します.
cost = 180 * x + 160 * y;
この部分は目的関数(運転コスト)の内容定義です.=
の左辺に目的関数を,右辺に目的関数の内容を記述します.*
は積,+
は和を表す演算子です.C++SIMPLEでは四則演算や数学関数(exp()
, sin()
...)などを式の記述に用いることができます.
// 製品ノルマ 6 * x + y >= 12; // 重油ノルマ/週 4 * x + 6 * y >= 24; // ガスノルマ/週
この部分では制約式(生産ノルマ)を定義しています.関係演算子>=
の左辺,右辺には,任意の式を記述できます.目的関数の内容定義の際と同様に,任意の式の中に演算子や数学関数を記述できます.左辺と右辺の関係を表す関係演算子には,以下のものを指定できます.
C++SIMPLEの関係演算子 | 定式化時の記述 |
---|---|
>= |
|
<= |
|
== |
// 各油田の日数制約 0 <= x <= 5; // 油田Xの週あたりの運転日数制約 0 <= y <= 5; // 油田Yの週あたりの運転日数制約
この部分は制約式(運転日数の上下限)を定義しています.ここでは変数の上下限を指定していますが,C++SIMPLEでは一般の制約式と変数の上下限制約を区別しませんので,x, yの部分に任意の式を書くことが可能です.
以上で,問題の定義の記述は完了です.
次に,これまでに定義した問題の最適解を求め,結果を出力する部分を記述します.
// 求解 solve();
solve()
は,定義したモデルについて最適解の計算を行う関数です.solve()
は,必ずモデル記述の後に記述する必要があります.
// 結果出力 x.val.print(); y.val.print(); cost.val.print();
この部分は,最適化計算結果の出力を指定しています.最適化計算後の値を出力するためには,最適化計算solve()
の後に記述する必要があります.
以上でこのモデルについてのC++SIMPLEの記述は終了です.
次にこのモデルを実行してみます(実行方法については「3.1利用方法」を参照してください).すると,数理最適化モデルを解く経過が,以下のように出力されます.
[Expand Constraints and Objectives] sample.smp:7:info: 展開中 目的関数 (1/5) name="全運転コスト" sample.smp:10:info: 展開中 制約式 (2/5) name="" sample.smp:11:info: 展開中 制約式 (3/5) name="" sample.smp:14:info: 展開中 制約式 (4/5) name="" sample.smp:15:info: 展開中 制約式 (5/5) name="" [About Nuorium Optimizer] Nuorium Optimizer xx.x.x (NLP/LP/IP/SDP module) <with META-HEURISTICS engine "wcsp"/"rcpsp"> <with Netlib BLAS> , Copyright (C) 1991 NTT DATA Mathematical Systems Inc. [Problem and Algorithm] PROBLEM_NAME sample NUMBER_OF_VARIABLES 2 NUMBER_OF_FUNCTIONS 3 PROBLEM_TYPE MINIMIZATION METHOD HIGHER_ORDER [Progress] <preprocess begin>.........<preprocess end> <iteration begin> res=4.0e+001 .... 2.8e-005 1.4e-007 <iteration end> [Result] STATUS OPTIMAL VALUE_OF_OBJECTIVE 750.0000021 ITERATION_COUNT 6 FUNC_EVAL_COUNT 9 FACTORIZATION_COUNT 7 RESIDUAL 1.402395924e-007 ELAPSED_TIME(sec.) 0.20
最後に結果出力に対応する結果が以下のように出力されます.
油田Xの運転日数=1.5 油田Yの運転日数=3 全運転コスト=750
=
の左辺は指定した変数と目的関数の名前で,name = "..."
に記述したものが出力されます.右辺には変数と目的関数の値が出力されています.
上に戻る