导论
本文主要介绍单变量非线性方程求根的划界法。采用的例子为典型的蹦极运动员模型,模型背景不再介绍,具体题目为求解使得以下方程的成立的m:
其中g,cd,t,v均已知。
划界法一个典型的特定是,在进行算法之前,需要给出一个区间,在此区间之内函数连续且存在且只存在一个根。通常通过画图寻找满足的区间。本例中函数图形如下:
易知,在[40, 200]区间内存在根。
二分法
二分法的思想是:计算区间中点处的函数值,函数值与哪个区间端点处的函数值具有相同的符号,则使用中点取代此端点,形成新的区间,如此不断迭代。迭代的终止条件为:
clear
clc
fm = @(m, cd, t, v) sqrt(9.81.*m./cd).*tanh(sqrt(9.81.*cd./m)*t) - v;
m = 50:0.1:200;
y = fm(m, 0.25, 4,36);
plot(m, y)
grid on
hold on
[root, fx, ea, iter] = bisect(@(m) fm(m, 0.25, 4, 36), 40, 200);
function [root, fx, ea, iter] = bisect(func, xl, xu, es, maxit, varargin)
% 使用二分法寻根
% 输入参数:
% func:函数名称
% xl,xu:区间上下界
% es:期望误差
% macit:最大迭代次数
% p1,p2,...:函数func的额外参数
% 输出参数:
% root:根
% fx:在根root处的函数值
% ea:实际相对误差
% iter:迭代次数
if nargin<3, error('至少输入三个参数:函数名,区间下界,区间上界'),end
test = func(xl, varargin{:})*func(xu, varargin{:});
if test>0, error('区间上下界处符号没有改变'), end
if nargin<4||isempty(es), es = 0.0001; end
if nargin<5||isempty(maxit), maxit = 50; end
iter = 0; xr = xl; ea = 100;
while(1)
xrold = xr;
xr = (xl+xu)/2;
iter = iter+1;
if xr~=0, ea = abs((xr - xrold)/xr) * 100; end
test = func(xl, varargin{:})*func(xr, varargin{:});
if test<0
xu = xr;
elseif test>0
xl = xr;
else
ea = 0;
end
if ea<=es || iter>maxit, break, end
plot(xr, func(xr, varargin{:}),'*')
hold on
end
root = xr; fx = func(xr, varargin{:});
end
可以看出,迭代21次结束循环,求得的解为142.7377。
关于二分法的误差分析,此处不再详细展开,只介绍一些与二分法求根相关的结论:
- 在二分法中采用近似误差作为迭代终止条件,但二分法中近似误差一定小于真误差,因此二分法得出的解在误差要求方面一定满足要求。
- 二分法达到某个绝对误差所需的迭代次数可以预先得到。
试位法
试位法又称为线性插值法,其思想是使用通过区间两端的直线与x轴的交点作为新的估计值。试位公式为:
试位法与二分法相比,除了计算x新值的公式不同之外其余步骤完全相同。
clear
clc
fm = @(m, cd, t, v) sqrt(9.81.*m./cd).*tanh(sqrt(9.81.*cd./m)*t) - v;
m = 50:0.1:200;
y = fm(m, 0.25, 4,36);
plot(m, y)
grid on
hold on
[root, fx, ea, iter] = shiwei(@(m) fm(m, 0.25, 4, 36), 40, 200);
function [root, fx, ea, iter] = shiwei(func, xl, xu, es, maxit, varargin)
% 使用试位法寻根
% 输入参数:
% func:函数名称
% xl,xu:区间上下界
% es:期望误差
% macit:最大迭代次数
% p1,p2,...:函数func的额外参数
% 输出参数:
% root:根
% fx:在根root处的函数值
% ea:实际相对误差
% iter:迭代次数
if nargin<3, error('至少输入三个参数:函数名,区间下界,区间上界'),end
test = func(xl, varargin{:})*func(xu, varargin{:});
if test>0, error('区间上下界处符号没有改变'), end
if nargin<4||isempty(es), es = 0.0001; end
if nargin<5||isempty(maxit), maxit = 50; end
iter = 0; xr = xl; ea = 100;
while(1)
xrold = xr;
fl = func(xl, varargin{:});
fu = func(xu, varargin{:});
xr = xu - (fu*(xl-xu) / (fl-fu));
iter = iter+1;
if xr~=0, ea = abs((xr - xrold)/xr) * 100; end
test = func(xl, varargin{:})*func(xr, varargin{:});
if test<0
xu = xr;
elseif test>0
xl = xr;
else
ea = 0;
end
if ea<=es || iter>maxit, break, end
plot(xr, func(xr, varargin{:}),'*')
hold on
end
root = xr; fx = func(xr, varargin{:});
end
试位法迭代29次得到了与二分法几乎相同的结果。