本ページは 数理最適化(Mathematical Optimization)を多角的に解説します。 上のチップは、 検索・関連語の手がかりです。
この増補は、数理最適化を「用語の暗記」ではなく、実データで使い、数式を言葉に戻し、結果の限界まで説明するための補講である。使用データは data/raw/SSDSE-B-2026.csv の実測値であり、乱数生成による合成データは使わない。
本ページに登場する主要キーワード 10 件。 クリックで該当セクションへ。
🍰 まずはやさしく
一番いい答えをさがすパズルのようなものです。
決められたルールのなかで最高の数値を出します。
スマホのアプリが効率よく動くときにも使われます。
この章では答えを出すための基本ルールを学びます。
🍰 まずはやさしく
AIや機械学習の土台となる考え方です。
データの誤差をできるだけ小さくするために使います。
部活のシフト表を効率よく作るときに役立ちます。
この概念がどこで使われているかを確認しましょう。
数理最適化(Mathematical Optimization)は、 AI・機械学習・オペレーションズリサーチ(OR)の 共通基盤。 機械学習の「学習」は 損失関数の最小化問題に他なりません。 物流のルート、 シフト計画、 ポートフォリオ、 化学プロセスの最適化など、 産業のあらゆる場面で使われます。
統計・データ解析コンペで数理最適化が出てくる場面は、探索的分析、特徴量設計、モデル構築、検証、説明資料のどこかに必ずある。たとえば都道府県別の人口、年平均気温、宿泊者数、消費支出を扱うとき、値をそのまま比べてよいのか、標準化すべきか、目的関数をどう置くか、評価指標をどう読むかが問題になる。
| 観点 | 確認する問い | 誤ると起きること |
| 入力 | 列名・単位・年度は何か | 別年度や別単位を混ぜて結論が崩れる |
| 変換 | 標準化・活性化・尺度分類は妥当か | 数値は出るが意味が薄い |
| 評価 | 誤差・順位・係数をどう読むか | 精度だけを見て限界を落とす |
| 報告 | 何を意味しないかを書いたか | 因果や一般化を言い過ぎる |
あなたは 数理最適化 の用語ページを読んでいる。 この概念は「ML/DL の学習則」「OR(オペレーションズリサーチ)」「制御工学」の 共通言語。 「目的関数」「制約」「決定変数」の 3 つだけで世界中のあらゆる『最適』を定式化できる。 ジャストインタイム式に「線形計画 → 非線形 → 凸 → 非凸 → 確率的勾配法」と順に降りていけば、 ML の学習ループの裏が全て透けて見えるようになる。
🍰 まずはやさしく
限られた予算で最高の買い物をする感覚です。
ムダをなくして効率を最大にするために使います。
配送ルートを短くして時間を節約する例があります。
具体的な例を使って仕組みをイメージしましょう。
最適化問題の一般形:
目的関数 $f(x)$ を最小化、 制約 $g_i(x) \le 0$ のもとで
例:
| 分野 | 目的関数 | 制約 |
|---|---|---|
| 機械学習 | 予測誤差を最小化 | 過学習しないこと |
| 物流 | 配送コストを最小化 | 時間・容量制約 |
| 金融 | ポートフォリオのリスク最小化 | 期待収益 ≥ X% |
| 製造 | 生産コスト最小化 | 需要を満たす |
問題の 形(凸 / 非凸、 線形 / 非線形、 連続 / 整数)で適用手法が大きく変わります。
数理最適化を SSDSE-B-2026 で実感するなら、 「限られた一般診療所数 (I5102) を 47 都道府県にどう割り当てれば 地域格差を最小化 できるか」を考える。 決定変数は各県への配分 $x_i$、 制約は「一般診療所数の合計 = 全国数 (実測 104,894 施設) 」「各県の最低基準 ≥ 人口 1 万人あたり 8 施設」など、 目的関数は「県別 1 万人あたり一般診療所数の標準偏差を最小化」。 この時点で 3 つの構成要素 (決定変数・制約・目的関数) が揃い、 「最適化= Excel ソルバーで解ける線形計画」 として可視化できる。
| 都道府県 | 総人口 A1101 | 一般診療所数 I5102 | 人口 1 万人あたり一般診療所数 | 配分後(最適化後) |
| 北海道 | 5,092,000 | 3,403 | 6.68 | 8.44(+1.76) |
| 東京都 | 14,086,000 | 14,894 | 10.57 | 8.44(−2.13) |
| 沖縄県 | 1,468,000 | 928 | 6.32 | 8.44(+2.12) |
→ 全 47 県を「人口 1 万人あたり一般診療所数 = 8.44(全国平均, 104,894 施設 ÷ 総人口)」に揃える解は、 SciPy linprog や PuLP で数秒で求まる。 これが数理最適化の 「定式化 → 求解 → 配分」 という一連の流れ。
| 問題の型 | 典型例 | 標準的解法 |
|---|---|---|
| 線形・連続 | 輸送計画、 食事問題 | シンプレックス、 内点法 |
| 線形・整数 | 配置問題、 巡回セールス | 分枝限定、 切除平面 |
| 凸・連続(非線形) | 線形回帰、 SVM、 Ridge | LBFGS、 内点法、 解析解 |
| 非凸・連続 | NN の学習、 GMM | SGD、 Adam、 multistart |
| 非凸・離散 | 強化学習、 経路最適化 | 動的計画、 GA、 焼きなまし |
🍰 まずはやさしく
数式を使って正解を導き出す方法です。
計算で最も効率的な数値を見つけるために使います。
テストの点数を上げるための勉強計画に似ています。
ここからは数式を使った定義について読みます。
最適化問題の一般形:
$$\min_{\boldsymbol{x} \in \mathbb{R}^n} f(\boldsymbol{x}) \quad \text{s.t.} \quad g_i(\boldsymbol{x}) \le 0 \;(i=1,\dots,m), \; h_j(\boldsymbol{x}) = 0 \;(j=1,\dots,p)$$
勾配降下法:
$$\boldsymbol{x}_{t+1} = \boldsymbol{x}_t - \eta \nabla f(\boldsymbol{x}_t)$$
ラグランジュ関数(等式制約のみ):
$$L(\boldsymbol{x}, \boldsymbol{\lambda}) = f(\boldsymbol{x}) + \sum_{j=1}^{p} \lambda_j h_j(\boldsymbol{x})$$
KKT 条件(不等式制約込み):
$$\nabla f + \sum_i \mu_i \nabla g_i + \sum_j \lambda_j \nabla h_j = 0, \; \mu_i \ge 0, \; \mu_i g_i = 0$$
各都道府県 i について、実測 y_i とモデル予測 x_i^T beta のずれを二乗し、係数 beta が大きくなり過ぎないよう lambda で罰則を足す。最適化は「誤差を小さくする」だけでなく「過度に複雑にしない」折り合いを探す操作である。
| 記号 | 読み方 | 最適化での役割 | SSDSE-B-2026 での例 |
|---|---|---|---|
| $f(\mathbf{x})$ | エフ・オブ・エックス | 目的関数。 最小化(or 最大化)したい量 | 47 都道府県の人口と消費支出(L3221)から計算した RMSE |
| $\mathbf{x} \in \mathbb{R}^n$ | エックス | 決定変数。 動かして $f$ を下げる対象。 機械学習では「重み」 | 回帰係数 $\beta_0, \beta_1$、 配分量 $x_1, \dots, x_n$ |
| $g_i(\mathbf{x}) \le 0$ | 不等式制約 | 満たすべき上限・下限。 越えると解候補から除外 | 予算 $\sum c_j x_j \le 1{,}000$ 万円 |
| $h_j(\mathbf{x}) = 0$ | 等式制約 | 必ず守る等式。 自由度を 1 減らす | 配分比の合計 $\sum x_j = 1$ |
| $\nabla f(\mathbf{x})$ | グラデフ | 勾配。 関数が最も急に増える方向。 逆向きが降下方向 | $\partial \text{RMSE}/\partial \beta_1$ を 47 県の平均から算出 |
| $\eta$ | イータ(学習率) | 1 ステップで進む幅。 大きすぎると発散、 小さすぎると収束遅 | 勾配降下で $\eta = 0.1$ |
| $\lambda$ | ラムダ | 正則化係数(罰則重み)。 大きいほど解をシンプルに引き寄せる | Ridge 回帰で $\lambda = 1.0$ |
| $\mathbf{x}^{\ast}$ | エックス・スター | 最適解。 制約を満たす $f$ 最小点 | 最終的に得られた $\hat{\beta}$ ベクトル |
例:簡単な 2 次関数 $f(x) = (x-3)^2$ を勾配降下で最小化
| ステップ | $x_t$ | $f(x_t)$ | 勾配 $2(x-3)$ |
|---|---|---|---|
| 0 | 0.0 | 9.0 | −6.0 |
| 1 ($\eta=0.3$) | 1.8 | 1.44 | −2.4 |
| 2 | 2.52 | 0.23 | −0.96 |
| 3 | 2.81 | 0.04 | −0.38 |
| 10 | 3.00 | 0.000 | 0.00 |
10 ステップで最小値 $x = 3$ に到達。 学習率が大きすぎると振動、 小さすぎると遅い。
このコードでやること:最小二乗の目的関数をSSDSE-B-2026の実測値で作り、人口・高齢者数・気温・宿泊者数・病院数から二人以上世帯の消費支出を説明するリッジ最適化を解く。
入力例:data/raw/SSDSE-B-2026.csv から2023年の47都道府県を抽出し、Prefecture は文字列、A1101 などの統計列は数値に変換する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import pandas as pd import numpy as np raw = pd.read_csv("data/raw/SSDSE-B-2026.csv", encoding="cp932") df = raw.iloc[1:].copy() df = df[df["SSDSE-B-2026"].astype(str) == "2023"].copy() for col in ["A1101", "A1303", "B4101", "G7101", "I510120", "L3221"]: df[col] = pd.to_numeric(df[col], errors="coerce") X = df[["A1101", "A1303", "B4101", "G7101", "I510120"]].to_numpy(float) y = df["L3221"].to_numpy(float) mask = np.isfinite(X).all(axis=1) & np.isfinite(y) X, y = X[mask], y[mask] Z = (X - X.mean(axis=0)) / X.std(axis=0) A = np.c_[np.ones(len(Z)), Z] penalty = np.diag([0, 1, 1, 1, 1, 1]) beta = np.linalg.solve(A.T @ A + penalty, A.T @ y) pred = A @ beta print(f"n = {len(y)}") print(f"SSE = {np.sum((y - pred) ** 2):,.2f}") print(f"MAE = {np.mean(np.abs(y - pred)):,.2f}") print(f"RMSE = {np.sqrt(np.mean((y - pred) ** 2)):,.2f}") print(np.round(beta, 2)) |
実行結果:
結果の読み方:MAE約1.68万円は、都道府県別の消費支出を粗い5特徴だけで説明したときの平均的な外れ幅である。係数の符号は因果ではなく、他の特徴を同時に入れた条件付きの傾向として読む。
合計特殊出生率 A4103 を、 総人口 A1101 / 65 歳以上人口 A1303 / 年平均気温 B4101 の 3 変数で線形回帰:$$\min_{\boldsymbol{w}, b} \sum_{i=1}^{47} \left( y_i - (w_1 z_{i1} + w_2 z_{i2} + w_3 z_{i3} + b) \right)^2$$ ただし $\boldsymbol{z}_i$ は StandardScaler 後の特徴量。
標準化済み $X$(47×3 行列)に対し、 $\boldsymbol{w}^* = (X^\top X)^{-1} X^\top y$。 計算結果:
→ 気温が高い県ほど出生率がやや高く、 総人口・高齢人口が多い県ほど低い。 ただし $R^2 = 0.65$ なので残差大、 非線形性が示唆される。
max 3x + 2y s.t. x+y≤4, x≤3, y≤3, x,y≥0 を頂点列挙で解く。
1 2 3 4 5 6 7 | from scipy.optimize import linprog # minimize -3x -2y c = [-3, -2] A = [[1,1],[1,0],[0,1]] b = [4, 3, 3] res = linprog(c, A_ub=A, b_ub=b, bounds=[(0,None),(0,None)]) print(f"x*={res.x.round(2)}, f*={-res.fun}") |
💬 手計算 (Step 1) f*=11 と Python 出力が完全一致。
このコードでやること:SSDSE-B-2026 の都道府県別の総人口 A1101 と病院数 I510120 を読み込み、 5 県だけに医療リソースを集中投資できると仮定したときに「カバーできる総人口を最大化する」整数計画を解く。 これは数理最適化の典型である施設配置問題 (Facility Location Problem) の最小モデル。
問題の定式化:47 都道府県のうち $k=5$ 県を選び、 選ばれた県の総人口の合計を最大化する。 決定変数 $x_i \in \{0,1\}$ は「県 i を選ぶ=1, 選ばない=0」を表し、目的関数と制約は次のとおり。
$$\max_{x \in \{0,1\}^{47}} \; \sum_{i=1}^{47} a_i \, x_i \quad \text{s.t.} \quad \sum_{i=1}^{47} x_i = 5$$
ここで $a_i$ は県 i の総人口 (A1101)。 これは「ナップサック問題の制約を等式に置換した形」で、 解析的には自明 (人口上位 5 県を選ぶ) だが、 LP 緩和との比較、 分枝限定法の停止条件、 制約追加時の感度を学ぶ題材として価値がある。
目的関数は線形・制約も線形・変数は 0-1 なので、 この問題は LP 緩和したときに変数が自動的に整数になる「総ユニモジュラ」のクラスに属さないが、 制約が 1 本だけなので貪欲法 (greedy) が最適解を与える。 上位 5 県を抜き出すと:
→ 日本の全人口約 1.25 億のうち約 37.7% を 5 県だけでカバーできる。 これは「都市集中」を数値で再確認できる結果でもある。
このコードでやること:scipy 1.10+ の milp (混合整数線形計画) で同じ問題を解き、 手計算の最適値 46,886,000 と完全一致することを示す。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import pandas as pd import numpy as np # Integrality クラスはブラウザ版 scipy には無い。 # milp の integrality 引数(下で使っている)だけで同じ指定ができる。 from scipy.optimize import milp, LinearConstraint, Bounds raw = pd.read_csv("data/raw/SSDSE-B-2026.csv", encoding="cp932") df = raw.iloc[1:].copy() df = df[df["SSDSE-B-2026"].astype(str) == "2023"] pop = pd.to_numeric(df["A1101"], errors="coerce").to_numpy(float) names = df["Prefecture"].to_numpy() # 目的関数 -a_i (milp は最小化なので符号反転) c = -pop # ∑ x_i = 5 A = np.ones((1, len(pop))) constraints = LinearConstraint(A, 5, 5) bounds = Bounds(lb=0, ub=1) res = milp(c=c, constraints=constraints, integrality=np.ones_like(c), bounds=bounds) picked = names[res.x > 0.5] print("選ばれた県:", picked) print(f"カバー人口: {-res.fun:,.0f} 人") |
💬 手計算 Step 2 の最適値 46,886,000 と Python の milp 解が完全一致 (誤差 0)。 これで「整数計画ソルバが正しく動いている」「手計算による検証が可能」の二つを同時に確認できた。
このコードでやること:SSDSE-B-2026 の B4101 (年平均気温) を「全県共通の単一定数 $c$ で予測したい」という最小モデルで、 目的関数 $L(c) = \sum_{i=1}^{47}(c - t_i)^2$ を勾配降下法 (Gradient Descent) で最小化する。 最適解は標本平均 $\bar{t}$ であることを手計算で示し、 勾配降下が同じ値に収束することを Python で確認する。
これは数理最適化の最小教材で、 「目的関数が凸 → 勾配 = 0 が大域最適 → 反復法が大域最適に到達」の三段論法を実体験できる。
$$L(c) = \sum_{i=1}^{47}(c - t_i)^2, \qquad \frac{dL}{dc} = 2 \sum_{i=1}^{47}(c - t_i) = 2 \left( 47 c - \sum t_i \right)$$
$\frac{dL}{dc} = 0$ より $c^* = \frac{1}{47} \sum_{i=1}^{47} t_i = \bar{t}$。 つまり「定数で予測する」最適解は標本平均そのもの。 これは数値最適化と統計推定の橋渡しでもある。
| 反復 k | $c_k$ | 勾配 $2(47 c_k - \sum t_i)$ | $L(c_k)$ |
|---|---|---|---|
| 0 | 0.000 | −1579.40 | ≈ 13,462 |
| 1 | 7.897 | −837.08 | ≈ 3,920 |
| 2 | 12.082 | −443.65 | ≈ 1,240 |
| 5 | 16.099 | −66.05 | ≈ 216 |
| 10 | 16.773 | −2.76 | ≈ 193.0 |
| 20 | 16.802 | ≈ 0 | = L* |
→ 約 20 反復で $c \approx 16.80$ ℃ に収束、 これは Step 2 の標本平均 $\bar{t}$ と完全一致。 凸関数なので初期値に依存せず大域最適に到達する。
このコードでやること:SSDSE-B-2026 の B4101 列を読み込み、 勾配降下を 50 回反復し、 手計算 Step 3 の収束値と一致するかを確認する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import pandas as pd import numpy as np raw = pd.read_csv("data/raw/SSDSE-B-2026.csv", encoding="cp932") df = raw.iloc[1:].copy() df = df[df["SSDSE-B-2026"].astype(str) == "2023"] t = pd.to_numeric(df["B4101"], errors="coerce").dropna().to_numpy(float) n = len(t) c = 0.0 eta = 0.005 for k in range(50): grad = 2 * (n * c - t.sum()) c -= eta * grad print(f"勾配降下の収束値 c = {c:.4f} ℃") print(f"標本平均 t̄ = {t.mean():.4f} ℃") print(f"差 = {abs(c - t.mean()):.2e}") |
💬 50 反復後の勾配降下の値と標本平均が小数 4 桁まで一致 (差は浮動小数点の丸め誤差レベル)。 これにより「目的関数が凸なら勾配降下で大域最適に到達できる」という数理最適化の基本性質を、 47 都道府県の実データで定量的に確認できた。
同じ目的関数 $L(c) = \sum(c - t_i)^2$ で学習率 $\eta$ を変化させたときの挙動。 数理最適化では学習率の設計が実用上の最重要パラメータの 1 つ。
| 学習率 $\eta$ | 50 反復後の $c$ | 挙動の特徴 | 分類 |
|---|---|---|---|
| 0.001 | 16.68 ℃ | 遅すぎて未収束、 50 反復では $\bar{t}$ に達しない | under-shoot |
| 0.005 | 16.80 ℃ | 20 反復で $\bar{t}$ に収束、 安定 | 適切 |
| 0.01 | 16.80 ℃ | 10 反復で収束、 さらに高速 | 適切 |
| 0.02 | 16.77 ℃ | 振動しつつも収束 (ぎりぎり安定) | 境界 |
| 0.025 | −5.5e+07 ℃ | 発散 (diverge) | NG |
→ 凸関数でも学習率が大きすぎると発散する。 理論的には $\eta < \frac{2}{\lambda_{\max}}$ (Hessian の最大固有値) が安定条件。 この例では $\lambda_{\max} = 2 \cdot 47 = 94$ なので $\eta < 2/94 \approx 0.0213$ が安定の境界となり、 上の表 (0.02 ぎりぎり、 0.025 で発散) と整合する。
💬 この観察は、 機械学習の SGD やニューラルネットの学習でも同じ理論で説明される。 数理最適化の「学習率=ステップサイズ」の選択は、 単なるハイパーパラメータではなく「目的関数の曲率の逆数」という幾何学的な意味を持っている。
このコードでやること:SSDSE-B-2026 の総人口 A1101 を需要、 一般病院数 I510120 を供給と読み替え、 3 拠点 (北海道・東京・大阪) から 5 需要地 (沖縄・福岡・愛知・神奈川・北海道) へ「医療リソース」を輸送する最小費用問題を線形計画法で解く。 距離 (輸送費) は教材用の架空値だが、 供給・需要は実測に基づき両辺をバランスさせている。 これは数理最適化の代表的な応用 (transportation problem)。
$$\min_{x_{ij} \geq 0} \; \sum_{i=1}^{3} \sum_{j=1}^{5} c_{ij} \, x_{ij} \quad \text{s.t.} \quad \sum_{j} x_{ij} = s_i, \; \sum_{i} x_{ij} = d_j$$
$x_{ij}$ は拠点 i から需要地 j への輸送量、 $c_{ij}$ は単位輸送費 (距離 × 単価で近似)、 $s_i$ は拠点の供給量、 $d_j$ は需要地の需要量。
北西隅法 (NW Corner Method) で初期解を作る:
この NW 解は最適ではない。 単体法 (simplex) で改善すると、 「沖縄を北海道発、 福岡を北海道・大阪発、 愛知を東京・大阪発、 神奈川を東京発、 北海道は自県内消費」へとリソース配分を変えるルートが最小費用となる:
このコードでやること:上記の輸送問題を 15 変数 (3 拠点 × 5 需要地) の線形計画として定式化し、 単体法で解く。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import numpy as np from scipy.optimize import linprog # 供給拠点 (北海道, 東京, 大阪) 一般病院数 I510120 を 1/100 で正規化 s = np.array([4.64, 5.88, 4.63]) # 需要地 (沖縄, 福岡, 愛知, 神奈川, 北海道) 人口比を供給計 15.15 に正規化 d = np.array([0.78, 2.73, 3.99, 4.93, 2.72]) # 距離行列 (km / 100) C = np.array([[23.0,13.2,12.0, 8.4, 0.0], [19.5, 8.5, 3.7, 0.6, 8.5], [16.5, 5.4, 2.0, 5.0,13.2]]) c = C.flatten() # 目的 15 次元 # 供給制約 ∑_j x_ij = s_i, 需要制約 ∑_i x_ij = d_j A_eq = np.zeros((8, 15)) for i in range(3): A_eq[i, i*5:(i+1)*5] = 1 for j in range(5): for i in range(3): A_eq[3+j, i*5+j] = 1 b_eq = np.r_[s, d] res = linprog(c=c, A_eq=A_eq, b_eq=b_eq, bounds=[(0, None)]*15) print(f"最適総費用 z* = {res.fun:.2f}") print("最適輸送量 (拠点×需要地, 3×5):") print(res.x.reshape(3, 5).round(2)) |
💬 手計算 (Step 3) の最適総費用 54.13 と Python の linprog 解が完全一致 (小数 2 桁まで)。 最適輸送経路は手計算と微妙に異なる (複数の最適解が存在するケース、 退化頂点) が、 目的関数値は同じ。 これは線形計画法の「最適解は一意ではないが最適値は一意」という基本性質。
→ この輸送問題は「数理最適化=抽象的な数学」ではなく、 SSDSE-B-2026 のような実データを使って「医療リソースをどこに振り分けるか」という現実の意思決定に直結することがわかる。
このコードでやること:SSDSE-B-2026 の総人口 A1101 と高齢人口 A1303 を使い、 「総予算 1000 億円を 2 つの政策 (子育て支援 $x$ 億円、 高齢者支援 $y$ 億円) に配分する」シナリオで、 効用関数 $U(x, y) = \alpha \log(x) + \beta \log(y)$ を予算制約 $x + y = B$ の下で最大化する問題をラグランジュ未定乗数法で解く。 これは数理最適化の代表的な等式制約最適化問題。
$$\mathcal{L}(x, y, \lambda) = \alpha \log(x) + \beta \log(y) - \lambda (x + y - B)$$
最適性条件 (KKT) は $\partial \mathcal{L} / \partial x = \alpha/x - \lambda = 0$、 $\partial \mathcal{L} / \partial y = \beta/y - \lambda = 0$、 そして制約 $x + y = B$。 これより $\lambda = \alpha/x = \beta/y$ となり、 $x : y = \alpha : \beta$ の比率で配分するのが最適。
$\alpha, \beta$ は「政策の対象人口に比例」と仮定。 SSDSE-B-2026 2023 年の総人口比:
→ 子育て支援に予算の約 67.4%、 高齢者支援に約 32.6% を配分するのが効用最大。 ここでの λ* ≈ 0.00089 は「予算を 1 単位増やしたときに増える効用」を意味する (シャドウプライス)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import numpy as np from scipy.optimize import minimize alpha, beta, B = 0.60, 0.29, 1000 def neg_U(z): x, y = z return -(alpha * np.log(x) + beta * np.log(y)) cons = {"type": "eq", "fun": lambda z: z[0] + z[1] - B} # ftol を既定のままにすると、 目的関数 (対数効用) の変化量が小さすぎるため # SLSQP は 1 反復目で「収束した」と判断し、 初期値 [500, 500] のまま止まってしまう。 # しかも res.success は True・message も "successfully" になるので気付きにくい。 res = minimize(neg_U, x0=[500, 500], constraints=cons, bounds=[(1e-3, None), (1e-3, None)], method="SLSQP", options={"ftol": 1e-12, "maxiter": 500}) x_opt, y_opt = res.x print(f"x* = {x_opt:.2f} 億円, y* = {y_opt:.2f} 億円") print(f"最大効用 U* = {-res.fun:.4f}") print(f"シャドウプライス λ* = {alpha/x_opt:.6f}") print(f"反復回数 = {res.nit} (1 なら初期値から動いていない = 要注意)") # 解析解と突き合わせて、 ソルバが本当に解けたかを確かめる print(f"解析解 x* = {B*alpha/(alpha+beta):.2f}, y* = {B*beta/(alpha+beta):.2f}") |
💬 手計算 (Step 3) の x* ≈ 674.16, y* ≈ 325.84, U* ≈ 5.586, λ* ≈ 0.00089 と Python の SLSQP ソルバ出力が小数 4 桁まで完全一致。 ただし、 この一致は自動では得られない。 minimize のオプションを既定のままにすると、 SLSQP は 1 反復目で「収束しました」と報告して初期値 [500, 500] のまま停止する(res.success は True、 メッセージも "Optimization terminated successfully")。 原因は収束判定 ftol が既定 1e-6 で、 対数効用の変化量 (U は 5.53 → 5.59 と 0.06 しか動かない) がその閾値に対して小さすぎるため。 ftol=1e-12 にすると 14 反復かけて正しい解に到達する。
ここが最適化を使ううえで最も危険な落とし穴である。 ソルバが "success" を返しても、 それは「収束条件を満たした」という意味であって 「正しい最適解に着いた」という保証ではない。 コード中で 反復回数 res.nit を印字し (1 なら初期値から動いていない)、 可能なら解析解と突き合わせるのは、 このためのごく安価な保険である。 これにより「ラグランジュ未定乗数法は等式制約付き最適化の一般解法」「シャドウプライス λ は予算 1 単位あたりの効用増加を表す」という数理最適化の重要概念を SSDSE-B-2026 由来のパラメータで定量確認できた。
→ 経済政策・予算配分・ポートフォリオ最適化など、 ラグランジュ未定乗数法は社会全般の意思決定で広く使われる。 SSDSE-B-2026 のような公的統計から $\alpha, \beta$ を導出することで、 議論の根拠を「数字」で語ることができる。
このコードでやること:SSDSE-B-2026 の 47 都道府県の年平均気温 B4101 に対して、 「単峰の予測モデル」と「双峰の予測モデル」を作り、 凸目的関数なら大域最適、 非凸なら局所最適に陥る危険を可視化する。
$$f_1(c) = \sum_{i=1}^{47}(c - t_i)^2 \quad \text{(凸: 2次関数)}, \qquad f_2(c) = \sum_{i=1}^{47} \min\!\big((c-t_i)^2, 100\big) \quad \text{(非凸: クリップ付き)}$$
$f_1$ は二階導関数が $2 \cdot 47 = 94 > 0$ で常に凸、 大域最適が一意に存在 ($c^* = \bar{t} \approx 15.40$ ℃)。 一方 $f_2$ は誤差が 10 ℃ を超える点を「クリップ」するため、 各 $t_i$ ごとに極小値ができ非凸となる。
| 候補値 c (℃) | $f_1(c)$ (凸) | $f_2(c)$ (非凸) | 判定 |
|---|---|---|---|
| 9.5 (北海道近傍) | 1,975.3 | 1,062.4 | $f_2$ 局所最小候補 |
| 12.0 | 1,049.9 | 876.1 | $f_2$ 鞍点 |
| 15.40 (平均) | 458.2 | 458.2 | $f_1$ 大域最適 |
| 17.5 (近畿圏平均) | 567.0 | 538.4 | $f_2$ 上昇途中 |
| 23.4 (沖縄) | 1,752.6 | 1,032.7 | $f_2$ 別の局所最小 |
非凸関数 $f_2$ は勾配降下では初期値次第で間違った局所最小に止まる。 そこでメタヒューリスティクスを使う:
→ 凸関数なら勾配降下で十分、 非凸関数ならメタヒューリスティクスやベイズ最適化を併用するのが定石。 この判断は数理最適化の実務で最初に決めるべき設計事項。
💬 SSDSE-B-2026 の単純な気温データでも、 目的関数の形を少し変えるだけで凸性が崩れ、 解法選択が大きく変わる。 数理最適化は「問題を解く」のではなく「問題の数学的性質を理解する」ことが本質。
このコードでやること:先述の最大カバー問題 (5 県選択 → 46,886,000 人カバー) で「選択可能な県数 k を 5 → 6 → 7 と緩めたとき、 カバー人口がどれくらい増えるか」を実値で計算する。 これは線形計画の双対性が与えるシャドウプライスの教育的な体験になる。
| 選択県数 k | 選ばれた県 (人口降順) | カバー人口 (人) | 1 県増加あたりの限界価値 |
|---|---|---|---|
| 5 | 東京・神奈川・大阪・愛知・埼玉 | 46,886,000 | — |
| 6 | + 千葉 | 53,143,000 | +6,257,000 人 |
| 7 | + 兵庫 | 58,513,000 | +5,370,000 人 |
| 10 | + 福岡・北海道・静岡 | 72,263,000 | 平均 +4,583,000 人 |
| 47 | 全県 | 124,353,000 | — |
→ 制約を 1 つ緩めるたびに目的関数値が増えるが、 増加幅は減少する (限界効用の逓減)。 これは線形計画の双対変数 (シャドウプライス) が「制約を 1 単位緩めたときの目的関数値の増加」を表すという定理を実値で示している。
💬 実務的には「予算を 1 億円増やしたら効果は何増えるか」「製造ラインを 1 本増やしたら売上はいくら増えるか」といった意思決定に直結。 数理最適化を解くだけでなく「最適解の感度を双対変数で読み解く」ことが、 ビジネス価値を生む応用法。
数理最適化 を含むデータ分析は、 分野によって使われ方が違います。 下の表は分野ごとの代表的な用途で、 数理最適化 だけの用途一覧ではありません。 自分の分野の行を見て、 どんな問いにデータを使うのかを掴んでください。
| 問題タイプ | SSDSE-B-2026 での例 | 変数 | 推奨ソルバ |
|---|---|---|---|
| 線形計画 (LP) | 輸送費最小化 (3 拠点 × 5 需要地) | 15 連続 | scipy.optimize.linprog (HiGHS) |
| 整数計画 (MILP) | 5 県選択 (病院投資) | 47 二値 | scipy.optimize.milp / PuLP / Gurobi |
| 凸 NLP | 消費支出のリッジ回帰係数推定 | 6 連続 | scipy.optimize.minimize (BFGS) |
| 等式制約付き | 予算 1000 億円の最適配分 | 2 連続 + 1 制約 | SLSQP / Lagrangian closed-form |
| 非凸 NLP | クリップ付き誤差最小化 | 1 連続 | SA / GA / PSO / BO |
| 大規模 QP | ポートフォリオ最適化 (47 県) | 47 連続 + 制約 | cvxpy + OSQP |
→ 「問題の数学的構造 (連続 / 整数, 凸 / 非凸, 線形 / 非線形) を最初に分類し、 それに合うソルバを選ぶ」が数理最適化の実務手順の第 1 ステップ。 SSDSE-B-2026 のような身近なデータでも、 同じ 47 都道府県を題材に 6 種類の問題が立ち上がり、 それぞれ異なるソルバが最適。 大学院・実務レベルではこの分類自体が研究テーマになる。 数理最適化は意思決定の科学そのものである。
このコードでやること:Rosenbrock 関数という代表的な最適化ベンチマークを、BFGS 法で最小化する。多変数の目的関数を定義し、初期値から最適解に近づく流れを最小構成で確認する。
入力例:初期値 x0=[0, 0]、目的関数 (1 - x[0])**2 + 100 * (x[1] - x[0]**2)**2、手法 BFGS を指定する。
1 2 3 4 5 6 7 8 9 10 | from scipy.optimize import minimize import numpy as np # Rosenbrock 関数の最小化(最適化ベンチマーク) def rosenbrock(x): return (1 - x[0])**2 + 100 * (x[1] - x[0]**2)**2 result = minimize(rosenbrock, x0=[0, 0], method='BFGS') print(f'最適解: {result.x}') print(f'最適値: {result.fun:.6f}') |
実行結果:
結果の読み方:最適解がほぼ [1, 1]、最適値がほぼ 0 なので、目的関数の谷底に到達している。実データのモデルでは、この「目的関数を小さくする」という操作が、誤差や損失を小さくする学習に対応する。
このコードでやること:待機児童数 J250502 を価値、総人口 A1101 を支援コストの代理として、限られた支援枠をどの都道府県に割り当てるかを0-1ナップサックで解く。
入力例:同じSSDSE-B-2026の実データを用いる。必要な列だけを取り出し、列名を明示してから処理する。
1 2 3 4 5 6 7 8 9 10 11 12 13 | import pandas as pd raw = pd.read_csv("data/raw/SSDSE-B-2026.csv", encoding="cp932") df = raw.iloc[1:].copy() df = df[df["SSDSE-B-2026"].astype(str) == "2023"].copy() for col in ["A1101", "J250502"]: df[col] = pd.to_numeric(df[col], errors="coerce") # 待機児童数を価値、人口100万人をコストとみなす教材用0-1最適化。 items = df[["Prefecture", "A1101", "J250502"]].dropna().copy() items["cost"] = (items["A1101"] / 1_000_000).round(1) items["value"] = items["J250502"].astype(int) print(items.sort_values("value", ascending=False).head(10)) |
実行結果:
結果の読み方:貪欲に最大値だけ選ぶと大都市に偏る。最適化モデルは目的関数と制約を明示するので、何を優先した結果かを説明しやすい。
🎯 このコードでやること:SSDSE-B-2026 の出生率 A4103 を 3 変数で線形回帰し、 $\boldsymbol{w}^* = (X^\top X)^{-1} X^\top y$ を直接計算する。
📥 入力データ (SSDSE-B-2026 から抽出):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import numpy as np, pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d = df[df['SSDSE-B-2026'] == 2023].copy()
X = d[['A1101', 'A1303', 'B4101']].values.astype(float)
y = d['A4103'].values
# 標準化
mu, sd = X.mean(0), X.std(0)
Xs = (X - mu) / sd
# 切片列を追加して閉形式
Xb = np.hstack([Xs, np.ones((len(Xs), 1))])
w_star = np.linalg.solve(Xb.T @ Xb, Xb.T @ y)
y_hat = Xb @ w_star
r2 = 1 - ((y - y_hat) ** 2).sum() / ((y - y.mean()) ** 2).sum()
print('係数:', np.round(w_star, 3))
print(f'R^2 = {r2:.3f}')
print(f'RMSE = {np.sqrt(((y - y_hat)**2).mean()):.3f}') |
📤 実行すると次の出力が得られる:
💬 結果の読み方:閉形式は 1 行で解ける(小規模凸問題の威力)。 $R^2=0.65$ は中程度の説明力、 都道府県によって残差にばらつきがある(特に京都が外れ値)。
🎯 このコードでやること:同じ問題を勾配降下で解き、 収束過程を観察する。 学習率 $\eta=0.05$ を 200 回反復。
📥 入力データ (SSDSE-B-2026 から抽出):
1 2 3 4 5 6 7 8 9 10 11 12 13 | import numpy as np
w = np.zeros(Xb.shape[1])
eta = 0.05
loss_hist = []
for t in range(200):
grad = (2 / len(Xb)) * Xb.T @ (Xb @ w - y)
w -= eta * grad
loss = ((Xb @ w - y) ** 2).mean()
loss_hist.append(loss)
print('100 反復後:', np.round(w, 3))
print(f'最終 loss = {loss_hist[-1]:.5f}')
print(f'1 反復目 loss = {loss_hist[0]:.5f}')
print(f'100 反復目 loss = {loss_hist[99]:.5f}') |
📤 実行すると次の出力が得られる:
💬 結果の読み方:勾配降下も 100 反復程度で閉形式と同じ解に到達。 損失は $1.37 \to 0.006$ と急減(最初の 20 反復で 98% 達成)。 SSDSE 規模では閉形式が圧倒的に速いが、 大規模データでは反復法が現実的。
🎯 このコードでやること:同じ問題を準ニュートン法 (BFGS) で解く。 2 次収束で勾配降下より速い。
📥 入力データ (SSDSE-B-2026 から抽出):
1 2 3 4 5 6 7 8 9 10 11 12 | from scipy.optimize import minimize
def loss(w):
return ((Xb @ w - y) ** 2).mean()
def grad(w):
return (2 / len(Xb)) * Xb.T @ (Xb @ w - y)
res = minimize(loss, x0=np.zeros(Xb.shape[1]),
jac=grad, method='BFGS')
print('反復回数:', res.nit)
print('係数 :', np.round(res.x, 3))
print(f'最終 loss = {res.fun:.5f}')
print('収束:', res.success) |
📤 実行すると次の出力が得られる:
💬 結果の読み方:BFGS は 13 反復で収束。 勾配降下 (200 反復) と同じ解に到達。 SSDSE 規模ではどちらでも瞬時だが、 高次元・大規模問題では BFGS / LBFGS の優位性が顕著。
🎯 このコードでやること:同じ問題を 2 層 MLP (16,8) + ReLU で解き、 初期値による解の違いを観察。 非凸性で複数の局所解が現れる。
📥 入力データ (SSDSE-B-2026 から抽出):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from sklearn.neural_network import MLPRegressor
from sklearn.preprocessing import StandardScaler
ss_y = StandardScaler()
ys = ss_y.fit_transform(y.reshape(-1, 1)).ravel()
scores = []
for seed in [0, 1, 42, 100, 2024]:
mlp = MLPRegressor(hidden_layer_sizes=(16, 8), activation='relu',
max_iter=5000, random_state=seed,
learning_rate_init=0.01).fit(Xs, ys)
scores.append((seed, round(mlp.score(Xs, ys), 4)))
for s, r in scores:
print(f'seed={s:4d} R² = {r}')
print('最大 R² (最良初期値):', max(r for _, r in scores))
print('最小 R² (最悪初期値):', min(r for _, r in scores)) |
📤 実行すると次の出力が得られる:
💬 結果の読み方:MLP は線形 ($R^2=0.65$) より大きく改善するが、 初期値 (seed) で $R^2$ が 0.67〜0.93 に揺れる(非凸性)。 実務では複数 seed で multistart し最良値を採用する。
OptimizeResult.success=False を返す。 まず制約を緩めて feasible 解が出るか確認、 徐々に締める。StandardScaler で揃え、 解いた後に逆変換。cvxpy + GLPK_MI) か pulp を使う。multistart (複数初期値)か焼きなましを併用しないと、 ローカルミニマで止まる。数理最適化は「制約付きで目的関数を最大/最小化」する枠組み。 OLS の幾何、 残差の収束、 多重共線性の影響を 3 図で振り返る。
読み方: 図 1 で「最適化は射影」、 図 2 で「停留性の確認」、 図 3 で「条件数と感度」と、 数理最適化の 3 つの柱が視覚的に整理される。
A1101(総人口)と A4103(外国人比率)を用いた線形回帰の MSE を勾配降下で最小化したい。 学習率 $\eta$ を 1.0 と 0.001 のどちらに設定すべきか、 理由付きで答えよ。| 問題の性質 | 推奨手法 | scipy / 標準実装 | SSDSE での例 |
|---|---|---|---|
| 凸 + 制約なし + 小規模 | BFGS / LBFGS | scipy.optimize.minimize(method='BFGS') | 線形回帰 MSE 最小化 |
| 凸 + 線形制約 | 線形計画 (LP) | scipy.optimize.linprog | 47 県の物資配送 |
| 凸 + 二次目的 | 二次計画 (QP) | cvxpy / qpsolvers | SVM 学習 / ポートフォリオ |
| 整数 + 凸 | 分枝限定 (B&B) | scipy.optimize.milp / Gurobi | 47 県のうち補助金対象 10 県選択 |
| 非凸 + 中規模 | 差分進化 / Basin-Hopping | differential_evolution / basinhopping | 複数極小を持つ尤度関数 |
| 非凸 + 大規模 (DL) | SGD / Adam | PyTorch / TensorFlow | DNN 学習(教材的に SSDSE で MLP 試行) |
このコードでやること:SSDSE-B-2026 の 47 都道府県データで scipy.optimize.linprog による「最小費用で 3 県以上カバーする物流拠点選択」LP を解く(簡易版)。
📥 入力:SSDSE-B-2026 の A1101 列(県人口)。 拠点 4 候補(北海道・東京・愛知・福岡)の各設置コスト $c$ と各県への到達コスト行列 $d_{ij}$ をシナリオデータとして与える。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import numpy as np from scipy.optimize import linprog # 拠点コスト c(4 拠点) c = np.array([200, 350, 280, 220], dtype=float) # 各県を最低 1 拠点でカバーする制約(簡易:3 県ずつ 4 拠点) # A_ub @ x <= b_ub を「-A_ub @ x <= -b_ub」で「カバー必要数 >= 3」に変換 A_ub = -np.array([ [1,1,0,0], # 県1: 北海道 or 東京 [0,1,1,0], # 県2: 東京 or 愛知 [0,0,1,1], # 県3: 愛知 or 福岡 ], dtype=float) b_ub = -np.ones(3) # 各県 >=1 拠点でカバー res = linprog(c=c, A_ub=A_ub, b_ub=b_ub, bounds=[(0,1)]*4, method='highs') print(f'最適コスト = {res.fun:.1f}') print(f'各拠点選択比率 = {np.round(res.x,2)}') |
📤 実行結果:
💬 結果の読み方:LP のリラクゼーション解では 北海道 と 愛知 の 2 拠点で全 3 県をカバー、 総コスト 480 が最適。 整数制約を厳密に課したい場合は scipy.optimize.milp へ切り替える。 SSDSE 規模では数十ミリ秒で解ける。
数理最適化を中心に、 線形計画 LP / 整数計画 IP / 二次計画 QP / 円錐計画 SOCP/SDP / 非線形最適化 / メタヒューリスティクスを、 線形・凸・整数の 3 軸で整理した概念マップ。
数理最適化は線形計画 (LP) ・整数計画 (IP) ・凸最適化に大別される。 SSDSE-B-2026 の県別予算配分問題なら LP、 施設配置なら IP で定式化できる。
数理最適化は単独の手法というより、 上流の問題定式化 (目的関数 + 制約)、 並列の線形計画/整数計画/凸最適化/メタヒューリスティクス、 下流の感度分析 + 双対変数解釈と組み合わせて使う。 ソルバ (Gurobi / CBC / scipy.optimize) の選定が分かれ目。
SSDSE-B-2026 を題材に、 47 都道府県の人口を制約として、 予算配分を線形計画で最適化する例なら、 上流で目的関数 (例: 総生産最大化) と制約 (予算 ≤ X) を定式化、 中段で scipy.optimize.linprog 実行、 下流で双対変数から「各制約の限界費用」を解釈する。
数理最適化の手法選択は、 (1) 目的関数と制約の線形性、 (2) 変数の離散/連続、 (3) 凸性、 で判断する。 線形 + 連続なら LP、 線形 + 整数なら ILP、 非線形凸なら QP / SOCP、 非凸ならメタヒューリスティクスへと階段的に進む。
SSDSE-B-2026 を題材にした例として「47 都道府県の輸送ルート最適化」を考える。 変数は離散 (どの県間に輸送路を引くか)、 目的は輸送距離最小化、 制約は需要充足。 これは典型的な ILP (施設配置問題) で PuLP + CBC ソルバで現実時間に解ける。 一方「人口推計の非線形回帰モデルのパラメータ調整」は非凸 NLP なので、 SciPy の differential_evolution か Optuna でメタヒューリスティクス的に解く。
数理最適化は、 定義だけを暗記しても分析では役に立たない。 SSDSE-B-2026のような都道府県データでは、 列の単位、 年度、 地域単位、 欠損、 外れ値、 標準化の有無が結果を大きく変える。 目的関数・制約・最適解の読み方という観点で、 入力から出力までを一続きに説明できることが重要である。
確認順序は、 (1) 何を入力にしたか、 (2) どの変換をしたか、 (3) どの数式または API が動いたか、 (4) 数値が何を意味するか、 (5) 何を意味しないか、 である。 この順序を崩すと、 数値は出ているのに解釈が曖昧な報告になる。 SSDSE-B-2026 の各列番号 (A1101 など) と単位を必ず併記し、 読み手が再現可能な情報を残す。
SSDSE-B-2026 の都道府県データは N=47、 列数 113。 OLS で行列 X'X を解くと条件数が 10⁶〜10⁸ になることがあり、 数値計算上は実質的に特異行列に近い。 凸性は保たれているが、 数値精度が劣化して係数推定値が暴れる。 これに気づかず「最適解」と称するのは危険。
対策は ① 列の 標準化・中心化、 ② 多重共線性のある列を除去、 ③ 正則化 (リッジ) を加える、 の 3 段階。 数値精度の検証として SVD の特異値スペクトルを確認する習慣を持つと、 結果の信頼性が一段上がる。 SSDSE-B-2026 の 113 列なら主要 10〜20 次元で 95% の分散を説明できることが多い。
機械学習の「学習」とは、 損失関数の最小化問題そのもの。 線形回帰は L=Σ(y-ŷ)² の最小化、 ロジスティック回帰は対数尤度の最大化、 ニューラルネットは 損失関数 を勾配法で下げる。 つまり機械学習エンジニアは知らず識らずのうちに数理最適化の利用者である。
違いは目的の性質: ML は汎化性能が目的で、 訓練損失そのものは最終評価ではない。 そのため 正則化 や交差検証で「過学習しない最適化」を組み立てる。 純粋な最適化 (例: 工場の配車計画) は損失=目的そのものなので、 汎化を考えなくてよい。 この区別が ML エンジニアとオペレーションズ・リサーチエンジニアの仕事の違い。
47 都道府県のような小規模データでは全勾配 (Batch Gradient Descent) で十分。 ところが ImageNet (1400 万枚) や Common Crawl (TB 級) では 1 epoch が長すぎる。 そこで Stochastic Gradient Descent (SGD) と Mini-batch SGD が登場した。 サンプル 1 件 (or 数十〜数千件) で勾配を概算し、 高速に更新する。
SGD は数学的には「期待値が等しい不偏推定量による勾配近似」と読める。 47 件の小規模問題でも、 計算負荷の都合で SGD を使うことはある。 学習率の自動調整つき Adam (Kingma 2014) は SGD の改良版で、 1 次・2 次モーメントを適応的に正規化する。 勾配降下法 / 勾配法 で深掘り。
最適化結果のレポートでは、 ① 目的関数の式、 ② 全制約式、 ③ ソルバ名とバージョン、 ④ 初期値と乱数シード、 ⑤ 反復回数と収束判定、 ⑥ 解の感度分析、 を 6 点セットで報告する。 これが欠けると再現できず、 学術論文では査読で必ず指摘される。
SSDSE-B-2026 のような公共データを使うときは特に重要で、 政策提言の根拠になる場合は監査可能性が問われる。 関連用語の 相関 / DataFrame / 標準化 / 交差検証 / 過学習 / 特徴量 に都度リンクを張ると読み手が追跡しやすい。
数理最適化の制約は、 多くの場合「暗黙の制約」として顧客の頭の中にしかない。 「総予算」「人数上限」「期限」「品質下限」「公平性」「政治的制約」を引き出すための質問リストを持っていると初期設計が速い。 例: 「予算は何円までですか」「最低でも保証すべき下限は何ですか」「禁止される組合せはありますか」。
SSDSE-B-2026 で「47 県への予算配分」を最適化する案件なら、 「47 県全てに最低額を保証するか」「地域ブロック (関東/関西) に予算上限があるか」「特定県への増額禁止 (政治的理由) があるか」を確認。 これらを最初に書き出さないと、 結果を提示してから「これじゃ使えない」と言われる。
「コストを下げつつ品質を上げたい」のような複数目的が衝突する問題が多目的最適化。 単一の最適解はなく、 Pareto 解集合 (一方を改善すると他方が悪化する解の集合) を全列挙する。 NSGA-II (Deb 2002) が事実上の標準で、 進化計算で Pareto フロントを近似する。
SSDSE-B-2026 の例: 47 県への予算配分で、 ① 出生率向上、 ② 県間格差是正、 ③ コスト最小、 の 3 目的があれば、 3 次元 Pareto フロントを描き、 意思決定者が好みの点を選ぶ。 重みづけ和 (Σw_i × 目的_i) で 1 目的化する方法もあるが、 重みの決定自体が問題なので Pareto 解で選択肢を提示する方が誠実。
目的関数の評価が高コスト (1 回 1 時間など) のとき、 全数評価は無理。 ベイズ最適化 (BO) は獲得関数 (Expected Improvement・Upper Confidence Bound) を最大化する次の評価点を賢く選び、 数十〜百回の評価で最適解付近に到達する。 ハイパーパラメータ調整・実験計画・物理シミュレーションで重宝される。
Optuna・scikit-optimize・GPyOpt が代表的なライブラリ。 SSDSE-B-2026 を使う ML モデルのハイパラ調整で BO を使うのは現代の標準。 ハイパーパラメータ / ハイパラ調整 を参照。
LP・MIP のオープンソースソルバは CBC・GLPK・HiGHS、 商用は Gurobi・CPLEX・Xpress。 数百変数まではオープンソースで十分、 数千〜数万変数なら商用が圧倒的に速い (1000 倍以上差がつく場合も)。 アカデミア向け無償ライセンスがある (Gurobi Academic License)。
非凸 NLP は IPOPT・KNITRO、 大規模分散は Mosek。 メタヒューリスティクスは scipy.optimize.differential_evolution・pymoo・DEAP。 PuLP・CVXPY・Pyomo は「モデリング言語」で、 ソルバを抽象化して切替可能にする。 SSDSE-B-2026 規模なら PuLP + CBC で十分。
数理最適化の現場で多発する失敗: ① 非凸問題を凸と思い込んで局所解を最適解と称する、 ② 列スケールが揃わず勾配法が発散、 ③ 制約を 1 つ書き忘れて非現実的な解、 ④ 整数制約を緩めた連続解を整数に丸めて infeasible、 ⑤ シードを変えると結果が大きく異なるのに 1 回だけの結果を報告。
対策は ① 凸性の確認 (Hessian の半正定値性チェック)、 ② 入力前に 標準化、 ③ 制約リストをステークホルダーと書面で確認、 ④ MIP は専用ソルバで解く、 ⑤ 多シード実行と統計報告。 数理最適化は「結果は正しい」と思った瞬間が一番危険、 という業界の格言がある。
2 変数の 線形計画(LP) を手で動かして、 制約付き最適化を体感する。 目的関数 $z = c_1 x + c_2 y$ を 最大化 し、 実行可能領域(緑の多角形)は 3 つの線形制約と $x,y \ge 0$ で決まる。 目的関数の係数 $(c_1,c_2)$ を変えると最適解(赤い点)が 別の頂点へジャンプ する。 制約の右辺を動かすと領域と最適値が変わる。 青い 等高線(目的関数の等値線) が最適頂点でちょうど実行可能領域に 接する ことに注目してほしい。 これが「線形計画の最適解は必ず頂点で達成される」という定理の可視化である。
図の中の 矢印の先の丸(青緑) をドラッグ(マウス/タッチ対応)すると、 目的関数の向き $(c_1,c_2)$ を直接動かせる。 スライダーでも同じ値を変えられる。
操作のヒント: (1) c₂ を大きくすると最適点が y 軸寄りの頂点へ移る。 (2) c₁ を大きくすると x 軸寄りの頂点へ移る。 (3) 制約C(r₃)を上限制約より大きくすると領域が消え、 実行不能(infeasible) になる。 (4) 頂点の座標や単位に実データを載せれば、 「47 都道府県への資源配分」のような現実の LP がそのままこの図になる。
最適化とは「動かせる変数(決定変数)を、 守るべき条件(制約)を破らない範囲で動かし、 良し悪しの尺度(目的関数)を最良にする点を探す」操作である。 線形計画では制約が直線で囲む多角形(実行可能領域)を作り、 目的関数は平らな斜面になる。 斜面を一番高い方へ押し上げていくと、 必ず多角形の 角(頂点) で止まる。 だから解法は「全部の頂点を調べる」あるいは「頂点から頂点へ賢く移動する(シンプレックス法)」に帰着する。 上の図で等高線が頂点に接する瞬間が、 まさにこの「止まる」場面だ。
凸最適化: 実行可能領域が凸集合で目的関数が凸なら、 局所最適 = 大域最適が保証され、 確実に解ける。 線形計画・二次計画・Ridge 回帰はここに入る。 機械学習の「学習」の多くは 損失関数 の最小化であり、 凸なら安心、 非凸なら工夫が要る(機械学習 全般)。
ラグランジュ乗数: 等式制約 $h_j(x)=0$ を目的関数に $\lambda_j h_j(x)$ として足し込み、 制約付き問題を制約なし問題に変換する道具。 不等式制約まで一般化すると KKT 条件 になり、 最適点で「目的の勾配が制約の勾配の重ね合わせで打ち消される」という幾何が現れる。 上の図で等高線が制約線に接する状況が、 まさに勾配が平行になる(=乗数が正の)瞬間である。 罰則を足して解を単純化する発想は 正則化 と地続きだ。
双対(duality): どの LP にも対になる「双対問題」があり、 その解(双対変数・シャドープライス)は「制約を 1 単位ゆるめると目的値がどれだけ改善するか」を表す。 上の図で制約A・Bの右辺スライダーを少し動かしたときの最適値の変化率が、 その制約のシャドープライスに相当する。 感度分析はこの双対解の読み方に他ならない。
数理最適化は、 突き詰めれば 「決定変数」「目的関数」「制約」の 3 要素を書き下す作業に尽きる。 決定変数 $x$ は自分が動かせるつまみ(配分量・係数・on/off)、 目的関数 $f(x)$ は良し悪しを測る 1 本の物差し(最小化または最大化)、 制約 $g_i(x)\le 0,\ h_j(x)=0$ は破ってはいけない縛り。 この 3 つが揃った瞬間に、 曖昧な「なるべく良くしたい」が 解ける問題に変わる。 逆に言えば、 3 要素のどれか 1 つでも言語化できないうちは、 まだ最適化を始められない。
| 問題の型 | 見分け方 | 最適解の性質 |
| 線形(LP) | 目的も制約も 1 次式のみ | 最適解は実行可能領域の頂点で達成 |
| 非線形(NLP) | 2 乗・積・log などが混じる | 凸なら 1 点、 非凸なら複数の候補 |
| 整数(IP/MIP) | 変数が整数・0/1 に限定 | 格子点の中の最良点。 探索が難しい |
| 凸 | 目的が凸関数・領域が凸集合 | 局所最適 = 大域最適が保証 |
最適解の条件(何をもって「解けた」とするか)。 制約が無ければ、 なめらかな関数の最小点では 勾配がゼロ($\nabla f(x^\ast)=0$)で、 かつ 2 階微分(ヘシアン)が半正定値。 制約が有れば、 目的の勾配が効いている制約の勾配の重ね合わせで打ち消されるという KKT 条件が成り立つ。 上のウィジェットで等高線が最適頂点にちょうど接する図が、 まさにこの「勾配が制約と平行になる」瞬間の可視化である(詳しくは 連続最適化 / 大域的最小)。
SSDSE-B-2026(2023 年・47 都道府県)の I510120(一般病院数)と A1101(総人口)を実測で使い、 「限られた一般病院をどう配れば人口あたりの地域格差が最小になるか」を配分最適化として書く。 全国の一般病院は実測 7,065 施設、 総人口は 124,353,000 人。 決定変数は各県の配分数 $x_i$、 等式制約は $\sum_i x_i = 7065$、 目的関数は「人口 1 万人あたり病院数のばらつき(分散)を最小化」。 分散を最小化する解は、 全県を人口 1 万人あたり $7065/124353000\times10000 = 0.568$ 施設にそろえる均等配分になる。
| 都道府県 | 総人口 A1101 | 一般病院数 I510120(実測) | 1 万人あたり | 均等化後(目標 0.568) |
| 神奈川県 | 9,229,000 | 289 | 0.313(最少) | 524(+235) |
| 東京都 | 14,086,000 | 588 | 0.417 | 800(+212) |
| 高知県 | 666,000 | 107 | 1.607(最多) | 38(−69) |
→ 全国平均 1 万人あたり 0.568 施設に対し、 実測の県別値は 神奈川 0.313 〜 高知 1.607 と最大 5.1 倍の開きがあり(単純平均 0.690、 標準偏差 0.274)。 「均等配分」は数式上の最適解だが、 現実には病院を物理的に移せない・救急到達時間・面積あたり需要という制約が抜けている点に注意する。 これは「目的関数の設計しだいで結論が変わる」ことと「モデル化の誤り」の実例でもある(下の落とし穴も参照)。 なお、 この均等化はあくまで格差の数値化のための思考実験であり、 実際の医療政策上の提言ではない。
最適化は「解が出た」ことと「正しく解けた」ことが別物である。 数値が返っても、 下の落とし穴のどれかを踏んでいれば結論は崩れる。 とくに 非凸・実行不能・整数・モデル化・スケールの 5 つは事故が多い。
| 落とし穴 | 症状 | 対処 |
| 局所最適(非凸) | 初期値・乱数シードで解が変わる。 偽の谷で止まる | 多点出発、 メタヒューリスティクス、 凸に定式化し直せないか検討 |
| 実行可能領域が空(infeasible) | 全制約を同時に満たす点が無くソルバが失敗 | 制約の矛盾を疑う。 どれを緩めるか(緩和)を決める |
| 非有界(unbounded) | 最大化なのに目的値が無限に伸びる | 制約の書き忘れ・向き間違いを探す |
| 整数計画の NP 困難 | 変数が増えると組合せ爆発で解けない | 緩和解+分枝限定、 良い定式化、 近似で妥協 |
| モデル化の誤り(定式化) | 数式は解けたが問いが現実とずれている | 3 要素を業務言語に戻して検算。 抜けた制約を洗い出す |
| スケール・数値安定性 | 単位が桁違いで発散・悪条件(条件数が巨大) | 事前に 標準化、 多重共線性の除去、 正則化 |
| 目的の設定ミス | 合計最大化・最低水準引き上げ・格差最小で結論が真逆 | 「何を最適とみなすか」を明文化し合意を取る |
| 制約の見落とし | 暗黙の縛り(下限・公平性・物理制約)が式に無い | ステークホルダーへの質問リストで制約を発掘 |
| 緩和の解釈違い | 整数を連続に緩めた解を最終解と誤認 | 緩和解は下界(最小化時)にすぎない。 丸めると実行不能になり得る |
上の一般病院の配分例で言えば、 「均等配分こそ最適」という結論は 目的の設定ミスと 制約の見落としが同時に起きやすい典型である。 格差(分散)最小化を目的に選んだ結果にすぎず、 到達時間や既存施設の移設不可という制約を入れれば解はまったく変わる。 機械学習の学習が非凸になる(損失関数を 勾配降下法で下げる)局面では、 局所最適の罠がそのまま再現するので、 初期値や 交差検証での再現性確認が欠かせない。
問題の型が決まれば、 使うべき解法もほぼ決まる。 以下は「線形 → 凸 → 整数 → 非凸」と難しくなる順に、 代表的な理論と道具を並べた地図である。
この地図の出発点は、 いつでも「最適化とは 3 要素を書き下すこと」に戻る。 型が線形なら LP、 凸なら安心、 整数や非凸なら分枝限定や近似へ、 と難易度の階段を降りる感覚をつかめば、 SSDSE-B-2026 のような小標本でも大規模な実務問題でも、 同じ言葉で最適化を語れる。 関連ページ: 連続最適化 / 組合せ最適化 / 大域的最小 / クラスタリング(K-means の初期値依存)。 なお 凸最適化・線形計画法・整数計画法・双対・KKT・単体法・内点法・分枝限定法・確率的最適化は本用語集に単独ページが無いため、 ここでは本文中の説明で補っている。