MATLAB 非线性隐函数拟合采坑记录(使用 fsolve solve nlinfit lsqcurvefit函数)

article/2025/9/9 8:03:28

MATLAB 非线性隐函数拟合采坑记录(使用 fsolve solve nlinfit lsqcurvefit函数)

  • 问题描述
  • 解决思路
  • 错误示范1
    • 代码思路
    • 原因解释
    • 模型更正
      • 更正模型1
      • 更正模型2
  • 错误示范2
    • 代码思路
    • 原因解释
    • 模型更正
      • 更正模型1
      • 更正模型2
  • 总结


问题描述

MATLAB的 nlinfit 和 lsqcurvefit 函数可以实现显函数非线性拟合,但是无法直接对隐函数进行拟合。所以,需要配合 fsolve 和 solve 函数建立自定义函数,然后实现隐函数的非线性拟合。

另外,理论上是可以实现多元非线性隐函数的拟合,但是目前我只尝试了形如 f ( x , y ) = 0 f(x,y)=0 f(x,y)=0 的隐函数,后续会对形如 f ( x 1 , x 2 , . . . , x n ) = 0 f(x_1,x_2,...,x_n)=0 f(x1,x2,...,xn)=0 多元函数进行尝试。


解决思路

由于我只是想解决拟合问题,但是并没有现成的数据给我拟合,所以我的解决思路是这样的:

  1. 人为定义一个形如 f ( x , y ) = 0 f(x,y)=0 f(x,y)=0 的隐函数(为了方便计算说明,这个隐函数也可以写成显函数的形式),随便定义一个函数如下所示。

f ( x 1 , x 2 ) = 1.2 x 1 3 + 3.6 x 2 5 = 0 f(x_1,x_2)=1.2x_1^3+3.6x_2^5=0 f(x1,x2)=1.2x13+3.6x25=0

  1. 然后手动设置 x 1 x_1 x1的范围,比如 x 1 = [ 0 , 0.01 , 0.02 , 0.03 , . . . , 4 ] x_1 = [0,0.01,0.02,0.03,...,4] x1=[0,0.01,0.02,0.03,...,4],并根据隐函数公式计算出 x 2 x_2 x2的值,然后给 x 1 x_1 x1 x 2 x_2 x2加上一定的噪音,这样就得到了拟合需要的数据。

上面两步的MATLAB代码如下所示

% x1 与 x2 的值计算
b = [1.2 3.6];
x = zeros(length(0:0.01:4),2);
x(:,1) = (0:0.01:4)';
x(:,2) = - ( b(1)/b(2) .* x(:,1).^3 ) .^ ( 1/5 );% 加入噪音
A = 0.01;
noise = (2 * rand(size(x)) - 1) * A;
x_noise = x + noise;
  1. 最后将加入噪音的 x 1 x_1 x1 x 2 x_2 x2 带入 nlinfit 或 lsqcurvefit 函数进行拟合,待拟合参数为 b b b ,预期拟合的结果为 b ( 1 ) = 1.2 , b ( 2 ) = 3.6 b(1)=1.2,b(2)=3.6 b(1)=1.2,b(2)=3.6,即预期的拟合结果应与我先前自己定义的隐函数的系数1.2和3.6相同。

错误示范1

由于在写代码过程中踩了不少的坑,所以现在这里记录一下两个错误的代码,以及发生错误的原因。

代码思路

由于进行拟合的两个函数只能直接拟合显函数,所以一开始我的思路是这样的:

  1. z = f ( x 1 , x 2 ) z=f(x_1,x_2) z=f(x1,x2) ,把原来的隐函数当做是一个二元显函数
  2. 然后令 z = 0 z=0 z=0 ,即函数值 z z z 恒等于0,这样即可输入 x 1 x_1 x1 x 2 x_2 x2 z z z 的值来实现隐函数的拟合。

这一思路的代码如下:

clear% 使用nlinfit 和 fsolve函数进行 非线性隐函数拟合的 错误示例1%% 建立隐函数,隐函数公式为b1*x1^3+b2*x2^5 = 0% x1 与 x2 的值计算
b = [1.2 3.6];
x = zeros(length(0:0.01:4),2);
x(:,1) = (0:0.01:4)';
x(:,2) = nthroot(- ( b(1)/b(2) .* x(:,1).^3 ), 5 );% 假设二元函数b1*x1^3+b2*x2^5 = z ,并令z=0
z = zeros(size(x,1),1);%% 加入噪音(-AA之间的随机数)
A = 0.01;
noise = (2 * rand(size(x)) - 1) * A;
x_noise = x + noise;%% 拟合% 创建待拟合模型的函数句柄
modelfun = @(beta,x)( beta(1).*x(:,1).^3 + beta(2).*x(:,2).^5 );% 待拟合参数的初始值(初始值的选取会在一定程度上影响拟合结果)
beta0 = [1;1];% 进行拟合,beta即拟合结果
beta = nlinfit(x_noise,z,modelfun,beta0)

