Rで 意思決定:非線形計画法 を試みます。
本ポストはこちらの続きです。

1. 非線形計画法(NLP)の解説
概要
非線形計画法(NLP)は、線形計画法(LP)をより一般化したもので、目的関数または制約条件の少なくとも一つが非線形(二次関数、分数関数、指数関数など)の数式で表される最適化問題です。
現実世界の問題は、必ずしも直線的な関係だけでモデル化できるとは限りません。例えば、投資のリスク(分散)は投資額の二乗に比例したり、生産量が増えると効率が上がってコストの伸びが鈍化したり(またはその逆)することがあります。こうした複雑な関係性を扱うことができるのが非線形計画法です。
線形計画法(LP)との主な違いと難しさ
項目 | 線形計画法 (LP) | 非線形計画法 (NLP) |
---|---|---|
数式 | 目的関数・制約条件がすべて線形(一次式) | 目的関数・制約条件のいずれかが非線形 |
実行可能領域 | 凸多角形(角ばった領域) | 曲線を含む領域(丸みを帯びた領域) |
最適解の場所 | 必ず領域の頂点に存在する | 領域の頂点、辺の上、あるいは内部のどこにでも存在しうる |
解の性質 | 最適解はただ一つ(大域的最適解) | 局所的最適解と大域的最適解が存在しうる |
最も大きな違いは「局所的最適解」の存在です。これは「その近傍では最適だが、領域全体で見ると最適ではない解」のことです。山登りに例えると、ある丘の頂上(局所的最適解)にたどり着いても、隣にはもっと高い山の山頂(大域的最適解)があるかもしれません。アルゴリズムによっては、この局所最適解に陥ってしまい、真の最適解を見つけられないことがあります。
具体例:ポートフォリオ最適化
金融工学における古典的な問題です。
- 目的: 複数の金融資産(株、債券など)に投資する際、投資全体のリスク(分散)を最小化したい。
- 制約:
- 期待されるリターン(収益率)は、ある目標値(例: 5%)以上でなければならない。
- 投資額の合計は、総資産額と一致しなければならない。
この問題では、リスク(分散)が各資産への投資額の二次関数で表されるため、非線形計画問題となります。
2. R言語による非線形計画法(NLP)のシミュレーション
ここでは、ある点からの距離を最小化する、という分かりやすい例でシミュレーションを行います。
問題設定
点 P(10, 5) があるとします。以下の制約条件を満たす領域内で、点Pに最も近い点 (x, y) を見つけてください。(※距離を最小化する代わりに、計算が簡単な距離の二乗を最小化します。結果は同じです)
- 目的関数(距離の二乗の最小化):
-
最小化 f(x, y) = (x - 10)^2 + (y - 5)^2
← 二次式なので非線形
-
- 制約条件:
x + y ≤ 10
x ≤ 8
- 境界条件(非負条件):
-
x ≥ 0
,y ≥ 0
-
シミュレーションの準備
# パッケージを読み込む
library(nloptr)
ステップ1:問題の定式化とRへの入力
nloptr
では、目的関数と制約条件をRのfunction
として定義します。
# 1. 目的関数 f(x)
# xはベクトルで、x[1]がx座標、x[2]がy座標に対応
<- function(x) {
eval_f return((x[1] - 10)^2 + (x[2] - 5)^2)
}
# 2. 目的関数の勾配
# 勾配は、各変数で偏微分したもの
<- function(x) {
eval_grad_f return(c(2 * (x[1] - 10), 2 * (x[2] - 5)))
}
# 3. 制約条件 g(x) <= 0
# nloptrは「<= 0」の形式で制約を定義する必要がある
<- function(x) {
eval_g # 制約1: x + y <= 10 => x + y - 10 <= 0
<- x[1] + x[2] - 10
constraint1 # 制約2: x <= 8 => x - 8 <= 0
<- x[1] - 8
constraint2
return(c(constraint1, constraint2))
}
# 4. 制約条件のヤコビアン(勾配行列)
<- function(x) {
eval_jac_g return(rbind(c(1, 1), c(1, 0)))
}
ステップ2:ソルバーの実行
nloptr()
関数に必要な情報を渡して、最適化を実行します。
# 初期値(計算の出発点)
<- c(1, 1)
x0
# 境界条件 (x >= 0, y >= 0)
<- c(0, 0) # lower bound
lb <- c(Inf, Inf) # upper bound
ub
# ソルバーの実行
<- nloptr(
solution x0 = x0,
eval_f = eval_f,
eval_grad_f = eval_grad_f,
eval_g_ineq = eval_g,
eval_jac_g_ineq = eval_jac_g,
lb = lb,
ub = ub,
opts = list("algorithm" = "NLOPT_LD_SLSQP", "xtol_rel" = 1.0e-8)
)
ステップ3:結果の確認と解釈
# --- 結果の表示 ---
<- solution$solution
optimal_point <- solution$objective
min_value
cat("非線形計画法のシミュレーション結果:\n")
cat("----------------------------------\n")
cat(sprintf("点P(10,5)に最も近い点 (x, y) は (%.4f, %.4f) です。\n", optimal_point[1], optimal_point[2]))
cat(sprintf("その時の目的関数の最小値(距離の二乗)は %.4f です。\n", min_value))
cat("メッセージ:", solution$message, "\n")
非線形計画法のシミュレーション結果:
----------------------------------
点P(10,5)に最も近い点 (x, y) は (7.5000, 2.5000) です。
その時の目的関数の最小値(距離の二乗)は 12.5000 です。
メッセージ: NLOPT_XTOL_REACHED: Optimization stopped because xtol_rel or xtol_abs (above) was reached.
この結果から、制約を満たす領域内で点 P(10, 5) に最も近い点は (7.5, 2.5) であることが分かりました。
ステップ4:結果の可視化
# グラフ描画エリアの設定
plot(NULL,
xlim = c(0, 12), ylim = c(0, 12), asp = 1,
xlab = "x", ylab = "y", main = "非線形計画問題の実行可能領域と最適解"
)grid()
# 実行可能領域をポリゴンで塗りつぶす
# 領域の頂点は (0,0), (8,0), (8,2), (0,10)
<- data.frame(
feasible_region x = c(0, 8, 8, 0),
y = c(0, 0, 2, 10)
)polygon(feasible_region$x, feasible_region$y, col = "lightgray", border = NA)
# 制約条件の境界線を描画
abline(a = 10, b = -1, col = "blue", lwd = 2) # x+y=10
abline(v = 8, col = "red", lwd = 2) # x=8
# 目的点 P(10,5) をプロット
points(10, 5, col = "purple", pch = 4, cex = 2, lwd = 2)
text(10, 5, labels = "P(10, 5)", pos = 4, col = "purple")
# 目的関数の等高線を描画
<- seq(0, 12, length.out = 100)
x_contour <- seq(0, 12, length.out = 100)
y_contour <- outer(x_contour, y_contour, function(x, y) (x - 10)^2 + (y - 5)^2)
z_contour contour(x_contour, y_contour, z_contour, levels = c(5, 12.5, 30, 50), add = TRUE, col = "orange")
# 最適解の点をプロット
points(optimal_point[1], optimal_point[2], col = "darkgreen", pch = 19, cex = 2)
text(optimal_point[1], optimal_point[2],
labels = sprintf("最適解\n(%.1f, %.1f)", optimal_point[1], optimal_point[2]),
pos = 2, col = "darkgreen"
)
# 凡例
legend("topright",
legend = c("制約: x+y<=10", "制約: x<=8", "実行可能領域", "目的点 P", "最適解", "目的関数の等高線"),
col = c("blue", "red", "lightgray", "purple", "darkgreen", "orange"),
lty = c(1, 1, NA, NA, NA, 1),
pch = c(NA, NA, 15, 4, 19, NA),
lwd = 2,
bg = "white"
)
Figure 1 から、以下を確認できます。
- 灰色の部分が、制約条件を満たす実行可能領域です。
- オレンジ色の円は、目的点Pを中心とする同心円(距離が等しい点の集まり)です。
- この円をPからだんだん広げていき、最初に実行可能領域に触れた点が、求める最適解となります。
- 今回の最適解 (7.5, 2.5) は、実行可能領域の辺の上(
x+y=10
の線上)に存在しており、線形計画法のように頂点にはないことが分かります。
以上です。