MATLAB函数 solve, vpasolve, fsolve, fzero, roots 功能和信息概览

求解函数 多项式型 非多项式型 一维 高维 符号 数值 算法
solve 支持,得到全部符号解 若可符号解则得到根 支持 支持 支持 当无符号解时

符号解方法:利用等式性质得到标准可解函数的方法

基本即模拟人工运算

vpasolve 支持,得到全部数值解 (随机初值)得到一个实根 支持 支持 $\times$ 支持 未知
fsolve 由初值得到一个实根 由初值得到一个实根 支持 支持 $\times$  支持

优化方法,即用优化方法求解函数距离零点最近,具体方法为信赖域方法。

包含预处理共轭梯度(PCG)、狗腿(dogleg)算法和Levenberg-Marquardt算法等

fzero 由初值得到一个实根 由初值得到一个实根 支持 $\times$  $\times$  支持

一维解非线性方程方法,二分法、二次反插和割线法的混合运用

具体原理见数值求解非线性方程的二分法、不动点迭代和牛顿法插值方法

roots 支持,得到全部数值解 $\times$ 支持 $\times$ $\times$  支持

特征值方法,即将多项式转化友矩阵(companion matrix)

然后使用求矩阵特征值的算法求得所有解(那是另外一个问题了)

  也就是说,之前写了几篇关于非线性求解的,如二分法、牛顿法(参见二分法、不动点迭代、牛顿法)、二次反插法(参见插值法),只有一个库函数用了类似的方法。

各函数用法详解

1. (符号/数值)解方程(组)函数 solve

  官方参考页:Equations and systems solver - MATLAB solve

  solve 是基本的用于符号解方程的内置函数,返回类型为符号变量矩阵($m\times n$ sym)。当无法符号求解时,抛出警告并输出一个数值解。基本形式为:

solve(eqn, var, Name, Val);  % eqn为符号表达式/符号变量/符号表达式的函数句柄, var为未知量; Name为附加要求,Val为其值

  可以用solve解一维方程。对于多项式,solve可以返回其所有值。

func1 = @(x)x^3 - 20*x^2 - 25*x + 500;    % 创建函数句柄.句柄中的变量不属符号变量,不需要定义!
syms x exp1; % 定义符号变量 x, exp1;
exp1 = x^3 - 20*x^2 - 25*x + 500; % 符号表达式,包含符号变量. 符号变量必须先有上一行定义! solve(exp1 == 0, x) % 命令行输入a,传入一个包含符号表达式的等式,x为所要求的变量
solve(exp1, x) % 命令行输入b,传入一个符号表达式,函数默认求其零点
solve(func1(x), x) % 命令行输入c,传入参数func1(x)等价于传入了符号表达式,和输入b完全一样
solve(func1(x) == 0, x) % 命令行输入d,这句话和a完全一样
solve(func1, x) % 命令行输入e,传入参数func1,这是一个函数句柄,函数默认求其零点 ans = % 命令行输出,三个解,为3*1的符号向量。对以上五种输入输出都完全一样
-5
5
20

  对于不可符号求解的函数零点/方程解,solve抛出警告并返回一个数值解:

exp1 = atan(x) - x - 1;    % 不可符号求零点的表达式
solve(exp1 == 0, x) % 命令行输入
% 命令行输出:
警告: Cannot solve symbolically. Returning a numeric approximation instead.
ans =
-2.132267725272885131625420696936

  值得注意的是,虽然称之为“数值解”,并且输出了一个位数非常多的小数,但查看数据类型发现ans的数据类型依然是符号变量。其实,如果是正常的double类型的变量,直接输出是不可能给出这么多位的。matlab里面默认打印精度是4位小数,可以用  format long  语句调整到15位小数,和机器精度基本持平。

  solve也可以求解方程组,此时输入的表达式epn和变量var为行向量(亲测列向量不可用):

exp1 = [x^2/9 + y^2/4 == 1, 2*x - 3*y == 0];    % 联立椭圆方程和直线方程
solution = solve(exp1, [x, y]); % 解方程组
% 命令行输出
solution =
包含以下字段的struct:
x: [2*1 sym]
y: [2*1 sym]
% 这意味着x和y均有两个解。函数输出的solution是一个struct,访问方法和C语言访问struct成员一样:
struct.x % 命令行输入
ans = % 命令行输出
(3*2^(1/2))/2
-(3*2^(1/2))/2

  可以看出,当solve给出符号解的时候,它是不会化简计算的。任何matlab的符号计算,包括四则运算、求导积分,都不具备化简/数值计算的功能。

  此外,solve函数还有一些选项可选,这使得符号求解名副其实,这才是solve的强大之处。运用solve函数,Name设定为'ReturnConditions',其值设定为true,表示要求solve函数输出详细信息。用这个方法我们可以得出一族解:

% 求解方程sin(x)=cos(x),我们知道这个方程有无穷多解
[solution, parameter, condition] = solve(sin(x)==cos(x), x, 'ReturnConditions', true);
% 命令行输出
solution = pi/4 + pi*k % 得到一族解,以pi为周期
parameter = k % parameter输出的是构成解的参数(符号变量)
condition =
in(k, 'integer'); % condition表明parameter的条件,此处k为整数

  而一般地,对于多变量的多项式(组),当多项式数量不足以确定所有参数时,按照以上设定,solve函数可以解出几个变量关于其他变量的函数:

exp1 = [x^2/9 + y^2/4 + z^2 == 1, 2*x - 3*y == 0];    % 椭球面和平面方程联立,结果应为曲线而非点
solution = solve(exp1, [x, y],'ReturnConditions', true); % 要求解出其中x和y的表达式
solution.x % 命令行输入:访问solution结构体的x参数
ans = % 命令行输出:x关于z的表达式,是符号向量,可以通过索引solution.x(1)和solution.x(2)分别访问
(3*2^(1/2)*(-(z - 1)*(z + 1))^(1/2))/2
-(3*2^(1/2)*(-(z - 1)*(z + 1))^(1/2))/2
% 结构体还有参数parameters和conditions。此处没有新生成的参数,因此parameters和conditions没有意义
solution.parameters % 命令行输入
ans =
Empty sym: 1-by-0
solution.conditions % 命令行输入
ans =
TRUE
TRUE

  在solve中,还可以使用  assume 函数来规定符号变量的性质(如整数、大于零、区间等等)。

2. 多初值的数值解方程(组)函数 vpasolve

  官方参考页:Solve equations numerically - MATLAB vpasolve

  vpasolve是一款数值解方程(组)的函数。输入一些个参数,返回符号型数值标量/向量(即以数值的形式显示但实际上还是符号变量)。它的基本使用方式是:

vpasolve(eqns, vars, init_guess, 'Random', randomvalue);    % 方程(组)eqns,变量vars,初值点init_guess(可缺省,在random模式下可写区间),'Random'设置randomvalue(可缺省)

  它的输入、功能和输出都和solve相仿。方程组的输入同样为行向量,变量组的输入也一样。

  当输入一个可以定解的多项式方程(组)时,vpasolve将会直接给出方程的数值解;若多项式方程数量不足以确定所有的解,那么vpasolve将会给出以剩余变量表示的所求变量的函数,只是表达式的一部分(系数等)可能会以数值的形式呈现。注意,有理分式方程将会多项式化以后一样处理。对于这些方程,init_guess的值写了也没用。

exp1 = (x-1)*(x-2)/(x-3);    % 分式方程
solution = vpasolve(exp1, x) % 命令行输入
solution = % 命令行输出,得到了全部解
1.0
2.0

  对于多元方程组,vpasolve的输出也是struct结构体,访问方法也和solve输出的struct一样。不同的是,vpasolve无法求出含参的解,即无法设定'ReturnConditions'选项。和solve类似,除了多项式方程和有理分式方程以外的任何方程,vpasolve都不会给出全部解。这样一看,似乎vpasolve只不过就是把solve的结果全部转化为数值形式,甚至许多solve的功能都不能满足。这样的想法当然不对,vpasolve也有其自身的优势,这来源于:

  A)可以设置初值进行数值求解。对于不可符号求解的方程,solve因为没有设定初值,可能无法搜索到合适的解。vpasolve则可以设置初值,从而可以进行后续解的搜索;B)可以随机取初值。我们都知道求解方程和最优化问题的初值选取非常玄学,而同样的初值最多只能有一个解。而结合循环等控制语句,利用vpasolve的随机初值功能可以让这一过程变得比较简单。比如可以写作初等函数的半整数阶Bessel(贝塞尔)函数,其零点有无穷多,但无法通过符号方法求解,在solve中会遇到很大的问题,但是用vpasolve设置合适的初值可以得到许多组临近初值的解。比如:

