(この記事はGemini 3.0 Pro (2025年12月01日)によって作成しました)
GlobalSearch は、MATLABのGlobal Optimization Toolboxの中で、「最も効率的でバランスが良い」 とされる大域的最適化ソルバーの一つです。
一言で言うと、「賢いマルチスタート(多点開始)手法」 です。
単にランダムな点から手当たり次第に計算を始めるのではなく、「見込みのある点」だけを厳選して局所探索(fmincon)を実行する ことで、計算時間を大幅に短縮しつつ大域解を見つけようとします。
1. GlobalSearchの仕組み(アルゴリズム)
GlobalSearch は、主に以下の2段階のプロセスで動作します。この仕組みを理解すると、なぜ「効率的」なのかが分かります。
-
散布探索 (Scatter Search) - 広範囲の偵察
- 探索空間内に多数の試行点(Trial Points)をばら撒きます。
- それぞれの点の関数値を評価します(この段階では最適化計算は行わず、値を見るだけです)。
-
フィルタリング - 有望な点の選抜
- すべての点から計算を始めるわけではありません。
- 評価した点の中から、「これまでに探索したことのない谷(Basin of Attraction)」にありそうで、かつ「関数値が低い(良い)」点 だけを選び出します。
- すでに探索済みの谷(局所解に収束しそうな場所)にある点は無視します。
-
局所探索 (
fmincon) の実行- 選抜された「見込みのある点」を初期値として、勾配法(
fmincon)を実行し、最小値を求めます。 - 最終的に見つかった複数の局所解の中で、最も良い(小さい)ものを大域的最適解として出力します。
- 選抜された「見込みのある点」を初期値として、勾配法(
2. メリットとデメリット
メリット
-
効率が良い:
MultiStart(全点から実行)に比べて、無駄な局所探索を省くため、計算時間が短いです。 -
滑らかな関数に強い: 内部で勾配法(
fmincon)を使うため、連続的で微分可能な関数であれば非常に高精度な解が得られます。 - 設定が楽: パラメータ調整が比較的少なく、デフォルト設定でも十分に機能します。
デデメリット
-
不連続な関数は苦手: 内部で使う
fminconが勾配(微分)を必要とするため、階段状の関数やノイズが激しい関数には不向きです(その場合はgaやparticleswarmが適しています)。 -
並列計算ができない: アルゴリズムの性質上、順次処理を行うため、Parallel Computing Toolboxを使った並列化ができません(並列化したい場合は
MultiStartを使います)。
3. MATLABでの実装手順
GlobalSearch を使うには、問題を記述した構造体(createOptimProblem)を作成し、それをソルバーに渡すという手順を踏みます。
以下は、複数の谷を持つ関数(Peaks関数に近いイメージ)の最小値を求めるサンプルコードです。
%% 1. 目的関数の定義(Rastrigin関数のような多峰性関数)
% 多数の谷(局所解)を持つ関数です
fun = @(x) x(1)^2 + x(2)^2 - 10*cos(2*pi*x(1)) - 10*cos(2*pi*x(2));
%% 2. 最適化問題の作成
x0 = [3, 3]; % あえて解から遠い、別の谷を初期値に設定してみます
lb = [-5, -5];
ub = [5, 5];
problem = createOptimProblem('fmincon', ...
'objective', fun, ...
'x0', x0, ...
'lb', lb, ...
'ub', ub);
%% 3. GlobalSearch オブジェクトの作成と実行
gs = GlobalSearch;
% 実行中のログを表示させたい場合は以下をコメントアウト解除
gs.Display = 'iter';
fprintf('GlobalSearchを実行中...\n');
[x_sol, f_sol] = run(gs, problem);
%% --- ここから可視化コード ---
% 1. 描画用のグリッドデータを作成
grid_num = 100; % 分解能
x_range = linspace(lb(1), ub(1), grid_num);
y_range = linspace(lb(2), ub(2), grid_num);
[X, Y] = meshgrid(x_range, y_range);
% 2. グリッド上の各点での関数値を計算
Z = zeros(size(X));
for i = 1:numel(X)
Z(i) = fun([X(i), Y(i)]);
end
% 3. プロットの作成
figure('Position', [100, 100, 1000, 500], 'Color', 'w');
% --- 左側:3Dサーフェスプロット(全体像) ---
subplot(1, 2, 1);
% surfcの戻り値(ハンドル)を変数 h_surf に受け取る
h_surf = surfc(X, Y, Z, 'EdgeColor', 'none');
hold on;
% プロットのハンドルもそれぞれ変数に受け取る
h_sol = plot3(x_sol(1), x_sol(2), f_sol + 20, 'rp', 'MarkerSize', 15, 'MarkerFaceColor', 'r');
h_start = plot3(x0(1), x0(2), start_val + 20, 'bo', 'MarkerSize', 10, 'MarkerFaceColor', 'b');
hold off;
title('3D景観:目的関数の全体像');
xlabel('x(1)'); ylabel('x(2)'); zlabel('f(x)');
colorbar;
colormap jet;
view(-30, 45);
% legend関数に「表示したいハンドル」を配列で渡す
% h_surf(1) が曲面本体を表します(h_surf(2)は等高線なので凡例から除外)
legend([h_surf(1), h_sol, h_start], '関数形状', 'GlobalSearchの解', '初期値(x0)', 'Location', 'best');
% --- 右側:等高線プロット(真上から見た図) ---
subplot(1, 2, 2);
contourf(X, Y, Z, 20); % 20段階の等高線
hold on;
% 解と初期値をプロット
plot(x_sol(1), x_sol(2), 'rp', 'MarkerSize', 15, 'MarkerFaceColor', 'r');
plot(x0(1), x0(2), 'bo', 'MarkerSize', 10, 'MarkerFaceColor', 'b');
hold off;
title('等高線:解の位置確認');
xlabel('x(1)'); ylabel('x(2)');
grid on;
axis square;
% 結果をコマンドウィンドウに表示
fprintf('\n--- 結果 ---\n');
fprintf('初期値 x0: [%.2f, %.2f]\n', x0(1), x0(2));
fprintf('大域的最適解: [%.4f, %.4f]\n', x_sol(1), x_sol(2));
fprintf('最小値: %.4f\n', f_sol);
4. どのような時に選ぶべきか?
- 関数: 滑らか(微分可能)である。
- 変数: 連続変数である(整数制約がない)。
-
状況:
- 単純な
fminconでは初期値によって結果が変わってしまい困っている。 -
MultiStartやgaほど計算時間をかけたくないが、ある程度信頼できる大域解が欲しい。
- 単純な
まとめると、「とりあえず大域解を求めたいが、関数が滑らかで微分可能」というケースにおける【第一選択肢】として非常に優秀なソルバーです。
もし、扱っている関数が「シミュレーション結果でノイズが含まれる」場合や「微分不可能」な場合は、別のアプローチが良いかもしれません。