首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 非線性最優化實戰:從最小二乘到 GNU GSL 多維極小化完整指南

     編輯:關於C++

問題定義

給定離散資料點 (Xᵢ, Yᵢ),尋找參數向量 θ 使誤差函數最小化:

min E(θ) = Σ [f(Xᵢ; θ) - Yᵢ]² 或 Σ |f(Xᵢ; θ) - Yᵢ|

s.t. θ ∈ [θ_lower, θ_upper]

線性問題:SVD 奇異值分解

模型 Y = Aθ (線性)。
A = UΣVᵀ → θ = VΣ⁻¹UᵀY (偽逆)。
優點:解析解、全域最優、無迭代。
缺點:僅限線性模型。

非線性問題:GNU GSL 多維極小化

GSL (GNU Scientific Library) 提供 gsl_multimin_fminimizer 框架,支援 Nelder-Mead 單純形法、共軛梯度法等無導數/有導數演算法。

完整 GSL 使用範本


#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 常數)類型適用場景優點缺點
nmsimplex2Nelder-Mead 單純形無導數、低維 (<10)、非凸鲁棒、不需導數高維慢、易陷局部最優
conjugate_frFletcher-Reeves 共軛梯度有導數、中高維、平滑凸收斂快、記憶體少需導數函數
conjugate_prPolak-Ribiere 共軛梯度同上,非二次函數更好實務表現優於 FR需導數
bfgs2BFGS 拟牛頓有導數、高精度需求超線性收斂記憶體 O(n²)

關鍵實務建議

  1. 參數縮放:不同量級參數 (如 1e-3 vs 1e3) 必須正規化或設定對應 ss 步長,否則單純形變形極端。
  2. 多起點搜尋:非凸問題從多組隨機初始點啟動,取全域最優。
  3. 邊界處理:GSL 無內建邊界,誤差函數內加入懲罰項 if (phi) return 1e100;。
  4. 數值穩定:誤差函數避免減法災難,用 hypot、log1p 等穩定函數。
  5. 編譯連結:g++ main.cpp -lgsl -lgslcblas -lm。

替代方案

  • Ceres Solver (Google) — 自動微分、強大邊界、失權最小二乘、多線程。
  • NLopt — 統一介面整合 20+ 演算法 (COBYLA, BOBYQA, MMA 等)。
  • Eigen + 自寫 LM — 輕量、無外部依賴、適合嵌入式。

總結:非線性最優化無銀彈。**理解問題幾何性質 (凸/非凸、維度、導數可得性) 選擇演算法**,配合參數縮放、多起點、邊界懲罰,才能在工程實務中穩健收斂。GSL 是 C/C++ 經典選擇,現代專案建議評估 Ceres/NLopt。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved