目录
一、前言
二、主要函数分析
2.1 lsfitcreatef
2.2 lsfitsetcond
2.3 lsfitfit
2.4 lsfitresults
三、基础代码实现
3.1 定义待拟合函数
3.2 数据拟合
四、可视化代码实现
4.1 拟合h文件
4.2 拟合cpp文件
4.2 代码实验
一、前言
本文记录基于Alglib进行非线性最小二乘拟合的实现方法。此外借用qwt图表库进行图表部分的显示。具体qwt配置方法可见之前写的文章——QWT+Qt Creator+MSVC的配置与使用。Alglib 是一个广泛应用于数值分析和数据处理的 C++ 库,它提供了丰富的数学函数和算法,涵盖了多个领域。
开发环境:
Qt版本:Qt 5.12.0
Qt Creator版本:Qt Creator 4.8.0
MSVC版本:MSVC 2017_64
QWT版本:QWT 6.3.0
Alglib版本:ALGLIB 4.03.0
二、主要函数分析
2.1 lsfitcreatef
alglib::lsfitcreatef 函数属于 Alglib 库中的最小二乘拟合(Least Squares Fitting)相关模块。它的主要作用是初始化一个最小二乘拟合的任务状态,为后续进行具体的拟合计算做准备,比如通过后续调用其他相关函数来完成诸如线性拟合、非线性拟合等不同类型的最小二乘拟合操作,以找到最适合给定数据点的函数模型参数。
void lsfitcreatef(const real_2d_array &x, //自变量x,二维数组const real_1d_array &y, //因变量y,一维数组const real_1d_array &c, //参数c,一维数组const double diffstep, //数值微分步长lsfitstate &state, //库中专门定义的用于记录最小二乘拟合任务状态信息的一种类型const xparams _xparams = alglib::xdefault);
其中自变量x是一个二维数组,如果要拟合的函数是多元函数, 则每一列代表一个自变量数据。const xparams _xparams是库中定义的一种参数结构体类型,它用于传递一些额外的、与自变量相关的配置参数或者约束条件等信息,这些信息有助于更精细地定制最小二乘拟合任务,使其能更好地适应不同的应用场景和数据特点。
2.2 lsfitsetcond
alglib::lsfitsetcond的主要作用是设定非线性最小二乘拟合的停止条件。
void lsfitsetcond(const lsfitstate &state, //它指代一个用于存储算法状态的结构。const double epsx, //基于迭代过程中一些向量的欧几里得范数(Euclidian norm)来定义停止条件的关键参数。const ae_int_t maxits, //它代表最大迭代次数。const xparams _xparams = alglib::xdefault);
2.3 lsfitfit
alglib::lsfitfit函数主要用于启动非线性拟合器的迭代过程。在非线性最小二乘拟合等相关任务中,通过调用这些函数,并传入相应的参数,就能驱动拟合算法依据给定的数据和相关计算规则不断迭代,以找到合适的拟合参数,使得拟合函数尽可能好地符合实际的数据情况。
void lsfitfit(lsfitstate &state, //它指代一个用于存储算法状态的结构。void (*func)(const real_1d_array &c, const real_1d_array &x, double &func, void *ptr),void (*rep)(const real_1d_array &c, double func, void *ptr) = NULL,void *ptr = NULL,const xparams _xparams = alglib::xdefault);
- void (*func)是一个回调函数(callback),用于计算给定自变量
x
处的函数值。- void (*rep)是一个可选的回调函数,它会在每次迭代完成后被调用。其作用主要是用于在迭代过程中做一些额外的信息记录、状态展示或者用户自定义的一些与迭代相关的操作等。同样是一个回调函数,它的作用是在给定自变量
x
处,既要计算出函数(或价值函数)值,还要计算出函数关于待拟合参数的梯度(gradient
)值。- *ptr是一个可选的指针,它可以被传递给这些回调函数。
2.4 lsfitresults
alglib::lsfitresults的作用是获取关于非线性最小二乘拟合所得到的结果相关情况。
void lsfitresults(const lsfitstate &state, ae_int_t &info, real_1d_array &c, lsfitreport &rep, //这是一个包含了多个字段的结构体,用于呈现关于此次优化(拟合)的详细报告信息,const xparams _xparams = alglib::xdefault);
Info 参数:
- 不同的取值对应不同的拟合结果状态,具体含义如下:
- -8 值:表示优化器在目标函数和 / 或其梯度中检测到了 NAN(非数字,比如 0/0 等情况会出现)或者 INF(无穷大,像对数函数中自变量取不恰当值时可能出现)这样的异常值。
- -7 值:意味着梯度验证失败了。
- -3 值:表示存在不一致的约束条件。
- 2 值:代表相对步长不再超过 EpsX 这个设定值了。
- 5 值:说明已经执行了 MaxIts 步(最大迭代次数)的迭代操作。
- 7 值:表示设定的停止条件太严格了,进一步的拟合改进已经变得不可能。
三、基础代码实现
3.1 定义待拟合函数
void function_cx_1_func(const real_1d_array &c, const real_1d_array &x, double &func, void *ptr)
{func = exp(-c[0]*pow(x[0],2));
}
3.2 数据拟合
//引入原始数据
real_2d_array x = "[[-1],[-0.8],[-0.6],[-0.4],[-0.2],[0],[0.2],[0.4],[0.6],[0.8],[1.0]]";
real_1d_array y = "[0.223130, 0.382893, 0.582748, 0.786628, 0.941765, 1.000000, 0.941765, 0.786628, 0.582748, 0.382893, 0.223130]";
//参数值猜想
real_1d_array c = "[0.3]";
//拟合参数设定
double epsx = 0.000001;
ae_int_t maxits = 0;
lsfitstate state;
lsfitreport rep;
double diffstep = 0.0001;
alglib::ae_int_t info = 5;
//进行非线性拟合
alglib::lsfitcreatef(x, y, c, diffstep, state);
alglib::lsfitsetcond(state, epsx, maxits);
alglib::lsfitfit(state, function_cx_1_func);
alglib::lsfitresults(state,info, c, rep);
四、可视化代码实现
4.1 拟合h文件
注:MyQWTChart类为基于QWT实现的自定义类。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QVector>
#include <QHBoxLayout>
#include "Dependence/alglib/alglibinternal.h"
#include "Dependence/alglib/ap.h"
#include "Dependence/alglib/interpolation.h"
#include "Dependence/alglib/linalg.h"
#include "Dependence/alglib/optimization.h"
#include "MyQWTChart.h"namespace Ui {
class MainWindow;
}
using namespace alglib;
class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);~MainWindow();MyQWTChart* m_pSourceQwt;MyQWTChart* m_pFitQwt;QHBoxLayout* m_pLayout;QVector<double> m_vSourceX;QVector<double> m_vSourceY;QVector<double> m_vFitX;QVector<double> m_vFitY;
private slots:void on_pb_Fit_clicked();private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H
4.2 拟合cpp文件
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);m_pSourceQwt = new MyQWTChart(this,"X","Y");m_pSourceQwt->AddCurve("原始数据",MyQWTChart::e_Ellipse,Qt::blue);m_pFitQwt = new MyQWTChart(this,"X","Y");m_pFitQwt->AddCurve("拟合曲线",MyQWTChart::e_Line,Qt::red);m_pLayout = new QHBoxLayout;m_pLayout->addWidget(m_pSourceQwt);m_pLayout->addWidget(m_pFitQwt);ui->gb_Show->setLayout(m_pLayout);
}MainWindow::~MainWindow()
{delete ui;
}
void function_cx_1_func(const real_1d_array &c, const real_1d_array &x, double &func, void *ptr)
{func = exp(-c[0]*pow(x[0],2));
}
void MainWindow::on_pb_Fit_clicked()
{m_vSourceX.clear();m_vSourceY.clear();real_2d_array x = "[[-1],[-0.8],[-0.6],[-0.4],[-0.2],[0],[0.2],[0.4],[0.6],[0.8],[1.0]]";real_1d_array y = "[0.223130, 0.382893, 0.582748, 0.786628, 0.941765, 1.000000, 0.941765, 0.786628, 0.582748, 0.382893, 0.223130]";for(int i=0;i<x.rows();i++){m_vSourceX.push_back(x[i][0]);m_vSourceY.push_back(y[i]);}real_1d_array c = "[0.3]";double epsx = 0.000001;ae_int_t maxits = 0;lsfitstate state;lsfitreport rep;double diffstep = 0.0001;alglib::ae_int_t info = 5;alglib::lsfitcreatef(x, y, c, diffstep, state);alglib::lsfitsetcond(state, epsx, maxits);alglib::lsfitfit(state, function_cx_1_func);alglib::lsfitresults(state,info, c, rep);m_vFitX.clear();m_vFitY.clear();for(int i=1;i<=2000;i++){double x = double(i/1000.0)-1;m_vFitX.push_back(x);alglib::real_1d_array xData;xData.setlength(1);xData[0] = x;double y;function_cx_1_func(c,xData,y,NULL);m_vFitY.push_back(y);}m_pFitQwt->SetData(0,m_vFitX,m_vFitY);m_pSourceQwt->SetData(0,m_vSourceX,m_vSourceY);
}
4.2 代码实验