运行几次后的结果如图所示

在这里插入图片描述
我们发现拟合的结果并不是1.2和3.6,反而非常的小,几乎等于0,这是为什么呢?


原因解释

这里拟合出的结果,隐函数的两个系数 b ( 1 ) b ( 2 ) b(1)\ b(2) b(1) b(2)基本都等于0,这是因为我把隐函数当成了函数值恒等于0的二元显函数,即 f ( x 1 , x 2 ) = z = 0 f(x_1,x_2)=z=0 f(x1,x2)=z=0

而在 f ( x 1 , x 2 ) = 1.2 x 1 3 + 3.6 x 2 5 = 0 f(x_1,x_2)=1.2x_1^3+3.6x_2^5=0 f(x1,x2)=1.2x13+3.6x25=0函数中,如果系数全等于0,那么等式仍然成立。

所以这导致了拟合的结果都是0。


模型更正

如果换一个待拟合的模型,那么会出现不同结果。

更正模型1

另外如果待拟合的模型函数是
f ( x 1 , x 2 ) = 1.2 x 1 3 + 3.6 x 2 5 + 1 = 0 f(x_1,x_2)=1.2x_1^3+3.6x_2^5+1=0 f(x1,x2)=1.2x13+3.6x25+1=0

那么拟合结果也会不准确,因为当 x 1 x 2 x_1\ x_2 x1 x2的值比较大时, x 1 3 x 2 5 x_1^3\ x_2^5 x13 x25的值远大于1,这就近似成 f ( x 1 , x 2 ) = 1.2 x 1 3 + 3.6 x 2 5 = 0 f(x_1,x_2)=1.2x_1^3+3.6x_2^5=0 f(x1,x2)=1.2x13+3.6x25=0,此时拟合的系数不会为0,但也不会是1.2和3.6。

而是满足 b ( 2 ) b ( 1 ) = 3.6 1.2 = 3 \frac{b(2)}{b(1)} =\frac{3.6}{1.2}=3 b(1)b(2)=1.23.6=3,且具体b(1)和b(2)的数值与常数项的取值有关,例如当常数项为1的时候,求解的结果基本稳定在 b ( 1 ) = 0.8 , b ( 2 ) = 2.4 b(1)=0.8,b(2)=2.4 b(1)=0.8,b(2)=2.4 左右。

更正模型2

如果待拟合的模型函数是
f ( x 1 , x 2 ) = 1.2 x 1 3 + 3.6 x 2 5 + 5 x 1 = 0 f(x_1,x_2)=1.2x_1^3+3.6x_2^5+5x_1=0 f(x1,x2)=1.2x13+3.6x25+5x1=0

那么最终拟合的结果会比较准确,即 b ( 1 ) = 1.2 , b ( 2 ) = 3.6 b(1)=1.2,b(2)=3.6 b(1)=1.2,b(2)=3.6


错误示范2

代码思路

之后我使用了fsolve函数来处理隐函数。代码如下

