🎓 レベル:基礎 | 重要度:A(必須)
要点(BLUF)
- モデリングとは現実を 「決定変数 → 目的関数 → 制約」 の順に数式へ翻訳する作業。
- 手順は固定:何を決める?(変数)→ 何を最良にする?(目的)→ 何を守る?(制約)。
- 良いモデルの分かれ目は「変数の定義が一意か」「できるだけ線形に保てるか」。
概念 ── 翻訳の3ステップ
最適化を解く前に、問題を数式にしなければならない。ここで失敗すると、どんな高性能ソルバーも無意味。翻訳は必ず次の順序で行う。
- 決定変数:こちらが「決められる量」は何か。単位・範囲・連続/整数を明示する。
- 目的関数:その変数の関数として「最良の物差し」を1つ書く。
- 制約:守るべき条件をすべて不等式・等式で書く(資源・需要・論理・非負)。
具体例 ── 2製品の生産計画
ある工場が製品 A・B を作る。利益は A が1個 40、B が1個 30。各製品は機械時間と材料を消費する。資源は限られている。利益最大の生産量は?
ステップ1:決定変数
ステップ2:目的関数(利益最大化)
ステップ3:制約(機械時間 ≤ 120、材料 ≤ 100)
これで現実の言葉が完全に線形計画になった。実体は第2章 線形計画の標準形と幾何 で解く。
Pythonで解いて翻訳を確認
翻訳が正しいか、scipy.optimize.linprog で解いて確かめる。linprog は 最小化なので、利益最大化は符号反転して与える(最適化問題とは の変換)。
import numpy as np
from scipy.optimize import linprog
# linprog は最小化なので、利益最大化 max 40xA+30xB は -利益を最小化する
c = [-40, -30] # 目的(符号反転)
A_ub = [[2, 1], # 機械時間 2xA + 1xB <= 120
[1, 1]] # 材料 1xA + 1xB <= 100
b_ub = [120, 100]
bounds = [(0, None), (0, None)] # xA, xB >= 0
res = linprog(c, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method="highs")
xA, xB = res.x
print(f"最適生産量: xA={xA:.1f}, xB={xB:.1f}")
print(f"最大利益: {-res.fun:.1f}") # 符号を戻す
実行結果:
最適生産量: xA=20.0, xB=80.0
最大利益: 3200.0
A を 20、B を 80 作ると利益 3200 が最大。検算:機械時間 (ぴったり使い切り)、材料 (使い切り)。**両資源が制約として「効いている」**から、ここが頂点最適になっている(線形計画の標準形と幾何)。
良いモデルの作法
- 変数は一意に定義する:「=生産量」では曖昧。「=1日あたり製品 A の生産個数(連続)」のように単位・期間・型まで決める。
- できるだけ線形に保つ:線形なら凸で扱いやすい(最適化問題の分類)。非線形項は線形化を検討(定式化の技法)。
- スケールを揃える:係数が桁違いだと数値的に不安定。単位を調整する。
- 論理条件は変数で表す:「もし作るなら最低ロット」などは 0/1 変数とビッグM で(定式化の技法)。
数式の直観的意味
なぜ「変数 → 目的 → 制約」の順なのか。変数が決まらないと目的も制約も書けない(すべて変数の関数だから)。変数の 定義域(連続か整数か、範囲)が、そのまま問題クラス(最適化問題の分類)を決める。つまりモデリングの最初の一手(変数の選び方)が、解きやすさをほぼ決めてしまう。同じ現実でも、変数の取り方次第で線形にも非線形にもなる ── ここがモデラーの腕の見せ所。
⚠️ よくある誤解・落とし穴
- 目的関数を2つ書かない。多目的なら重み付き和にするか、片方を制約に回す。素朴に「両方最大化」とは書けない。
- 非負制約を忘れない:生産量・在庫は 。これを落とすと負の生産という無意味解が出る。
- 暗黙の制約を見落とさない:整数性(人数は整数)、容量上限、需要下限など、現実の「当たり前」を式にする。
- 過剰に詳細化しない:解ければよい粒度に留める。変数が増えるほど解きにくくなる。
関連ノート
- 前提:最適化問題とは
- 解く側:線形計画の標準形と幾何
- モデリングの引き出し(線形化・論理制約):定式化の技法
- ツールの使い分け:実装ツールの使い分け
- 章のハブ:最適化の基礎 章目次