高斯牛顿法(C++实现)

#include <iostream>
#include <cmath>
#include <Eigen/Eigen>

using namespace Eigen;

#define ITER_STEP   (1e-5)
#define ITER_CNT    (100)

typedef void (*func_ptr)(const VectorXd &input, const VectorXd ¶ms, VectorXd &output);

void people_func(const VectorXd &input, const VectorXd ¶ms, VectorXd &output)
{
    double A = params(0, 0);
    double B = params(1, 0);

    for (int i = 0; i < input.rows(); i++) {
        output(i, 0) = A * exp(input(i, 0) * B);
    }
}

void get_jacobian(func_ptr func,
        const VectorXd &input,
        const VectorXd ¶ms,
        MatrixXd &output
        )
{
    int m = input.rows();
    int n = params.rows();

    VectorXd out0(m, 1);
    VectorXd out1(m, 1);
    VectorXd param0(n, 1); 
    VectorXd param1(n, 1); 

    //output.resize(m, n);

    for (int j = 0; j < n; j++) {
        param0 = params;
        param1 = params;
        param0(j, 0) -= ITER_STEP;
        param1(j, 0) += ITER_STEP;
        func(input, param0, out0);
        func(input, param1, out1);
        output.block(0, j, m, 1) = (out1 - out0) / (2 * ITER_STEP);
    }
}

void gauss_newton(func_ptr func,
        const VectorXd &inputs,
        const VectorXd &output,
        VectorXd ¶ms
        )
{
    int m = inputs.rows();
    int n = params.rows();

    // jacobian 
    MatrixXd jmat(m, n);
    VectorXd r(m, 1);
    VectorXd tmp(m, 1);

    double pre_mse = 0.0;
    double mse;

    for (int i = 0; i < ITER_CNT; i++) {
        mse = 0.0;
        func(inputs, params, tmp);
        r = output - tmp;
        get_jacobian(func, inputs, params, jmat);
        mse = r.transpose() * r;
        mse /= m;
        if (fabs(mse - pre_mse) < 1e-8) {
            break;
        }
        pre_mse = mse;
        VectorXd delta = (jmat.transpose() * jmat).inverse() * jmat.transpose() * r;
        printf ("i = %d, mse %lf.\n", i, mse);
        params += delta;
    }

    std::cout << "params:" << params.transpose() << std::endl;
}


int people_fit()
{
    // A * exp(B * x)
    VectorXd inputs(8, 1);
    inputs << 1, 2, 3, 4, 5, 6, 7, 8;
    VectorXd output(8, 1);
    output << 8.3, 11.0, 14.7, 19.7, 26.7, 35.2, 44.4, 55.9;

    VectorXd params(2, 1);
    params << 8, 0.7;
    gauss_newton(people_func, inputs, output, params);
    return 0;
}

void tri_func(const VectorXd &input, const VectorXd ¶ms, VectorXd &output)
{
    double A = params(0, 0);
    double B = params(1, 0);
    double C = params(2, 0);
    double D = params(3, 0);

    for (int i = 0; i < input.rows(); i++) {
        output(i, 0) = A*sin(B*input(i, 0)) + C*cos(D*input(i, 0));
    }
}

int tri_func_fit()
{
    // A * sin(Bx) + C * cos(Dx)
    double A = 5;
    double B = 1;
    double C = 10;
    double D = 2;

    const int smp_cnt = 100;
    VectorXd input(smp_cnt, 1);
    VectorXd output(smp_cnt, 1);
    VectorXd params(4, 1);

    params << 1, 1, 9, 1;

    for (int i = 0; i < smp_cnt; i++) {
        input(i, 0) = i;
        output(i, 0) = A*sin(B*input(i, 0))+C*cos(D*input(i, 0)) + (rand() % 255) / 255.0;
    }
    gauss_newton(tri_func, input, output, params);
    return 0;
}

int main()
{
    people_fit();
    //tri_func_fit();
}


高斯牛顿法是一种非线性最小二乘拟合的优化方法,可以用于曲线拟合。以下是使用C语言实现高斯牛顿法拟合曲线的一种方法: 假设有一个非线性模型 y = f(x,θ),其中θ是模型中的参数,我们要通过给定的数据集{(x1, y1), (x2, y2), …, (xn, yn)}来估计θ的值。 首先,需要定义一个目标函数J(θ),它表示给定数据点和当前参数θ时的误差平方和: J(θ) = Σi=1 to n(yi - f(xi,θ))^2 然后,我们需要求解J(θ)的最小值。高斯牛顿法就是通过迭代来逼近最小值的方法。 在每次迭代中,我们需要计算J(θ)关于θ的一阶导数和二阶导数。一阶导数可以使用数值方法(如有限差分)来计算,二阶导数可以通过对一阶导数进行求导得到。 然后,我们可以使用以下公式更新参数θ的值: θ = θ - (J'(θ) * J''(θ)^(-1)) 其中,J'(θ)是J(θ)的一阶导数向量,J''(θ)是J(θ)的二阶导数矩阵,^(-1)表示矩阵的逆。 最后,我们可以使用更新后的参数θ来计算拟合曲线上的点,并与原始数据进行比较,以评估拟合效果。 在C语言中,可以使用数值计算库(如GNU Scientific Library)来实现高斯牛顿法。以下是一个简单的示例代码: ``` #include <stdio.h> #include <gsl/gsl_multifit.h> double model_func(double x, const gsl_vector *coeff) { double a = gsl_vector_get(coeff, 0); double b = gsl_vector_get(coeff, 1); double c = gsl_vector_get(coeff, 2); return a * exp(-b * x) + c; } int main() { const int n = 10; // 数据点数目 double x[n] = {0.0, 0.1, 0.2, ..., 0.9}; // x坐标 double y[n] = {1.0, 0.9, 0.8, ..., 0.1}; // y坐标 gsl_vector *coeff = gsl_vector_alloc(3); // 参数向量 gsl_multifit_function_fdf f; f.f = &model_func; f.df = NULL; f.fdf = NULL; f.n = n; f.p = 3; f.params = NULL; gsl_multifit_fdfsolver *solver = gsl_multifit_fdfsolver_alloc(gsl_multifit_fdfsolver_lmder, n, 3); gsl_vector_set(coeff, 0, 1.0); gsl_vector_set(coeff, 1, 1.0); gsl_vector_set(coeff, 2, 1.0); gsl_multifit_fdfsolver_set(solver, &f, coeff); int iter = 0, status; do { iter++; status = gsl_multifit_fdfsolver_iterate(solver); if (status) { break; } status = gsl_multifit_test_delta(solver->dx, solver->x, 1e-4, 1e-4); } while (status == GSL_CONTINUE && iter < 100); gsl_vector_free(coeff); gsl_multifit_fdfsolver_free(solver); return 0; } ``` 该代码使用指数函数模型(y = a * exp(-b * x) + c)拟合给定的数据点,其中参数a、b、c是需要估计的。首先,我们定义了一个模型函数model_func,它接受一个x坐标和一个参数向量,返回对应的y坐标。然后,我们使用GNU Scientific Library中的gsl_multifit_fdfsolver_lmder函数创建了一个高斯牛顿法求解器,并使用gsl_multifit_fdfsolver_set函数初始化了参数向量和模型函数。接下来,我们使用gsl_multifit_fdfsolver_iterate函数迭代求解最小值,直到达到最大迭代次数或误差足够小。最后,我们释放了求解器和参数向量的内存。 需要注意的是,该代码仅仅是一个简单的示例,实际应用中需要根据具体问题进行修改和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值