版权声明:本文为优快云博主「尘中远」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/czyt1988/article/details/21743595
在进行曲线拟合时用的最多的是最小二乘法,其中以一元函数(线性)和多元函数(多项式)居多,下面这个类专门用于进行多项式拟合,可以根据用户输入的阶次进行多项式拟合,算法来自于网上,和GSL的拟合算法对比过,没有问题。此类在拟合完后还能计算拟合之后的误差:SSE(剩余平方和),SSR(回归平方和),RMSE(均方根误差),R-square(确定系数)。
1.fit类的实现
先看看fit类的代码:(只有一个头文件方便使用)
这是用网上的代码实现的,下面有用GSL实现的版本
#ifndef CZY_MATH_FIT
#define CZY_MATH_FIT
#include <vector>
/*
尘中远,于2014.03.20
主页:http://blog.youkuaiyun.com/czyt1988/article/details/21743595
参考:http://blog.youkuaiyun.com/maozefa/article/details/1725535
*/
namespace czy{
///
/// \brief 曲线拟合类
///
class Fit{
std::vector<double> factor; ///<拟合后的方程系数
double ssr; ///<回归平方和
double sse; ///<(剩余平方和)
double rmse; ///<RMSE均方根误差
std::vector<double> fitedYs;///<存放拟合后的y值,在拟合时可设置为不保存节省内存
public:
Fit():ssr(0),sse(0),rmse(0){factor.resize(2,0);}
~Fit(){}
///
/// \brief 直线拟合-一元回归,拟合的结果可以使用getFactor获取,或者使用getSlope获取斜率,getIntercept获取截距
/// \param x 观察值的x
/// \param y 观察值的y
/// \param isSaveFitYs 拟合后的数据是否保存,默认否
///
template<typename T>
bool linearFit(const std::vector<typename T>& x, const std::vector<typename T>& y,bool isSaveFitYs=false)
{
return linearFit(&x[0],&y[0],getSeriesLength(x,y),isSaveFitYs);
}
template<typename T>
bool linearFit(const T* x, const T* y,size_t length,bool isSaveFitYs=false)
{
factor.resize(2,0);
typename T t1=0, t2=0, t3=0, t4=0;
for(int i=0; i<length; ++i)
{
t1 += x[i]*x[i];
t2 += x[i];
t3 += x[i]*y[i];
t4 += y[i];
}
factor[1] = (t3*length - t2*t4) / (t1*length - t2*t2);
factor[0] = (t1*t4 - t2*t3) / (t1*length - t2*t2);
//
//计算误差
calcError(x,y,length,this->ssr,this->sse,this->rmse,isSaveFitYs);
return true;
}
///
/// \brief 多项式拟合,拟合y=a0+a1*x+a2*x^2+……+apoly_n*x^poly_n
/// \param x 观察值的x
/// \param y 观察值的y
/// \param poly_n 期望拟合的阶数,若poly_n=2,则y=a0+a1*x+a2*x^2
/// \param isSaveFitYs 拟合后的数据是否保存,默认是
///
template<typename T>
void polyfit(const std::vector<typename T>& x
,const std::vector<typename T>& y
,int poly_n
,bool isSaveFitYs=true)
{
polyfit(&x[0],&y[0],getSeriesLength(x,y),poly_n,isSaveFitYs);
}
template<typename T>
void polyfit(const T* x,const T* y,size_t length,int poly_n,bool isSaveFitYs=true)
{
factor.resize(poly_n+1,0);
int i,j;
//double *tempx,*tempy,*sumxx,*sumxy,*ata;
std::vector<double> tempx(length,1.0);
std::vector<double> tempy(y,y+length);
std::vector<double> sumxx(poly_n*2+1);
std::vector<double> ata((poly_n+1)*(poly_n+1));
std::vector<double> sumxy(poly_n+1);
for (i=0;i<2*poly_n+1;i++){
for (sumxx[i]=0,j=0;j<length;j++)
{
sumxx[i]+=tempx[j];
tempx[j]*=x[j];
}
}
for (i=0;i<poly_n+1;i++){
for (sumxy[i]=0,j=0;j<length;j++)
{
sumxy[i]+=tempy[j];
tempy[j]*=x[j];
}
}
for (i=0;i<poly_n+1;i++)
for (j=0;j<poly_n+1;j++)
ata[i*(poly_n+1)+j]=sumxx[i+j];
gauss_solve(poly_n+1,ata,factor,sumxy);
//计算拟合后的数据并计算误差
fitedYs.reserve(length);
calcError(&x[0],&y[0],length,this->ssr,this->sse,this->rmse,isSaveFitYs);
}
///
/// \brief 获取系数
/// \param 存放系数的数组
///
void getFactor(std::vector<double>& factor){factor = this->factor;}
///
/// \brief 获取拟合方程对应的y值,前提是拟合时设置isSaveFitYs为true
///
void getFitedYs(std::vector<double>& fitedYs){fitedYs = this->fitedYs;}
///
/// \brief 根据x获取拟合方程的y值
/// \return 返回x对应的y值
///
template<typename T>
double getY(const T x) const
{
double ans(0);
for (size_t i=0;i<factor.size();++i)
{
ans += factor[i]*pow((double)x,(int)i);
}
return ans;
}
///
/// \brief 获取斜率
/// \return 斜率值
///
double getSlope(){return factor[1];}
///
/// \brief 获取截距
/// \return 截距值
///
double getIntercept(){return factor[0];}
///
/// \brief 剩余平方和
/// \return 剩余平方和
///
double getSSE(){return sse;}
///
/// \brief 回归平方和
/// \return 回归平方和
///
double getSSR(){return ssr;}
///
/// \brief 均方根误差
/// \return 均方根误差
///
double getRMSE(){return rmse;}
///
/// \brief 确定系数,系数是0~1之间的数,是数理上判定拟合优度的一个量
/// \return 确定系数
///
double getR_square(){return 1-(sse/(ssr+sse));}
///
/// \brief 获取两个vector的安全size
/// \return 最小的一个长度
///
template<typename T>
size_t getSeriesLength(const std::vector<typename T>& x
,const std::vector<typename T>& y)
{
return (x.size() > y.size() ? y.size() : x.size());
}
///
/// \brief 计算均值
/// \return 均值
///
template <typename T>
static T Mean(const std::vector<T>& v)
{
return Mean(&v[0],v.size());
}
template <typename T>
static T Mean(const T* v,size_t length)
{
T total(0);
for (size_t i=0;i<length;++i)
{
total += v[i];
}
return (total / length);
}
///
/// \brief 获取拟合方程系数的个数
/// \return 拟合方程系数的个数
///
size_t getFactorSize(){return factor.size();}
///
/// \brief 根据阶次获取拟合方程的系数,
/// 如getFactor(2),就是获取y=a0+a1*x+a2*x^2+……+apoly_n*x^poly_n中a2的值
/// \return 拟合方程的系数
///
double getFactor(size_t i){return factor.at(i);}
private:
template<typename T>
void calcError(const T* x
,const T* y
,size_t length
,double& r_ssr
,double& r_sse
,double& r_rmse
,bool isSaveFitYs=true
)
{
T mean_y = Mean<T>(y,length);
T yi(0);
fitedYs.reserve(length);
for (int i=0; i<length; ++i)
{
yi = getY(x[i]);
r_ssr += ((yi-mean_y)*(yi-mean_y));//计算回归平方和
r_sse += ((yi-y[i])*(yi-y[i]));//残差平方和
if (isSaveFitYs)
{
fitedYs.push_back(double(yi));
}
}
r_rmse = sqrt(r_sse/(double(length)));
}
template<typename T>
void gauss_solve(int n
,std::vector<typename T>& A
,std::vector<typename T>& x
,std::vector<typename T>& b)
{
gauss_solve(n,&A[0],&x[0],&b[0]);
}
template<typename T>
void gauss_solve(int n
,T* A
,T* x
,T* b)
{
int i,j,k,r;
double max;
for (k=0;k<n-1;k++)
{
max=fabs(A[k*n+k]); /*find maxmum*/
r=k;
for (i=k+1;i<n-1;i++){
if (max<fabs(A[i*n+i]))
{
max=fabs(A[i*n+i]);
r=i;
}
}
if (r!=k){
for (i=0;i<n;i++) /*change array:A[k]&A[r] */
{
max=A[k*n+i];
A[k*n+i]=A[r*n+i];
A[r*n+i]=max;
}
}
max=b[k]; /*change array:b[k]&b[r] */
b[k]=b[r];
b[r]=max;
for (i=k+1;i<n;i++)
{
for (j=k+1;j<n;j++)
A[i*n+j]-=A[i*n+k]*A[k*n+j]/A[k*n+k];
b[i]-=A[i*n+k]*b[k]/A[k*n+k];
}
}
for (i=n-1;i>=0;x[i]/=A[i*n+i],i--)
for (j=i+1,x[i]=b[i];j<n;j++)
x[i]-=A[i*n+j]*x[j];
}
};
}
#endif
GSL实现版本,此版本依赖于GSL需要先配置GSL,GSL配置方法网上很多,我的blog也有一篇介绍win + Qt环境下的配置,其它大同小异:http://blog.youkuaiyun.com/czyt1988/article/details/39178975
验证:
//主函数,这里将数据拟合成二次曲线
int main(int argc, char* argv[])
{
Fit fs;
double arry1[5] = { 0,0.25,0,5,0.75 };
double arry2[5] = { 1,1.283,1.649,2.212,2.178 };
double coefficient[5];
memset(coefficient, 0, sizeof(double) * 5);
vector<double> vx, vy;
for (int i = 0; i<5; i++)
{
vx.push_back(arry1[i]);
vy.push_back(arry2[i]);
}
//内部函数gauss_solve输出factor即为多项式系数
fs.polyfit(vx
, vy
, 2
, true);
cin.get();
return 0;
}
版权声明:本文为优快云博主「尘中远」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/czyt1988/article/details/21743595