clear% 使用nlinfit/lsqcurvefit配合fsolve进行拟合,拟合失败案例1
% 拟合失败原因b1*v^3+b2*u^5 = 0中,b1和b2的值的比例只要等于1.2/3.6,等式就成立
% 所以拟合出来的曲线是对的,但是参数并不一定等于1.23.6
% 这也是报错"警告: 解处的 Jacobian 矩阵为病态,而且某
% 些模型参数的估计值可能不准确(不可识别)。进行预测时要谨慎。"的原因%% 原始数据 % 建立隐函数,隐函数公式为b1*u^3+b2*v^5 = 0
b = [1.2 3.6];
u = (0:0.01:4)';
% 使用这种方式开方,否则有可能返回复数值,而我们只需要实数值
v = nthroot(- ( b(1)/b(2) .* u.^3 ) , 5 );% 加入噪音(-AA之间的随机数,自变量u不加噪音,应变量v加噪音)
A = 0.01;
noise = (2 * rand(size(v)) - 1) * A;
v_noise = v + noise;%% 拟合% nlinfit中,beta是待拟合参数,u(x1)是自变量,函数输出结果v(x2_cal)是应变量,v_noise是fsolove用于求解方程组时候的初始值
modelfun = @(beta,x1)calfun(beta,x1,v_noise);% 拟合初始值
beta0 = [1;1];% 使用nlinfit拟合(用另一个拟合的时候就把这个注释掉)
beta = nlinfit(u,v_noise,modelfun,beta0)% 使用lsqcruvefit拟合(用另一个拟合的时候就把这个注释掉)
% beta = lsqcurvefit(modelfun,beta0,u,v_noise)%% 结果展示
figure
hold on% 原始数据
scatter(u,v_noise);% 拟合后的数据
v_fit = nthroot( -beta(1)/beta(2).*u.^3 , 5 );
plot(u,v_fit,'LineWidth',2);%% test (这一节用于验证fsolve求解出的结果是否正确,不用时注释)% b = [1.2 3.6];
% u = (0:0.01:4)';
% v = nthroot(- ( b(1)/b(2) .* u.^3 ) , 5 );
% 
% % 加入噪音(-AA之间的随机数)
% A = 0.01;
% noise = (2 * rand(size(v)) - 1) * A;
% v_noise = v + noise;
% 
% % 构建模型函数
% modelfun = @(beta,x1)calfun(beta,x1,v_noise);
% 
% % 计算结果
% v_test = modelfun([1.2 3.6],u);
% 
% % 通过画图验证结果
% figure
% plot(v_noise);
% figure
% plot(v_test)%% 由自变量计算隐函数的因变量
% x1 x2 分别为隐函数的两个变量
% 把x1当函数自变量,x2当函数因变量
% beta为需要拟合的参数
% x2_cal 为根据模型计算出的x2的值
% 输入x2的目的是,fsolve求解x2_cal的时候,需要使用x2当求解的初始值function x2_cal = calfun(beta,x1,x2)% 预分配
x2_cal = zeros(size(x1));% 设置fsolve求解器为不显示输出
% 'TolFun'设置成1e-16貌似没啥用
options = optimoptions('fsolve','Display','none','TolFun',1e-16);% 对每一个输入的u都计算出相应的v_cal
for i = 1:length(x1)% 设置求解的初始值(fsolve函数从设置的初始值开始找方程的解)% 把实验得到的v的值当做初始值。v0 = x2(i);% 设置求解方程组的函数句柄fun = @(v)hidefunction(beta,x1(i),v);% 求解v的值x2_cal(i) = fsolve(fun,v0,options);endend%% 隐函数模型
% b为需要拟合的参数
% x1和x2为变量
function F = hidefunction(b,x1,x2)F = b(1).*x1.^3 + b(2).*x2.^5;end

使用这种方法拟合的结果如下

在这里插入图片描述
在这里插入图片描述

我们可以从中看到,拟合会发出警告,提示我们雅各比矩阵为病态,并且拟合出来的参数也不对,但是拟合曲线却拟合的很好,这是为什么呢?


原因解释

观察我们拟合的模型,不难发现, b ( 1 ) b ( 2 ) b(1)\ b(2) b(1) b(2) 只要成任意比例都能满足等式成立,所以拟合的时候MATLAB会报警告,因为这个模型没有唯一解。所以返回任意一组解,即使系数值不是1.2 3.6,只要二者比例是对的,最终拟合的曲线就是正确的。

f ( x 1 , x 2 ) = 1.2 x 1 3 + 3.6 x 2 5 = 0 f(x_1,x_2)=1.2x_1^3+3.6x_2^5=0 f(x1,x2)=1.2x13+3.6x25=0

如果换一个待拟合的模型,那么会出现不同结果。

备注:在拟合 f ( x 1 , x 2 ) = 1.2 x 1 3 + 3.6 x 2 5 = 0 f(x_1,x_2)=1.2x_1^3+3.6x_2^5=0 f(x1,x2)=1.2x13+3.6x25=0时,使用 solve 或 lsqcurvefit 的结果是一样的因为系数 b ( 1 ) b ( 2 ) b(1)\ b(2) b(1) b(2) 只要成任意比例都能满足等式成立。

另外,使用solve求解速度非常慢,所以实际操作建议使用fsolve。


模型更正

更正模型1

