C++最小二乘法拟合-(线性拟合和多项式拟合)

本文介绍了一种多项式拟合方法,该方法能够根据用户指定的阶次进行拟合,并提供了计算拟合后的误差指标,包括剩余平方和(SSE)、回归平方和(SSR)、均方根误差(RMSE)和确定系数(R-square)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

版权声明:本文为优快云博主「尘中远」的原创文章,遵循 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值