syms x;
exp1 = sin(x)/x;
exp1 = diff(exp1, x); % 对原函数求导,得到的函数零点和3/2阶贝塞尔函数的零点相同
% 命令行输入
solution = solve(exp1, x)
% 命令行输出,每次得到的结果均一样,为一个很遥远的解
警告: Cannot solve symbolically. Returning a numeric approximation instead.
solution =
-227.76107684764829218924973598808 solution = vpasolve(exp1, x, 1) % 命令行输入
solution = % 命令行输出大概就是0
0.00000000000000000000000014187233415524662526214503949551 solution = vpasolve(exp1, x, 3) % 命令行输入
solution = % 命令行输出
4.4934094579090641753078809272803  

  另外,一些有限个解的方程,比如 $atanx=x/2$ ,我们已经知道它有解0,根据画图还可以确定在x>0和x<0范围内各有一个解。根据atanx的性质,我们知道所有的解肯定在区间[-5,5]之中。如果使用solve,每次均有警告并且输出一样,无法获得三个不同的解;即使是之后讲的fsolve也需要每次给定初始估计(init guess)。对于vpasolve,当确定范围了以后可以简单地使用循环的控制语句,只需要规定随机撒点的区间为[-5,5]:

syms x;
exp1 = atan(x) - x/2;
for i = 1:5
solution = vpasolve(exp1 == 0, x, [-5, 5], 'Random', true);
disp(solution);
end

  输出结果:

-1.3628993400364241574879337535051e-53    % 也差不多即0
2.3311223704144226136678359559171 % 这就是要求的正根
-2.3311223704144226136678359559171 % 这就是要求的负根,和正根关于原点对称
2.3311223704144226136678359559171
0

  很轻松地得到了该方程的全部解而不用再去手动猜测了。

3. 数值解方程(组)函数 fsolve

  官方参考页:Solve system of nonlinear equations -  MATLAB fsolve

  fsolve可能是目前matlab的内置库函数中最常用的求(非线性)方程(组)解的函数,也是最为人熟知的。它用于数值求解方程(组),具有较广的适用范围(适用于高维和非线性、非多项式情形),甚至可以求矩阵方程的解(即甚至可以求解未知量为矩阵的情景)。fsolve函数的基本形式是:

[x, fval, exitflag, output, jacobian] = fsolve(func, init_guess, options);
% 输入函数句柄func,初值(向量)init_guess,求解选项options(可缺省);
% 输出解x,x处值fval(也就是残差,可缺省),计算终止信息exitflag、输出信息output、x处雅可比矩阵jacobian(均可缺省)

  比如参考页面给出的示例非线性方程组:$$e^{-e^{x_1+x_2}}-x_2(1+x_1^2)=0$$ $$x_1cos(x_2)+x_2sin(x_1)=\frac{1}{2}$$  这是一个迷一般的方程组,嵌套的自然指数让人十分混乱,我们也并不期望得到这个方程的符号解或者解析解。我们将该方程组转化为matlab函数句柄:

x = sym('x', [1,2]);
% 生成符号变量向量
f = [exp(-exp(-x(1)+x(2))) - x(2)*(1 + x(1)^2), x(1)*cos(x(2)) + x(2)*sin(x(1)) - 0.5];
% 生成函数句柄func,该句柄的输入参数为一向量
func = matlabFunction(f, 'Vars',{[x(1), x(2)]});

  然后调用fsolve对于函数func进行求解,输出一个求解消息和解solution:

% 命令行输入
solution = fsolve(func, [0,0])
% 命令行输出
Equation solved.
fsolve completed because the vector of function values is near zero
as measured by the default value of the function tolerance, and
the problem appears regular as measured by the gradient.
solution =
0.3931 0.3366

  需要注意的是,fsolve输入的函数句柄func只接受一个变量!fsolve可用于高维的情形,如例子中的二维,是通过将函数句柄的输入转化为向量实现的,即func接受一个向量形式的变量。对于创建一个输入参数为向量的函数句柄,简单地采用@方法似乎是行不通的。以上采用的方法是利用函数matlabFunction,定义变量('Var')为向量[x(1),x(2)],从而将符号变量f转化为函数句柄func。另一种可能更加普适(但更加麻烦)的方法参见官方参考页的示例或者matlab中函数fsolve的help文档,通过定义一个函数文件来实现这一操作(函数function文件和函数句柄是等价的)。

  函数fsolve提供了一些可以作为输出设置的options选项。options的设置如下:

options = optimoptions('fsolve', 'Display', opt1, 'PlotFcn', opt2);
% opt1可以填 'none'/'off'(无额外显示)/'iter'(迭代信息表格)
% opt2可以填函数 @optimplotfirstorderopt 绘制一阶极值条件随迭代的变化

  现在,尝试使用'iter'和'@optimplotfirstorderopt选项:

options = optimoptions('fsolve', 'Display', 'iter', 'PlotFcn', @optimplotfirstorderopt);
solution = fsolve(func, [0,0], options);
% 除了上述输出,还有了另外的输出:
Norm of First-order Trust-region
Iteration Func-count f(x) step optimality radius
0 3 0.385335 0.503 1
1 6 0.0336737 0.642449 0.206 1
2 9 0.000110235 0.122659 0.0162 1.61
3 12 8.13142e-11 0.00681475 1.13e-05 1.61
4 15 4.11698e-22 7.0962e-06 3.06e-11 1.61

  

  输出内容中,iteration为迭代次数,func-count为函数的总调用次数,f(x)为函数值的一个性质(暂时还没搞清楚是啥,毕竟二维映射不可能只有一个值),Norm of step应当是迭代步长(相邻迭代点间隔)的范数(模长),first order optimality 一阶优化条件,最终迭代是否终止的判据就是一阶优化条件是否足够接近零。绘图可以看出,随着迭代的进行,一阶优化条件趋于零。

  理论上,fsolve函数还允许指定求解的算法,比如使用单纯信赖域,或者使用狗腿信赖域,或者使用Levenberg-Marquardt算法。但总而言之,fsolve的算法均属优化算法,也因此在这里不足以讨论完全,留待优化部分的笔记说明。

4. 数值求一维函数零点函数 fzero

  官方参考页:非线性函数的根 - MATLAB fzero

  fzero用于求函数零点。这个函数致力于求解一维函数的零点。其基本形式:

x = fzero(func, init_guess, options)    % func为函数句柄,init_guess为初值,options可以包括其他要求(可缺省)

  fzero在应用上最令人高兴的是其丰富的输出内容,有利于观察迭代的结果,这用到options控制。options的控制方法为:

options = optimset(A, B);    % A为一个选项名,B为该选项值

  然后将options变量带入函数即可。具体可以参见参考页,在此举两个例子,比如希望输出迭代的每一步:

options = optimset('Display', 'iter');  % 设定'Display'选项为'iter'模式
func = @(x)x^3 + x + 5;
zeropt = fzero(func, 10, options);
disp(zeropt);

  则有输出(节选):

Func-count    x          f(x)             Procedure
26 -4.05097 -65.5287 initial
27 -3.40338 -37.8248 interpolation
28 -2.541 -13.9473 interpolation
29 -2.541 -13.9473 bisection
30 -1.65947 -1.22938 interpolation
31 -1.57533 -0.484774 interpolation
32 -1.52086 -0.0386585 interpolation
33 -1.51616 -0.00138072 interpolation
34 -1.51598 -4.15907e-06 interpolation
35 -1.51598 -4.49535e-10 interpolation
36 -1.51598 -8.88178e-16 interpolation
37 -1.51598 -8.88178e-16 interpolation

  从中,我们可以看到每一步的x变化,f(x)的取值,甚至每一次迭代执行的操作:是二分法(bisection)或者插值类方法(interpolation)。我们还可以将迭代步骤可视化:

options = optimset('PlotFcns', @optimplotfval);  % 每次输出函数值
func = @(x)x^3 + x + 5;
zeropt = fzero(func, 0, options);
disp(zeropt);

  输出图片:

5. 数值求多项式零点函数 roots

  官方参考页:多项式根 - MATLAB roots

  除了求多项式根啥也干不了的一个函数,输入也和其他求根函数迥异。roots的标准形式如下,输入一个行向量,返回一个double型的列向量:

r = roots(p);    % 其中,p为一个行向量,里面依次为多项式降次排序时各次项系数(若无该次则写0)

  roots也不是一无是处。相比于fzero和fsolve这样的函数,roots可以给出多项式的所有解,包括实数解和复数解:

p = roots([1, 0, 0, -1])    % 命令行输入
p = % 命令行输出三个解,其中一对共轭复根,一个实根
-0.5000 + 0.8660i
-0.5000 - 0.8660i
1.0000 + 0.0000i

  