另外如果待拟合的模型函数是
f ( x 1 , x 2 ) = 1.2 x 1 3 + 3.6 x 2 5 + 1 = 0 f(x_1,x_2)=1.2x_1^3+3.6x_2^5+1=0 f(x1,x2)=1.2x13+3.6x25+1=0

此时与上一种不使用fsolve和solve的方法不同,在这种情况下能得到正确的拟合参数,即 b ( 1 ) = 1.2 , b ( 2 ) = 3.6 b(1)=1.2,b(2)=3.6 b(1)=1.2,b(2)=3.6

更正模型2

如果待拟合的模型函数是
f ( x 1 , x 2 ) = 1.2 x 1 3 + 3.6 x 2 5 + 5 x 1 = 0 f(x_1,x_2)=1.2x_1^3+3.6x_2^5+5x_1=0 f(x1,x2)=1.2x13+3.6x25+5x1=0

那么最终拟合的结果也会比较准确,即 b ( 1 ) = 1.2 , b ( 2 ) = 3.6 b(1)=1.2,b(2)=3.6 b(1)=1.2,b(2)=3.6


总结

拟合结果不正确,不一定是对MATLAB的函数使用错误,也可能是模型本身设置的有问题。

综上所述,不使用fsolve和solve函数,所求的拟合结果有局限性,不推荐使用。solve函数的求解时间过长,也不推荐使用。而nlinfit和lsqcurvefit性能目前来看差不多。

所以,推荐使用fsolve配合nlinfit或lsqcurvefit进行非线性隐函数的拟合。


http://chatgpt.dhexx.cn/article/UTKkxDW1.shtml

相关文章

Matlab多元非线性函数拟合

看了多篇文章,觉得没有一篇比较全,且可以参照的多元非线性函数拟合,看了多篇文章后总结以下内容,主要以示例给出,希望能帮助到大家快速上手。 1.需要用到的函数语法 beta nlinfit(X, Y, modelfun, beta0) X为你的自…

MATLAB中用nlinfit做多元非线性拟合(回归)

MATLAB中有一个多元非线性拟合的功能是nlinfit 基本语法是: beta nlinfit(X,Y,modelfun,beta0) 式子左边的beta可以是一个向量,向量的元素就是要回归的模型中的参数。 式子右边,modelfun是要回归的函数形式。X是函数的自变量数据&#xff1b…

使用nlinfit函数进行拟合时出现Error using nlinfit>checkFunVals (line 611)

在使用nlinfit函数进行拟合时出错,内容如下: The function you provided as the MODELFUN input has returned Inf or NaN values.从第一行可以看出,由于赋予的初始值导致了函数生成了NaN(无解),所以整个回…

【数学建模】多元非线性回归nlinfit(Matlab代码实现)

目录 1 基本语法 2 算例及Matlab代码实现 2.1 算例 2.2 数据 2.3 Matlab代码实现 1 基本语法 2 算例及Matlab代码实现 2.1 算例 熔喷非织造材料是口罩生产的重要原材料,具有很好的过滤性能,其生产工艺简单、成本低、质量轻等特点,受到国…

Matlab 使用nlinfit 函数进行多元非线性回归,并且绘制曲线拟合的误差区间

Matlab 使用nlinfit 函数进行多元非线性回归,并且绘制曲线拟合的误差区间 一、前言二、nlinfit函数使用1、函数语法2、拟合示例: 三、误差阴影绘制四、整体源码五、思考参考博客 一、前言 这个也是最近我接到的一个小项目里的内容: 有一组数…

利用nlinfit函数实现数据非线性拟合

所谓“拟合”,指的是在已有一组实验数据的前提下,研究这组数据有怎样的函数关系——最终结果是从这一组看似漫无规律的数据点中“找出”能用数学表达式表示的规律。 用数学语言描述的拟合定义如下: 一个典型的数据拟合过程包括以下几个步骤&…

Matlab非线性拟合函数——nlinfit

我们平时最常用的非线性拟合函数还是多项式拟合,有一天学弟突然问了我nlinfit 这个函数,然后直接查询matlab官方文档,原来非线性函数还可以用这个函数,下面来看看matlab官方文档的说明: 英文?没关系,下面看一下中文用法: beta = nlinfit(X, Y, modelfun, beta0) beta:…

[MATLAB]非线性回归--自配函数(nlinfit)

当谈到非线性回归模型的时候,同学们应该紧密的将线性回归紧密结合在一起,因为非线性回归很容易过拟合。那我们从一个案例谈一下非线性 拿到题目看到一个变量x一个y,非线性问题步骤应该是这样子的: 画出散点图根据散点图确定须配…

