非线性方程(组):一维非线性方程(二)插值迭代方法 [MATLAB]
一般而言,方程没有能够普遍求解的silver bullet,但是有几类方程的求解方法已经非常清晰确凿了,比如线性方程、二次方程或一次分式。一次方程可以直接通过四则运算反解出答案,二次方程的求根公式也给出了只需要四则运算和开根号的符号表达式。而一次分式的分子即为一次函数。更多的方程并没有普适的符号表达式,但通过用便于求零点的函数模仿、代替之也可以估计零点的位置。插值方法可以实现这一思路。
插值迭代方法包括割线法、二次插值法等多项式插值方法,反插法以及线性分式插值法等等,其核心是用几个点及其函数值信息,通过插值产生一条容易计算零点的函数图线,然后用插值函数的零点来估计原函数的零点,不断迭代以达到足够的精度。每个算法的大致思路均相同,不再分别阐述具体原理。
1. 割线法(Secant Method)
用一次函数模拟原函数,用该一次函数的零点作为原函数零点的下一个估计。起始需要两个点。迭代式:$$x_{k+1}=x_k-f(x_k)\frac{x_k-x_{k-1}}{f(x_k)-f(x_{k-1})}$$ 迭代式和牛顿法的迭代式类似。实际上,割线法正是用两点的割线斜率代替了牛顿法中使用的切线斜率(即导数):$f'(x_k)\approx [f(x_k)-f(x_{k-1})]/(x_k-x_{k-1})$ .
function [ zeropt, iteration ] = SecantMethod( func, start0, start1, prec )
% 割线法求零点,函数句柄func,两个起点start0,start1,绝对精度prec
% 返回:零点zeropt,迭代步数iteration
prev = start0;
current = start1;
next = current - func(current)*(current - prev)/(func(current) - func(prev));
iteration = 0;
while abs(next - current) > prec && iteration < 500
prev = current;
current = next;
next = current - func(current)*(current - prev)/(func(current) - func(prev));
iteration = iteration + 1;
end
if iteration >= 500
error('Method fails to converge');
end
zeropt = next;
end
进行试验:
% 用二次函数x^2-x-2
func = @(x)x^2-x-2;
[zero, iter] = SecantMethod(func, 6, 10, 0.0001)
% 输出
zero = 2.0000, iter = 7 % 输入
[zero, iter] = SecantMethod(func, -3, -9, 0.0001)
% 输出
zero = -1.0000, iter = 6
【迭代步复杂度】两次函数值求值+固定次数的四则运算(5)。
【收敛速度】超线性收敛,$r\approx 1.618$
【优势】1)每迭代步复杂度低,收敛速度中等;2)不用求导,只需要进行函数求值操作
【劣势】收敛速度不够快。
2. 二次插值法(Muller's Method)
用二次函数模拟原函数,将二次函数的根作为下一个零点估计值。每次迭代删去最老的一个点,利用包括新点的连续三个点进行插值。起始需要三个点。
二次插值面临的问题是,二次函数并不一定有实根。事实上,二次插值法的过程中完全不排斥出现复根,而且理论上依然可以收敛。至于对于二次方程的两个根,选取哪一个,主要是在迭代表达式中为了减免减法所带来的有效数字损失而定的。
二次插值法相对复杂,其图像由于复根的出现也不直观,但是其收敛率却达到了:$r\approx 1.839$. Muller证明了任意m次多项式插值方法的收敛率r满足方程:$$r^{m+1}-r^m-...-r^2-r-1=0, \quad 或\quad r^{m+1}=\sum\limits_{k=0}^mr^k$$
3. 二次反插法
同样用二次函数模拟原函数,但反插法使用的是旋转90°的抛物线,即x关于y的二次函数。这样可以同时解决没有实根和选择恐惧症的问题,因为x关于y的二次函数与横轴必定有且只有一个交点。
根据Lagrange插值的方法,假设已有点 $(a, f_a),(b,f_b),(c, f_c)$ ,他们的二次反插应当为:$$x=a\frac{(y-f_b)(y-f_c)}{(f_a-f_b)(f_a-f_c)}+b\frac{(y-f_a)(y-f_c)}{(f_b-f_a)(f_b-f_c)}+c\frac{(y-f_a)(y-f_b)}{(f_c-f_b)(f_c-f_a}$$ 现在下一个点的纵坐标是零(作为零点的估计),则应当有:$$x=-\frac{af_bf_c(f_b-f_c)+bf_af_c(f_c-f_a)+cf_af_b(f_a-f_b)}{(f_a-f_b)(f_b-f_c)(f_c-f_a)}$$
function [ zeropt, iteration ] = InveQuadra( func, start0, start1, start2, prec )
% 二次反插求零点。输入函数句柄func,三个起始点start0-start2,要求精度prec
% 返回两个值:零点zeropt,迭代步数iteration
a = start0; b = start1; c = start2;
fa = func(a); fb = func(b); fc = func(c);
diffab = fa - fb; diffbc = fb - fc; diffca = fc - fa;
next = a*fb*fc/(diffab*diffca) + b*fa*fc/(diffab*diffbc) + c*fa*fb/(diffbc*diffca);
next = - next;
iteration = 0;
while abs(next - c) > prec && iteration < 500
a = b; b = c; c = next;
fa = func(a); fb = func(b); fc = func(c);
diffab = fa - fb; diffbc = fb - fc; diffca = fc - fa;
next = a*fb*fc/(diffab*diffca) + b*fa*fc/(diffab*diffbc) + c*fa*fb/(diffbc*diffca);
next = - next;
iteration = iteration + 1;
end
if iteration >= 500
error('Method fails to converge.');
end
zeropt = next;
end
用同样的函数进行试验:
func = @(x)x^2-x-2;
% 输入
[zero, iter] = InveQuadra(func, -3, -9, -7, 0.0001)
% 输出
zero = -1.0000, iter = 5 % 输入
[zero, iter] = InveQuadra(func, 31, 16, 67, 0.0001)
% 输出
zero = 2.0000, iter = 8 func = @(x)x^3 - 20*x^2 - 25*x + 500;
% 输入
[zero, iter] = InveQuadra(func, -10, 10, -80, 0.0001)
% 输出
zero = 20.0000, iter = 4
可以看出,当应用于同样函数类似地靠近某一真值的种子时,二次反插比割线法收敛更快一点点。
【迭代步复杂度】需要三次函数求值+若干次四则运算。考虑到需要插值的是二次曲线,四则运算的数量显著高于割线法。
【收敛速度】和二次插值相同,二次反插的收敛率也是 $r\approx 1.839$.
【优势】1)不用求导。如果用牛顿法一样的思路,形成二次曲线不仅要求一阶导,还要求二阶导!2)避免了二次插值的复根问题和选根问题;3)$r\approx 1.839$ 已经比较令人满意。
【劣势】计算复杂度相对于割线法来说较大,且需要三个起始点。
4. 线性分式插值法
线性分式插值法使用形如 $\phi(x)=\frac{x-u}{vx-w}$ 的形式来插值之前的迭代点,并将插值函数的零点u作为新的零点的估计值。线性分式插值法主要就是为了求有水平或竖直方向渐近线的函数,这类函数亲测采用二次反插会有格外的困难,常常莫名其妙地跑到无穷远处去;仔细分析时证实,由于这类函数在很宽的范围内较为平坦,二次反插往往会形成非常尖锐的抛物线,该抛物线零点甚至很容易朝远离原点方向移动;如此数次迭代则趋于无穷。而线性分式插值法则可以解决这个问题。
代码实现:
function [ zeropt, iteration ] = InveQuadra( func, start0, start1, start2, prec )
% 二次反插求零点。输入函数句柄func,三个起始点start0-start2,要求精度prec
% 返回两个值:零点zeropt,迭代步数iteration
a = start0; b = start1; c = start2;
fa = func(a); fb = func(b); fc = func(c);
diffab = fa - fb; diffbc = fb - fc; diffca = fc - fa;
next = a*fb*fc/(diffab*diffca) + b*fa*fc/(diffab*diffbc) + c*fa*fb/(diffbc*diffca);
next = - next;
iteration = 0;
while abs(next - c) > prec && iteration < 500
a = b; b = c; c = next;
fa = func(a); fb = func(b); fc = func(c);
diffab = fa - fb; diffbc = fb - fc; diffca = fc - fa;
next = a*fb*fc/(diffab*diffca) + b*fa*fc/(diffab*diffbc) + c*fa*fb/(diffbc*diffca);
next = - next;
iteration = iteration + 1;
end
if iteration >= 500
error('Method fails to converge.');
end
zeropt = next;
end
试验如下:
% 这个函数恰好是分式的形式,然而用二次反插困难重重
func = @(x)1 - 3/x;
% 输入
[zero, iter] = LinFraction(func, 1, 5, 10, 0.0001)
% 输出
zero = 3, iter = 1 func = @(x)9 - 1/x^2;
% 输入
[zero, iter] = LinFraction(func, 1, 5, 10, 0.0001)
% 输出
zero = 0.3333, iter = 6
【迭代步复杂度】 和二次反插基本相同,三次函数求值及四则运算若干。
【收敛率】惊了,居然也和二次插值/反插相同,为 $r\approx 1.839$
该算法的特点已经如上所述。
非线性方程(组):一维非线性方程(二)插值迭代方法 [MATLAB]的更多相关文章
- Javascript数组系列二之迭代方法1
我们在<Javascript数组系列一之栈与队列 >中介绍了一些数组的用法.比如:数组如何表现的和「栈」一样,用什么方法表现的和「队列」一样等等一些方法,因为 Javascript 中的数 ...
- Javascript数组系列三之迭代方法2
今天我们来继续 Javascript 数组系列的文章,上文 <Javascript数组系列二之迭代方法1> 我们说到一些数组的迭代方法,我们在开发项目实战的过程中熟练的使用可以大大提高我们 ...
- 非线性方程(组):一维非线性方程(一)二分法、不动点迭代、牛顿法 [MATLAB]
1. 二分法(Bisection) 1) 原理 [介值定理] 对于连续的一元非线性函数,若其在两个点的取值异号,则在两点间必定存在零点. [迭代流程] 若左右两端取值不同,则取其中点,求其函数值,取中 ...
- Numpy数组对象的操作-索引机制、切片和迭代方法
前几篇博文我写了数组创建和数据运算,现在我们就来看一下数组对象的操作方法.使用索引和切片的方法选择元素,还有如何数组的迭代方法. 一.索引机制 1.一维数组 In [1]: a = np.arange ...
- 二值化方法:Kittler:Minimum Error Thresholding
Kittler二值化方法,是一种经典的基于直方图的二值化方法.由J. Kittler在1986年发表的论文“Minimum Error Thresholding”提出.论文是对贝叶斯最小错误阈值的准则 ...
- 二值法方法综述及matlab程序
在某些图像处理当中一个关键步是二值法,二值化一方面能够去除冗余信息,另一方面也会使有效信息丢失.所以有效的二值化算法是后续的处理的基础.比如对于想要最大限度的保留下面图的中文字,以便后续的定位处理. ...
- JS高程5.引用类型(6)Array类型的位置方法,迭代方法,归并方法
一.位置方法 ECMAScript5为数组实例添加了两个位置:indexOf()和 lastIndexOf().这两个方法接收两个参数:要查找的项和(可选的)表示查找起点位置的索引(如在数组[7,8, ...
- JS_高程5.引用类型(6)Array类型的位置方法,迭代方法,归并方法
一.位置方法 ECMAScript5为数组实例添加了两个位置:indexOf()和 lastIndexOf().这两个方法接收两个参数:要查找的项和(可选的)表示查找起点位置的索引(如在数组[7,8, ...
- Java一维与二维数组的拷贝与排序
Java一维与二维数组的拷贝与排序 目录 Java一维与二维数组的拷贝与排序 Arrays.sort() 一维数组升序排序 二维数组按行升序排序 二维数组按列升序排序 Java中的数组 Java中数组 ...
随机推荐
- 南京IT企业环境之最深心得体会
我是南京做嵌入式的. 之前搞过一年的PC平台Linux内核开发,Linux内核态的仅仅要不是非常复杂的BUG还是能修复的.一年的Linux用户态软件开发. 然后近期搞了两年ARM嵌入式开发. 做的CM ...
- Extjs 自定义控件
// JavaScript Document Ext.namespace('CRM.Panels'); CRM.Panels.UserDetail = Ext.extend(Ext.Panel,{ w ...
- ios开发 int,NSInteger,NSUInteger,NSNumber
分享一下,在工作工程中遇到的一些不留心的地方: 1.当需要使用int类型的变量的时候,可以像写C的程序一样,用int,也可以用NSInteger,但更推荐使用NSInteger,因为这样就不用考虑设备 ...
- 走进ELK原理
日志的分析在联调和后期维护是非常重要. 今天让我们进入ELK...让我们一起感受这个强大的日志收集,日志分析存储,日志查询界面化显示的震撼效果吧. Elasticsearch是个开源分布式搜索引擎,它 ...
- Linux ulimit 命令
ulimit命令用来限制系统用户对 shell 资源的访问,常见用法如下: [root@localhost ~]$ ulimit -a # 查看当前所有的资源限制 [root@localhost ~] ...
- Unity官网针对IOS开发有比较好的建议
Unity官网针对IOS开发有比较好的建议,我总结了翻译如下,后面附上原文. 尽量控制定点数量(注意所谓顶点不是建模时的顶点,而是引擎渲染时的顶点.例如,模型一个顶点如果设置了2个法向,那么对引擎来说 ...
- poj_3461 kmp
题目大意 给定两个字符串S1, S2,求S1中包含多少个S2串.其中S1长度最大为 1000000, S2长度最大为10000. 题目分析 典型的字符串匹配问题,直接匹配显然会超时,考虑使用kmp算法 ...
- 【Android M】获取屏幕锁定的相关信息:“无”,“滑动”,“PIN码”,"图案","密码"
ENV: Android M 6.0.1 import android.os.UserHandle; import com.android.internal.widget.LockPa ...
- Mahout实现的算法
在Mahout实现的机器学习算法见下表 算法类 算法名 中文名 分类算法 Logistic Regression 逻辑回归 Bayesian 贝叶斯 SVM 支持向量机 Perceptron 感知器算 ...
- 处理URL传递中文乱码问题
在网上搜了很多资料都没有搞定,一般都有以下几种说法: 方法1:在后台中先获得字符串的iso-8859-1编码形式数组,再使用此数组实例一个UTF-8编码形式String类型字符串. 页面提交的url为 ...