非线性方程(组):MATLAB内置函数 solve, vpasolve, fsolve, fzero, roots [MATLAB]的更多相关文章

  1. 【原创】Matlab.NET混合编程技巧之找出Matlab内置函数

                  本博客所有文章分类的总目录:[总目录]本博客博文总目录-实时更新    Matlab和C#混合编程文章目录 :[目录]Matlab和C#混合编程文章目录 Matlab与.N ...

  2. 【原创】Matlab.NET混合编程技巧之直接调用Matlab内置函数

                  本博客所有文章分类的总目录:[总目录]本博客博文总目录-实时更新    Matlab和C#混合编程文章目录 :[目录]Matlab和C#混合编程文章目录 在我的上一篇文章[ ...

  3. Matlab.NET混编技巧之——找出Matlab内置函数

    原文 http://www.cnblogs.com/asxinyu/p/3295309.html Matlab与.NET的混合编程,掌握了基本过程,加上一定的开发经验和算法基础,肯 定不难.反之,有时 ...

  4. Matlab.NET混合编程技巧之——直接调用Matlab内置函数(附源码)

    原文:[原创]Matlab.NET混合编程技巧之--直接调用Matlab内置函数(附源码) 在我的上一篇文章[原创]Matlab.NET混编技巧之——找出Matlab内置函数中,已经大概的介绍了mat ...

  5. Matlab.NET混合编程技巧之——找出Matlab内置函数

    原文:[原创]Matlab.NET混合编程技巧之--找出Matlab内置函数 Matlab与.NET的混合编程,掌握了基本过程,加上一定的开发经验和算法基础,肯定不难.反之,有时候一个小错误,可能抓破 ...

  6. Matlab内置函数

    [原创]Matlab.NET混编技巧之——找出Matlab内置函数   Matlab与.NET的混合编程,掌握了基本过程,加上一定的开发经验和算法基础,肯定不难.反之,有时候一个小错误,可能抓破脑袋, ...

  7. 循序渐进Python3(三) -- 1 -- 内置函数

    对函数有了一定了解之后,我们来看看Python的内置函数.下图是Python所有的内置函数,共68个.

  8. FreeMarker惯用内置函数

    1.sequence?first 返回sequence的第一个值. 2.sequence?last 返回sequence的最后一个值. 3.sequence?reverse 将sequence的现有顺 ...

  9. Oracle中REGEXP_SUBSTR及其它支持正则表达式的内置函数小结

    Oracle中REGEXP_SUBSTR函数的使用说明: 题目如下:在oracle中,使用一条语句实现将'17,20,23'拆分成'17','20','23'的集合. REGEXP_SUBSTR函数格 ...

随机推荐

  1. Struts2之web元素访问与模板包含与默认Action使用

    上一篇为大家介绍了如何使用Action进行参数接收,以及简单的表单验证,本篇为大家介绍一下关于Action访问web元素的三种方式(request.session.application):模板包含( ...

  2. vmware下Ubuntu屏幕分辨率设置

    1.查看现有设备 xrandr -q 输出如下: Screen 0: minimum 1 x 1, current 800 x 600, maximum 8192 x 8192 Virtual1 co ...

  3. python-django开发学习笔记一

    1.简述 1.1 开发环境 该笔记所基于的开发环境为:windows8.python2.7.5.psycopg2-2.4.2.django1.5.4.pyCharm-2.7.3.以上所描述的软件.插件 ...

  4. Saltstack之api

    Salt-API 1,htps证书 2,配置文件 3,验证.使用PAM验证 4,启动salt-api 安装salt-api yum -y install salt-api 创建用户 useradd - ...

  5. POJ-1322 Chocolate(概率DP)

    Chocolate Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 9279 Accepted: 2417 Special Jud ...

  6. nowcoder2018年全国多校算法寒假训练营练习比赛(第一场)

    [气死我了 写完了博客发布 点看来一看怎么只剩下一半不到的内容了!!!!!!!!!!] [就把卡的那两道放上来好了 其余的不弄了 生气!!!!!] 可以说是很久没有打比赛了 今天这一场主要是  基础算 ...

  7. ubuntu16.04下安装artoolkit5

    目前对AR技术的常见理解就是CV(Computer Vision)+CG(Computer Graphic).CV的方法很多,简单些比如FREAK+ICP(ARToolKit中的NFT),复杂些就是S ...

  8. Anaconda安装教程+Tensorflow教程

    Anaconda安装 1.拿到安装包,点击安装 2.下一步 3.下一步 4.下一步 5.下一步 6.下一步 7.更改文件显示路径 ctrl+f查找 # The directory to use for ...

  9. ArcGIS API for javascript开发笔记(六)——REST详解及如何使用REST API调用GP服务

    感谢一路走来默默支持和陪伴的你~~~ -------------------欢迎来访,拒绝转载-------------------- 一.Rest API基础 ArcGIS 平台提供了丰富的REST ...

  10. POJ3468 a simple problem with integers 分块

    题解:分块 解题报告: 是个板子题呢qwq 没什么可说的,加深了对分块的理解趴还是 毕竟这么简单的板子题我居然死去活来WA了半天才调出来,,,哭了QAQ 还是说下我错在了哪几个地方(...是的,有好几 ...