給定離散資料點 (Xᵢ, Yᵢ),尋找參數向量 θ 使誤差函數最小化:
min E(θ) = Σ [f(Xᵢ; θ) - Yᵢ]² 或 Σ |f(Xᵢ; θ) - Yᵢ|
s.t. θ ∈ [θ_lower, θ_upper]
模型 Y = Aθ (線性)。
A = UΣVᵀ → θ = VΣ⁻¹UᵀY (偽逆)。
優點:解析解、全域最優、無迭代。
缺點:僅限線性模型。
GSL (GNU Scientific Library) 提供 gsl_multimin_fminimizer 框架,支援 Nelder-Mead 單純形法、共軛梯度法等無導數/有導數演算法。
#include
#include
// 1. 定義誤差函數 (介面要求)
double error_func(const gsl_vector *params, void *data) {
// 解包參數
double p1 = gsl_vector_get(params, 0);
double p2 = gsl_vector_get(params, 1);
// ... 取得資料指標
auto *xy = static_cast>*>(data);
double sum = 0;
for (auto [x, y] : *xy) {
double pred = model(x, p1, p2); // 你的模型函數
double err = pred - y;
sum += err * err; // 最小二乘
}
return sum;
}
// 2. 封裝 GSL 函數結構
gsl_multimin_function minex_func;
minex_func.f = error_func;
minex_func.n = 2; // 參數維度
minex_func.params = &data_points;
// 3. 選擇演算法並初始化
const gsl_multimin_fminimizer_type *T = gsl_multimin_fminimizer_nmsimplex2; // Nelder-Mead 改良版
gsl_multimin_fminimizer *s = gsl_multimin_fminimizer_alloc(T, 2);
// 4. 設定初始點與步長
gsl_vector *x = gsl_vector_alloc(2);
gsl_vector *ss = gsl_vector_alloc(2);
gsl_vector_set(x, 0, init_p1);
gsl_vector_set(x, 1, init_p2);
gsl_vector_set_all(ss, 0.01); // 初始步長 (對應收斂容忍度)
gsl_multimin_fminimizer_set(s, &minex_func, x, ss);
// 5. 迭代優化
int status;
size_t iter = 0;
do {
iter++;
status = gsl_multimin_fminimizer_iterate(s);
if (status) break;
double size = gsl_multimin_fminimizer_size(s);
status = gsl_multimin_test_size(size, 1e-12); // 收斂判斷
if (status == GSL_SUCCESS) {
printf("Converged at iter %zu: f=%.12f, size=%.3f\n", iter, s->fval, size);
// 取得最優參數
double best_p1 = gsl_vector_get(s->x, 0);
double best_p2 = gsl_vector_get(s->x, 1);
}
} while (status == GSL_CONTINUE && iter < 10000);
// 6. 釋放資源
gsl_vector_free(x);
gsl_vector_free(ss);
gsl_multimin_fminimizer_free(s);
| 演算法 (GSL 常數) | 類型 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|---|
nmsimplex2 | Nelder-Mead 單純形 | 無導數、低維 (<10)、非凸 | 鲁棒、不需導數 | 高維慢、易陷局部最優 |
conjugate_fr | Fletcher-Reeves 共軛梯度 | 有導數、中高維、平滑凸 | 收斂快、記憶體少 | 需導數函數 |
conjugate_pr | Polak-Ribiere 共軛梯度 | 同上,非二次函數更好 | 實務表現優於 FR | 需導數 |
bfgs2 | BFGS 拟牛頓 | 有導數、高精度需求 | 超線性收斂 | 記憶體 O(n²) |
ss 步長,否則單純形變形極端。if (phi) return 1e100; 。hypot、log1p 等穩定函數。g++ main.cpp -lgsl -lgslcblas -lm。總結:非線性最優化無銀彈。**理解問題幾何性質 (凸/非凸、維度、導數可得性) 選擇演算法**,配合參數縮放、多起點、邊界懲罰,才能在工程實務中穩健收斂。GSL 是 C/C++ 經典選擇,現代專案建議評估 Ceres/NLopt。