dozer使用: list对象mapping 配置

记录dozer的使用,复杂类型配置。 文档:https://dozermapper.github.io/user-guide.pdf 参考地址:https://github.com/klvnnsrikanth/DozerMappingExample.git Demo 的目录结构: 不是集合的普通映射方式 Source 类: Destinatio…

java dozer map转对象_对象转换利器之Dozer

在Java的世界中,经常会涉及到需要在2个对象中进行转换,比如说: 调用SOAP Web服务,需要把自己的Domain对象转换为Soap服务的Jaxb对象请求, 在分层级SOA架构中,2个层级之间Domain对象的转换, 在分…

SpringBoot2.x 集成 Dozer

Dozer是Java Bean到Java Bean的映射器,它以递归的方式将数据从一个对象复制到另一个对象。通常,这些Java Bean将具有不同的复杂类型。它支持简单属性映射,复杂类型映射,双向映射,隐式显式映射,以及递归映射…

java dozer 官网,Dozer数据对象转换神器

首先,为什么要进行数据转换? 在一个分层的体系结构中,经常会使用DTO、PO、VO等封装数据,封装数据到特定的数据对象中,然而在很多情况下,某层内部的数据是不允许传递到其它层,不允许对外暴露的&a…

SpringBoot 整合 Dozer 映射框架

SpringBoot 整合 Dozer 映射框架 1. Dozer 介绍2. 为什么要使用映射框架 Dozer3. Dozer 映射框架的使用 1. Dozer 介绍 Dozer 是一个 Java Bean 到 Java Bean 的映射器,它递归地将数据从一个对象复制到另一个对象。Dozer 是用来对两个对象之间属性转换的工具&#…

Dozer的学习

Dozer的学习 此教程基于黑马程序员Java品达通用权限项目,哔哩哔哩链接:https://www.bilibili.com/video/BV1tw411f79E?p44 1.dozer介绍 Dozer是Java Bean到Java Bean映射器,它以递归方式将数据从一个对象复制到另一个对象。 dozer是用来对…

SpringBoot Dozer

文章目录 Dozerspringboot 配置 Dozer演示Dozer工具接口和实现类全面测试项目实战演示 Dozer Dozer是什么? Dozer是一个JavaBean映射工具库。 它支持简单的属性映射,复杂类型映射,双向映射,隐式显式的映射,以及递归映射。 它支…

Dozer的使用(整合springboot)

dozer是实体类与实体类之间转换的工具&#xff0c;下面是简单操作配置 引入依赖&#xff1a; <!--dozer 实体类转换工具--><dependency><groupId>net.sf.dozer</groupId><artifactId>dozer-spring</artifactId><version>5.5.1<…

【JAVA】Dozer 介绍及快速入门教程

文章目录 概述使用安装入门XML 映射注解映射SpringBoot 集成 结语 概述 Dozer 是什么? Dozer 是 Java Bean 到 Java Bean 的映射器&#xff0c;他以递归的方式将数据从一个对象复制到另一个对象。 它支持简单的属性映射&#xff0c;复杂类型映射&#xff0c;双向映射&#…

1.Dubbo教程

转自&#xff1a;https://blog.csdn.net/hellozpc/article/details/78575773 2. 什么是dubbo 2.1. 简介 DUBBO是一个分布式服务框架&#xff0c;致力于提供高性能和透明化的RPC远程服务调用方案&#xff0c;是阿里巴巴SOA服务化治理方案的核心框架&#xff0c;每天为2,000个服务…

黑马程序员Dubbo快速入门,Java分布式框架dubbo教程

分布式系统中的相关概念 &#xff08;一&#xff09;互联网项目架构目标-特点 &#xff08;二&#xff09;互联网项目架构目标-目标 &#xff08;三&#xff09;集群和分布式 &#xff08;四&#xff09;架构演进 Dubbo概述 &#xff08;一&#xff09;dubbo概述 1、dub…

Dubbo快速入门教程

Dubbo入门教程 1 、Dubbo概述 DUbbo架构 2、Dubbo快速入门 Zookeeper 安装传统的模块化开发Duboo入门小案例 修改Service模块代码修改WEB模块代码增加公共的接口模块 3、Dubbo高级特性 dubbo-admin管理平台 dubbo-adminm安装 一、dubbo-admin安装二、dubbo-admin简单使用 序…