Upload
others
View
16
Download
0
Embed Size (px)
Citation preview
第 3 章 数值分析概述
数值分析也称为数值计算方法,隶属于计算数学的范畴,是研究用计算机求解各种数
学问题的数值方法及其理论的一门学科。
数值分析既有纯数学高度的抽象性与严密科学性,又有应用数学的广泛性与边缘性,
是一门与计算机使用密切结合的实用性很强的数学课程。总的来说,数值分析大致有以下
特点:
� 面向计算机,能根据计算机特点提供切实可行的有效算法;
� 有可靠的理论分析,能任意逼近并达到精度要求,对近似算法要保证收敛性和数值
稳定性,还要对误差进行分析;
� 要有好的计算复杂性,其中时间复杂性好是指节省时间,空间复杂性好是指节省存
储量,这也是建立算法要研究的问题,关系到算法能否在计算机上实现;
� 要有数值实验,即任何一个算法除了从理论上要满足上述三点外,还要通过数值实
验证明是行之有效的。
本章我们将介绍一些数值分析基础知识,如数值分析的主要内容概述、误差理论、数
值算法概述及数值计算中应注意的问题等。
3.1 数值分析主要内容及任务
数值分析的内容主要包括数值逼近、方程求解、数值线性代数及一些边缘应用,各部
分又包含了相应的内容,本书中要介绍的有关数值分析的内容如图 3-1所示。
数值分析用于求解实际数学问题时,首先要建立合适的数学模型,然后设计相应的数
值计算方法并编写程序,最后上机实验,得出结果并分析。该过程可以用图 3-2所示的框
架图来描述。
MATLAB数值计算实战
·88·
图 3-1 数值分析主要内容框架图
图 3-2 数值分析求解数学问题流程
3.2 误差理论
早在中学时期我们就接触过误差的概念。例如,在做化学实验时,从量筒上读出的某
液体体积为 40.1ml,这个数字就不是一个精确的值,而是含有误差的近似值。事实上,误
差在人们的日常生活中无处不在,无处不有。如量体裁衣,量与裁的结果都不是精确无误
的,都含有误差。
3.2.1 误差来源
从科学研究和实际工程技术问题计算的全过程来看,误差的来源主要有以下 4个方面。
第 3章 数值分析概述
·89·
1.模型误差
用计算机解决实际问题时,首先要将实际问题转化为数学问题,即所谓的建立数学模
型,由于各种实际问题往往是十分复杂的,而数学模型是对描述的实际问题进行抽象、简
化而得到的,往往忽略了一些次要因素,因此数学模型只是客观现象的一种近似、粗糙的
描述。这种数学模型与实际问题之间出现的误差称为模型误差。例如,牛顿第二定律建立
的模型虽然是一种优秀的近似,但在实际中永远无法准确地预测出结果,像风和空气阻力
的微小变化等各种因素都可能导致计算结果偏离预测,因此在计算过程中必须先将其进行
必要的处理或简化。
2.参数误差(观测误差)
数学模型中的物理参数的具体数值一般通过实验测定或观测得到,因此与真值之间必
然会有误差,这种误差称为参数误差或观测误差。
观测误差产生的原因很多,总结起来主要有以下 3个方面:
(1)测量仪器:测量工作通常是利用测量仪器进行的。而每一种测量仪器都具有一定
限度的精密度,因此使得观测值的精度受到了一定的限制。另外,仪器本身受制造工艺的
限制也有一定的误差;
(2)观测者:不同的观测者感觉器官的辨别能力是有一定差异的,而且就算是同一个
观测者,在不同的时间、空间的感觉器官的辨别能力也会有所不同;
(3)外界条件:温度、湿度、压强、风力、大气折光、电离层等因素都会对观测结果
直接产生影响,随着这些因素的变化,它们对观测结果的影响也随之不同,因此观测结果
产生误差是必然的。
3.方法误差(截断误差)
在数学模型(包括参数值)确定以后,就要考虑选用某种数值方法具体进行计算,许
多数值方法都是近似方法,因此求出的结果与准确值之间是有误差的,这种误差称为方法
误差或截断误差。
【例 3-1】给定积分1
2
30
1d
1I x
x=
+∫ ,
(1)用MATLAB求其解析解;
(2)用 5阶泰勒级数近似代替被积函数,计算积分的近似值,并在图形上予以显示。
【分析】求定积分的解析解,只需使用MATLAB符号运算工具箱中的 int函数求解即
可,要将被积函数 5阶泰勒展开则需要使用 taylor函数,下面给出实现程序的代码如下: 1. syms x % 声明符号变量
2. y=1/(1+x^3); % 被积函数
3. I1=int(y,0,1/2); % 定积分的解析解
4. yt=taylor(y,x,'order',6); % 被积函数 5阶泰勒展开
MATLAB数值计算实战
·90·
5. I2=int(yt,0,1/2); % 泰勒展开式的定积分
6. fplot(y,[0,1/2],'k','linewidth',2) % 绘制被积函数
7. hold on % 图形保持
8. fplot(yt,[0,1/2],'k--','linewidth',2) % 绘制被积函数的 5阶泰勒逼近多项式 9.legend(gca,{['$$y =',latex(y),'$$'],['$${y_t}=',latex(yt),'$$']},...
% 添加图例 10. 'interpreter','latex','fontsize',12,'Position',[0.65,0.7,0.25,0.2]) 11. text(0.05,0.92,['$$\int_0^\frac{1}{2} {{y}dx}=',latex(I1),'$$'],...
12. 'interpreter','latex','fontsize',12) % 添加文本标注 13. text(0.05,0.9,['$$\int_0^\frac{1}{2} {{y_t}dx}=',latex(I2),'$$'],...
14. 'interpreter','latex','fontsize',12) % 添加文本标注
程序的运行结果如图 3-3所示。
图 3-3 截断误差存在性演示
由程序运行结果可知,无论从被积函数与 Taylor展开多项式的图形以及相应的积分值
来看,都可以发现截断误差的存在。
4.舍入误差
在使用计算机进行数值计算时,所计算数据的位数可能很多甚至有无穷多位,而计算
机的字长是有限的,因此在进行数值计算的过程中,对计算得到的中间结果数据要使用“四
舍五入”或其他规则取近似值,因而使计算过程产生误差。这种误差称为舍入误差。
例如, 9007199254740993 18014398509481983,
9007199254740992 18014398509481984
- -
内的所有实数在 MATLAB 中都
表示为-1,这样就不可避免地出现了舍入误差。
由上述误差来源分析可知:对于实际问题,在建立数学模型时,模型本身已存在着模
型误差和观测误差,即这两种误差是客观存在的,称为固有误差;而后两种误差是由计算
第 3章 数值分析概述
·91·
方法所引起的,称为计算误差。因此,在数值计算方法中,主要讨论的是计算误差,即截
断误差和舍入误差。
3.2.2 绝对误差与相对误差
数值误差来源于用近似的数值操作或量表示准确的数值操作或量。对于这样的误差,
准确结果或真值与近似结果之间的关系可以用下述公式表示:
e=x-x*
其中,x为准确值,x*是 x的一个近似值,e称为近似值 x
*的绝对误差,简称误差。
在通常情况下,我们不能算出准确值 x,也不可能算出误差的准确值,因此这个值虽
然客观存在,但在实际计算中是很难得到的,而一般只能估测误差的某个范围,即估测出
误差的绝对值不超过某正整数 ε,数学表达式为: *
e x x ε= − ≤
这时称 ε为近似值 x*的绝对误差限,简称误差限。有时也表示为:
x=x*±ε
当然,上述误差的定义也有其不足——没有考虑被估计值幅度的量级。例如有两个量:* *
1 1 1 2 2 210 1, 1000 5x x e x x e= ± = ± = ± = ± ,虽然 e2>e1,但我们却不能说
*
1x 的近似程度比
*
2x
好。这时需要考虑被估计值幅度的量级,具体的做法是将误差相对真值归一化,即: *
r
x x
e
x
−=
称 er为近似值 x*的相对误差。相应的相对误差限记为:
*
r r
x x e
e
x x
ε−= = ≤
在实际计算中通常用式*
*r
x
εε = 来近似相对误差限。
3.2.3 有效数字
若近似值 x*的误差限是某一数位的半个单位,且该位到 x
*的第一位非零数字共有 n位,
那么就说 x*具有 n位有效数字。科学计数法中通常将 n位有效数字 x
*表示为:
x*=±0.a1a2…an×10
m
即
x*=±(a1×10
-1+a2×10
-2+…+an×10
-n)×10
m
其中 m为一整数,a1,a2,…,an是 0~9之间的整数,且 a1≠0。再由有效数字的定义知:
* 110
2
m n
e x x−= − ×≤
MATLAB数值计算实战
·92·
因此,近似值 x*的误差限为
110
2
m nε −= × 。
根据上述描述,可以编写相应的程序,求近似值的有效数字位数及近似值的误差限。
函数代码如下: 1. function [n,e]=getdigits(xtrue,x)
2. % GETDIGITS 获取近似值的有效数字位数及误差限
3. err=xtrue-x; % 求误差
4. [~,m]=enotation(x); % 用科学计数法表示近似值
5. [err,q]=enotation(err); % 用科学计数法表示误差
6. if err<5 % 判断误差的第一位非零数字是否小于 5 7. n=m-q; 8. else 9. n=m-q-1; 10. end
11. e=sym(1/2)*10^(m-n); % 返回误差限
12. %% 利用科学计数法表示数字 x,将数字 x化为 a0.a1a2...*10^m次的形式 13. function [x,m]=enotation(x)
14. x=abs(x); % 对 x取绝对值
15. p=0; % 计数器
16. while x>=10 % 判断数字 x的绝对值是否不小于 10
17. x=x/10; % 逐步除 10,最终得到 a0
18. p=p+1; % 计数器加 1 19. end
20. if x~=0 % 判断 x是否能被 10整除
21. while x<1 % 判断 x是否真小数
22. x=x*10; % 逐步乘 10,最终得到 a0
23. p=p-1; % 计数器减 1 24. end 25. end 26. m=p+1; 27. end 28. end
【例 3-2】已知圆周率 π 的真值为 π=3.1415926535897931…,若 π 的近似值取为
π*=3.1415,问 π
*有几位有效数字,并给出误差限。若 π的近似值取为 π
*=3.141593呢?
【分析】本题只需直接调用 getdigits函数求解即可,编写如下语句: >> pi_true=3.1415926535897931; >> pi_1=3.1415; >> [n1,e1]=getdigits(pi_true,pi_1) n1 = 4 e1 = 1/2000 >> pi_2=3.14159; >> [n2,e2]=getdigits(pi_true,pi_2) n2 = 6 e2 = 1/200000
第 3章 数值分析概述
·93·
3.2.4 误差的传播与估计
数值计算中误差的传播情况非常复杂,参与运算的数据往往都是近似数,它们都带有
误差,而这些数据的误差在各次运算中又会进行传播与积累,使计算结果产生一定的误差。
这时确定计算结果所能达到的精度则显得十分重要,但这往往也是很难办到的,不过对计
算误差进行一定的定量估计还是可以做到的。
先从二元函数 y=f(x1,x2)开始,设* *
1 2,x x 分别是 x1,x2的近似值,y
*是函数 y 的近似值,
且* * *
1 2( , )y f x x= ,函数 f(x1,x2)在点
* *
1 2( , )x x 处的泰勒展开式为:
* *
* * * *
1 2 1 2 1 1 2 2
1 2
* * *2 2 2
* 2 * * * 2
1 1 1 1 2 2 2 22 2
1 1 2 2
( , ) ( , ) ( ) ( )
1( ) 2 ( )( ) ( )
2!
f ff x x f x x x x x x
x x
f f fx x x x x x x x
x x x x
∂ ∂ = + − + − ∂ ∂
∂ ∂ ∂ + − + − − + − + ∂ ∂ ∂ ∂
i i
i i i �
式中*
1 1 1( )x x e x− = 和
*
2 2 2( )x x e x− = 一般都是小量值,若忽略高阶小量,则上式可简化为:
* *
* *
1 2 1 2 1 2
1 2
( , ) ( , ) ( ) ( )f f
f x x f x x e x e xx x
∂ ∂ = + + ∂ ∂
i i
因此,
* *
* * *
1 2 1 2 1 2
1 2
( ) ( , ) ( , ) ( ) ( )f f
e y y y f x x f x x e x e xx x
∂ ∂= − = − ≈ + ∂ ∂ i i
上式即是绝对误差传播公式。式中,
*
1
f
x
∂ ∂
和
*
2
f
x
∂ ∂
分别是 *
1x 和 *
2x 对 y
*的绝对误差增长
因子,它们分别表示绝对误差 e(x1)和 e(x2)经过传播后增大或缩小的倍数。
由相对误差的定义,有
* *
* 1 2
* * *
1 2
* ** *
* *1 2
1 2* *
1 2
( ) ( )( )( )
( ) ( )
r
r r
e x e xe y f fe y
y x y x y
x xf fe x e x
y x y x
∂ ∂= ≈ + ∂ ∂
∂ ∂= ⋅ + ⋅ ∂ ∂
上式就是相对误差传播公式。式中
*
1
*
1
x f
y x
∂ ∂
和
*
2
*
2
x f
y x
∂ ∂
分别是*
1x 和
*
2x 对 y
*的相对误
MATLAB 数值计算实战
·94·
差增长因子,它们分别表示相对误差*
1( )
re x 和
*
2( )
re x 经过传播后增大或缩小的倍数。
将上述误差传播公式推广到一般的多元函数 1 2( , , , )
ny f x x x= � 中,有
*
1
( ) ( )n
i
i i
fe y e x
x=
∂ ≈ ∂
∑ i
*
1
( ) ( )n
i
i i
fy x
xε ε
=
∂ ≈ ∂
∑ i
**
* *
*
1
( ) ( )n
i
r r i
i i
x fe y e x
y x=
∂ ≈ ∂
∑ i
* **
* *
** *
1 1
( )( )( ) ( )
n n
i i
r r i
i ii i
x xy f fy x
x y xy y
εεε ε= =
∂ ∂ = ≈ = ∂ ∂
∑ ∑i i
由上式可知,误差增长因子的绝对值很大时,数据误差在运算中传播后,可能会造成
结果的很大误差。
下面给出两数和、差、积与商的误差传播公式:
1 2 1 2
* *
1 2 2 1 1 2
*
*1 1
1 2 2* * 2
2 2 2
* *
* * *1 2
1 2 1 2* * * *
1 2 1 2
* * *
1 2 1 2
* * *1
1 2 2
2
( ) ( ) ( )
( ) ( ) ( )
1( ) ( ) ( 0)
( )
( ) ( ) ( )
( ) ( ) ( )
( ) ( ) ( 0)
r r r
r r r
r r r
e x x e x e x
e x x x e x x e x
x x
e e x e x x
x x x
x x
e x x e x e x
x x x x
e x x e x e x
x
e e x e x x
x
± ≈ ± ≈ + ≈ − ≠
± ≈ ±± ±
≈ +
≈ − ≠
【例 3-3】设 2018a = , 2017b = ,分别取它们具有 8 位有效数字的近似值,并估
计 a-b的有效数字位数。
【分析】要获得 a和 b具有 8位有效数字的近似值,可以使用 vpa函数结合 char函数
和 str2double函数实现,知道了 a和 b的近似值后,可以调用 getdigits函数求得 a和 b近
似值的误差限,然后根据误差传播公式得到 a-b的误差限,进而可以反推出 a-b的有效数
字位数。下面给出实现语句如下: >> format longG % 设置数字显示方式
>> a=sqrt(2018); b=sqrt(2017); % a和 b的精确值
>> a1=str2double(char(vpa(a,8))) % a的具有 8位有效数字的近似值
a1 =
第 3章 数值分析概述
·95·
44.922155
>> [n1,e1]=getdigits(a,a1) % 获取近似值 a1的有效数字位数和误差限
n1 = % 返回 8位说明生成的 8位有效数字的近似值是正确的
8
e1 =
1/2000000
>> b1=str2double(char(vpa(b,8))) % b的具有 8位有效数字的近似值
b1 =
44.911023
>> [n2,e2]=getdigits(b,b1) % 获取近似值 b1的有效数字位数和误差限
n2 =
8
e2 =
1/2000000
>> e=e1+e2 % 近似值 a1-b1的最大误差限
e =
1/1000000
>> m=ceil(log10(abs(a1-b1))) % 科学表示式 x=±0、a1a2…an×10m中 m的值
m =
-1
>> n=fix(log10(10^m/(2*e))) % 近似值有效数字的最小位数,由不等式m-n
1×10 e
2≤ 反推得到
n =
4
应该指出的是,由误差估计式得出绝对误差限和相对误差限时,由于取了绝对值并用
三角不等式放大,因此是按最坏情形给出的,所以由此得出的结果是很保守的。
3.3 数值算法概述
数值算法是数值分析的灵魂,数值算法的好坏直接影响数值分析的结果。本节首先从
宏观角度介绍数值分析中的一种非常重要而且使用频率最高的数值算法——迭代法,然后
介绍数值算法的稳定性和病态问题。
3.3.1 迭代法
1.简单迭代法
简单迭代法是指只利用前一次的旧值即可推出新值的一种迭代法,其迭代关系式可以
写为:
1( )
n n+ =x f x 或
1, 1 1 1, 2, ,
2, 1 2 1, 2, ,
, 1 1, 2, ,
( , , , )
( , , , ), 1,2,3,
( , , , )
n n n m n
n n n m n
m n m n n m n
x f x x x
x f x x xn
x f x x x
+
+
+
= = = =
�
�
�
������
�
MATLAB数值计算实战
·96·
这样,只需给出迭代初值和迭代次数,就可以根据上面的迭代关系式给出简单迭代法
求值的MATLAB实现程序,程序代码如下: 1. function varargout=simple_iteration(fun,x0,n)
2. % SIMPLE_ITERATION 简单迭代法求值 3. if nargin<3
4. n=100; % 默认迭代次数 5. end
6. x=zeros(n+1,length(x0)); % 预置零矩阵存储每次迭代 7. for k=1:n+1
8. x(k,:)=x0; % 将每次迭代得到的值存入矩阵
9. x0=feval(fun,x0); % 迭代计算 10. end
11. if nargout==1 % 若只有一个输出参数,则以表格型数据输出 12. varargout{1}=table((1:n+1).',x,'VariableNames',{'Num','Series'});
13. else % 若有两个输出参数,则分别输出迭代次数序列和迭代序列值
14. [varargout{1:2}]=deal((1:n+1).',... % 第一个输出参数为迭代次数序列
15. x); % 第二个输出参数为迭代序列 16. end
【例 3-4】用迭代法求正数 a的平方根 x a= 。已知求平方根的迭代公式为:
1
1
2n n
n
a
x x
x+
= +
给定迭代初值 x0=1,迭代次数 n=4,试求 a的近似值并返回近似值的有效数字位数。
【分析】直接调用 simple_iteration 函数即可求得平方根的近似值,然后调用 getdigits
函数可以求得近似值的有效数字位数,编写如下语句: >> format long >> a=2; >> fun=@(x)(x+a/x)/2; >> [~,x]=simple_iteration(fun,1,4) x = 1.000000000000000 1.500000000000000 1.416666666666667 1.414215686274510 1.414213562374690 >> n=getdigits(sqrt(a),x(end)) n = 12
【例 3-5】已知 Clifford系统可以由以下三角函数迭代式描述:
1
1
1
sin( ) cos( )
sin( ) cos( )
sin( )
n n n n
n n n n
n n
x ay z bx
y z cx dy
z e bx
+
+
+
= − = − =
其中,参数 a=2.24,b=0.43,c=-0.65,d=-2.43,e=1.0,试选取初值 x0=-10,y0=-0.1,
z0=-1,迭代 20000次,绘制迭代序列的相图。
第 3章 数值分析概述
·97·
【分析】设
n
n n
n
x
y
z
=
x ,则
2 3 1
3 1 2
1
1
sin( ) cos( )
sin( ) cos( )
sin( )
n n n
n n n n
n
a b
c d
e b
< > < > < >
< > < > < >+
< >
− = −
x x x
x x x x
x
,这样就可以调用
simple_iteration 函数计算迭代序列,然后利用 plot3 函数绘制迭代序列的相图,编写如下
语句: >> fun=@(a,b,c,d,e)@(x)[sin(a*x(2))-x(3)*cos(b*x(1)); x(3)*sin(c*x(1))-cos(d*x(2));
e*sin(b*x(1))];
>> a=2.24;b=0.43;c=-0.65;d=-2.43;e=1;
>> x0=[-10,-0.1,-1];
>> [~,x]=simple_iteration(fun(a,b,c,d,e),x0,20000);
>> plot3(x(2:end,1),x(2:end,2),x(2:end,3),'k.','MarkerSize',1)
程序的运行结果如图 3-4所示。
图 3-4 Clifford系统迭代序列相图
【例 3-6】有一对兔子,从出生后的第 3个月起每个月都生一对兔子,小兔子长到第 3
个月后每个月又生一对兔子,假设所有的兔子都不死,试求 3年内每个月的兔子对数。
【分析】假设第 n个月的兔子对数为 Fn,其中年龄分别为 1个月及 2个月以上的兔子
对数分别为 an和 bn,显然,1 1
2 2
1, 0
0, 1
a b
a b
= = = =
,且
1
1
n n
n n n n
a b
b a b F
+
+
= = + =
因此
MATLAB数值计算实战
·98·
1 1 1 1 1
1 1 1 2 2 21, 1
n n n n n n nF a b b b F F
F a b F a b
+ + + + −= + = + = + = + = = + =
为能调用 simple_iteration函数,需要将上述迭代式进行改造。设1
n
n
n
F
F +
=
x ,则
2
1 1
1 2 1
2 1
n n n
n
n n n n n
F F
F F F
< >+ +
+ < > < >+ +
= = = + +
x
x
x x
这样就可以调用函数 simple_iteration编写如下语句: >> x0=[1;1];
>> fun=@(x)[x(2);x(1)+x(2)];
>> s=simple_iteration(fun,x0,35);
>> F=reshape(s.(2)(:,1),6,[]).'
F =
1 1 2 3 5 8 13 21 34 55 89 144
233 377 610 987 1597 2584
4181 6765 10946 17711 28657 46368
75025 121393 196418 317811 514229 832040
1346269 2178309 3524578 5702887 9227465 14930352
�说明:本题中的数列{Fn}称为 Fibonacci 数列,又称为黄金分割数列,因为当 n 趋向于
无穷大时,前一项与后一项的比值越来越逼近黄金分割比 0.618。关于 Fibonacci
数列还有很多“优美”的性质,感兴趣的读者可以查阅相关文献进行学习。
2.线性随机IFS迭代法
线性随机 IFS迭代是分形理论的内容,而且还是分形理论的一个重要分支。它将待生
成的图像看成是由许多与整体相似的(自相似)或经过一定变换后与整体相似的(自仿射)
小块拼贴而成。相似变换是仿射变换的一个特例,仿射变换的数学表达式为
:
x ax by e
y cx dy fω
′ = + + ′ = + +
或 :
x a b x e
c dy y fω
′ = + ′
其中,ω代表仿射变换,x和 y是变换前图形的坐标值,x'和 y'是变换后图形的坐标值;a,
b,c,d,e,f是仿射变换系数,其中a b
c d
对图形进行缩放、旋转、对称、错切等变换,
e
f
对图形进行平移变换。
对于一个比较复杂的图形,可能需要多个不同的仿射变换来实现,仿射变换族{ωn}
控制着图形的结构和形状,由于仿射变换的形式是相同的,所以不同的形状取决于仿射变
换的系数。另外,仿射变换族{ωn}中,每一个仿射变换被调用的概率不一定是等同的,也
第 3 章 数值分析概述
·99·
就是说,落入图形各部分中点的数目不一定相同,这就需要引进一个新的量,即仿射变换
ω被调用的概率 P。从而,6个仿射变换系数{a,b,c,d,e,f }和一个概率 P便组成了
线性随机 IFS迭代最关键的部分——IFS码。
一般来讲,仿射变换 ω被调用的概率 P取决于仿射变换子图的面积,子图面积越大,
落入该子图的点数就越多,此子图所对应的仿射变换系数被选中的概率就越大,也就是此
子图对应的概率值越大。
下面我们简单介绍一下 IFS码的获取,更加详细的介绍,
读者可以参考相关书籍。以 Sierpinski 垫片为例,Sierpinski
垫片可以看成一个 ABC∆ 被均分成 4部分,然后将中间的三
角形抠去,剩下 3个小三角形①②③的一种操作结果,如图
3-5所示。
由图 3-5可以看到,每一个小三角形的边长均为原三角
形的一半(x,y方向上同时缩小 0.5倍),而且,小三角形①
没有平移;小三角形②可以看成由小三角形①沿 x轴正向平
移 0.5 个单位得到;小三角形③可以看成由小三角形①沿 x
轴正向平移 0.25 个单位,沿 y轴正向平移3
4个单位得到。于是 Sierpinski 垫片可以用以
下 3个仿射变换来表示:
1
2
3
0.5 0 0:
0 0.5 0
0.5 0 0.5:
0 0.5 0
0.250.5 0:
0 0.5 3 / 4
x x
y y
x x
y y
x x
y y
ω
ω
ω
′ = + ′ ′ = + ′ ′ = + ′
小三角形
小三角形
小三角形
①
②
③
由于生成的 3 个小三角形的面积相等,所以可以让 ω1,ω2,ω3出现的概率相同或相
近,这里取 1 2 3
1
3p p p= = = ,这样即得到 Sierpinski垫片的一种 IFS码,如表 3-1所示。
表 3-1 Sierpinski垫片的一种IFS码
i ai bi ci di ei fi pi
1 0.5 0 0 0.5 0 0 1/3
2 0.5 0 0 0.5 0.5 0 1/3
3 0.5 0 0 0.5 0.25 3 / 4 1/3
下面给出线性随机 IFS迭代的算法步骤。
(1)生成在区间[0,1]上服从均匀分布的随机数 R。
图 3-5 Sierpinski垫片的构造
MATLAB数值计算实战
·100·
(2)分配仿射变换 ω1,ω2,…,ωn的概率空间,分别为
2 2 3 2 1 1
1 1
1 1 1 1 1 1
[0, ], ( , ], ( , ], ( , ], ( ,1]n n n
i i i i i i
i i i i i i
p p p p p p p p
− − −
= = = = = =∑ ∑ ∑ ∑ ∑ ∑� 。
(3)判断随机数落入哪一个概率空间,并调用相应的仿射变换。
(4)根据仿射变换关系式,计算仿射变换后的 x',y'值并储存。
(5)循环执行第(1)~(4)步的过程 N-1次,并将上一次计算出的 x'、y'值作为这
一次的 x、y值参与计算。
根据上面的步骤可以编写相应的程序绘制 Sierpinski垫片,程序代码如下: 1. IFS=[0.5,0,0,0.5,0,0,1/3; 2. 0.5,0,0,0.5,0.5,0,1/3;
3. 0.5,0,0,0.5,0.25,sqrt(3)/4,1/3]; % IFS码
4. n=30000; % 迭代次数
5. x0=rand(2,1); % 迭代初值
6. X=[x0,zeros(2,n)]; % 预置初始矩阵存储每次的迭代值 7. for k=1:n
8. r=rand; % 产生[0,1]上的一个随机数
9. if r<=IFS(1,end) % 若随机数属于第一个仿射变换 10. x=[IFS(1,[1,2]);IFS(1,[3,4])]*x0+IFS(1,[5,6]).';
11. elseif r<=IFS(1,end)+IFS(2,end) % 若随机数属于第二个仿射变换 12. x=[IFS(2,[1,2]);IFS(2,[3,4])]*x0+IFS(2,[5,6]).';
13. else % 若随机数属于第三个仿射变换 14. x=[IFS(3,[1,2]);IFS(3,[3,4])]*x0+IFS(3,[5,6]).'; 15. end
16. X(:,k+1)=x; % 将迭代值存入矩阵 X中
17. x0=x; % 更新迭代初值 18. end
19. plot(X(1,:),X(2,:),'k.','markersize',1) % 绘制 Sierpinski垫片
20. axis equal % 设置坐标轴显示方式
程序的运行结果如图 3-6所示。
图 3-6 IFS迭代绘制的 Sierpinski垫片
第 3章 数值分析概述
·101·
为使上述程序具有通用性,可以将上述程序稍加修改,修改后的函数语句如下: 1. function ifs_draw(IFS,n)
2. % IFS_DRAW 线性随机 IFS迭代 3. if nargin==1
4. n=30000; % 默认迭代次数 5. end
6. p=IFS(:,end); % 每个仿射变换被调用的概率
7. P=cumsum(p); % 概率累加,得到每个仿射变换的概率空间
8. x0=rand(2,1); % 迭代初值
9. X=[x0,zeros(2,n)]; % 预置初始矩阵存储每次的迭代值 10. for k=1:n
11. d=rand-P; % 计算随机数与概率空间向量的差值
12. ind=find(d<=0); % 找出随机数所属的概率空间的位置 13. x=[IFS(ind(1),[1,2]);IFS(ind(1),[3,4])]*x0+IFS(ind(1),[5,6]).';
% 计算迭代点
14. X(:,k+1)=x0; % 将迭代值存入矩阵 X中
15. x0=x; % 更新迭代初值 16. end
17. plot(X(1,:),X(2,:),'k.','markersize',1) % 绘制 IFS图形
18. axis equal % 设置坐标轴显示方式
这样,只需在命令窗口中执行如下语句即可得到图 3-6中所示的图形。 >> IFS=[0.5,0,0,0.5,0,0,1/3; 0.5,0,0,0.5,0.5,0,1/3; 0.5,0,0,0.5,0.25,sqrt(3)/4,1/3]; >> ifs_draw(IFS)
下面再看一个例子,已知某树木的 IFS码如表 3-2所示。
表 3-2 树木的IFS码
i ai bi ci di ei fi pi
1 0.06 0 0 0.6 0 0 0.1
2 0.04 0 0 -0.5 0 1 0.1
3 0.46 0.32 -0.34 0.38 0 0.6 0.1
4 0.48 -0.15 0.17 0.42 0 1 0.23
5 0.43 0.37 -0.26 0.48 0 1 0.23
6 0.42 -0.36 0.35 0.31 0 0.8 0.24
则可以调用 ifs_draw函数绘制树木的 IFS分形图,编写语句如下: >> M=[0.06 0 0 0.6 0 0 0.1; 0.04 0 0 -0.5 0 1 0.1; 0.46 0.32 -0.34 0.38 0 0.6 0.1; 0.48 -0.15 0.17 0.42 0 1 0.23; 0.43 0.37 -0.26 0.48 0 1 0.23; 0.42 -0.36 0.35 0.31 0 0.8 0.24]; >> ifs_draw(M,50000)
运行结果如图 3-7所示。
MATLAB数值计算实战
·102·
图 3-7 IFS迭代绘制的树木
3.复平面上的迭代
复平面上的迭代也属于分形几何学的范畴。复平面上的迭代研究的是复平面上的映射
的迭代行为,其格式如下:
( , )z f z c← ,其中 ,z c∈�
分形几何中非常有名的 Julia 集和 Mandelbrot 集均由复平面上的二次函数2( , )f z c z c= + 迭代得到的,但两者在构造算法上有所不同。Julia 集是给定参数 c 值,搜
索 z平面上的所有点,以寻找吸引域及其边界的复杂结构,即在 z 平面中绘制图形;而
Mandelbrot集是选择一个初始的 z点,在不同的 c值下追踪其迭代点列,然后在 c平面上
记录点列的结构并绘图。下面分别介绍 Julia集和Mandelbrot集的绘制步骤。
Julia集绘制步骤如下。
(1)在 ( i )z z x y= + 平面上确定绘图区域 [ ] [ ]min max min max, ,x x y y× 并计算绘图区域的等间
隔采样点。
(2)将所有采样点代入迭代式 2
1n nz z c+ = + 进行多次迭代。
(3)当迭代过程中的某个复数 Zk的模值大于 M(M 是预定义的一个大数,若模值大
于 M,则认为 zn向∞逃逸,因此 M 称为逃逸半径)时,记录相应的迭代次数 k,否则将
最大迭代次数 N作为当前记录点。
(4)将 z平面上各采样点用对应的迭代次数着色即得到相应的 Julia集。
根据上面的步骤可以编写相应的程序,程序语句如下: 1. function Julia(xspan,yspan,c) 2. if nargin==2
3. c=-0.46+1i*0.57; % 复常数 c的默认值 4. end
5. M=2; % 逃逸半径
第 3章 数值分析概述
·103·
6. N=100; % 最大迭代次数
7. x=linspace(xspan(1),xspan(end),512); % 绘图区域 x方向采样点
8. y=linspace(yspan(1),yspan(end),512); % 绘图区域 y方向采样点
9. [X,Y]=meshgrid(x,y); % 利用向量创建矩阵
10. z=X+1i*Y; % 绘图区域的等间隔采样点
11. W=ones(size(z))*N; % 预置矩阵存储逃逸点对应的迭代次数 12. for k=1:N
13. z=z.^2+c; % 复平面迭代计算
14. W(abs(z)>M)=k; % 将当前迭代次数赋给 W中逃逸点对应的位置
15. z(abs(z)>M)=nan; % 将 z中的逃逸点去除,不再参与后面的计算 16. end
17. image(x,y,W) % 绘制 Julia集
在命令窗口中执行如下语句: >> Julia([-1.5,1.5],[-1.5,1.5]) >> axis equal off
运行结果如图 3-8所示。
Mandelbrot集绘制步骤如下。
(1)在 ( i )c c p q= + 平面上确定绘图区域 [ ] [ ]min max min max, ,p p q q× 并计算绘图区域的等间
隔采样点。
(2)确定迭代初始点 z0,并将所有采样点代入迭代式2
1n nz z c+ = + 中进行多次迭代。
(3)当迭代过程中的某个复数 zk的模值大于逃逸半径 M时,记录相应的迭代次数 k,
否则将最大迭代次数 N作为当前记录点。
(4)将 c平面上各采样点用对应的迭代次数着色即得到相应的Mandelbrot集。
图 3-8 Julia集
根据上面的步骤可以编写相应的程序,程序语句如下: 1. function Mandelbrot(pspan,qspan)
2. M=2; % 逃逸半径
3. N=100; % 最大迭代次数
MATLAB数值计算实战
·104·
4. p=linspace(pspan(1),pspan(end),512); % 绘图区域 p方向采样点
5. q=linspace(qspan(1),qspan(end),512); % 绘图区域 q方向采样点
6. [P,Q]=meshgrid(p,q); % 利用向量创建矩阵
7. c=P+1i*Q; % 绘图区域的等间隔采样点
8. W=ones(size(c))*N; % 预置矩阵存储逃逸点对应的迭代次数
9. z=0; % 迭代初值 10. for k=1:N
11. z=z.^2+c; % 复平面迭代计算
12. W(abs(z)>M)=k; % 将当前迭代次数赋给 W中逃逸点对应的位置
13. c(abs(z)>M)=nan; % 将 c中的逃逸点去除,不再参与后面的计算 14. end
15. image(p,q,W) % 绘制 Mandelbrot集
在命令窗口中执行如下语句: >> Mandelbrot([-2,0.5],[-1.25,1.25]) >> axis equal off
运行结果如图 3-9所示。
图 3-9 Mandelbrot集
�说明:上述两个函数程序中,用户只需将 for循环中的迭代式稍加修改,即可绘制广义
Julia集或广义Mandelbrot集。
3.3.2 数值算法的稳定性
解决一个计算问题往往不止一种算法,用不同算法计算的结果其精度往往也不尽相
同,甚至相差很大,这是由于初始数据的误差或计算中的舍入误差在计算过程中的传播造
成的。一个算法如果输入数据有误差,而在计算过程中舍入误差不增长,则称该算法是数
第 3章 数值分析概述
·105·
值稳定的,否则称此算法为不稳定的。换句话说,若误差传播是可控制的,则称此算法是
数值稳定的,否则称此算法为不稳定的。下面通过一个实例说明稳定的算法与不稳定的算
法之间的巨大差异性。
【例 3-7】利用数值算法计算定积分 1
0
1e d , 0,1,2,
e
n x
nx x n= =∫ �I 。
【分析】由分部积分法可得
1 1 111
00 0 0
11
10
1 1 1de e e d e e d
e e e
1 e d 1e
n x n x x n n x
n
n x
n
x x x n x x
n
x x n
−
−−
= = − = −
= − = −
∫ ∫ ∫
∫
I
I
又1
00
1 1d 1
x
e x
e e
= = −∫I ,取其具有 8位有效数字的近似值,执行如下语句:
>> format longG >> I0=1-1/exp(1);
>> I0a=str2double(char(vpa(I0,8))) % I0的具有 8位有效数字的近似值 I0a = 0.63212056 >> [n0,e0]=getdigits(I0,I0a)
n0 = % 近似值有效数字位数 8
e0 = % 近似值误差限 1/200000000
这样由递推式和初始值即可推出 I1,I2,…,I15,…的近似值,编写如下语句: 1. syms x % 声明符号变量
2. I=ones(16,1)*I0a; % 存储 0~15次的近似值
3. Is=I; % 存储 0~15次的精确值 4. for n=1:15
5. m=1-n*I(n); % 递推计算
6. I(n+1)=str2double(char(vpa(m,8))); % 将递推值存入向量 I中 7. Is(n+1)=str2double(char(vpa(int(x^n*exp(x-1),0,1),8)));
% 将精确值存入 Is中 8. end 9. s=table(I,Is,'VariableNames',{'ApproxValue','ExactValue'},...
% 返回表格形数据 10. 'RowNames',cellfun(@num2str,num2cell(0:15),'UniformOutput',false))
运行结果如下: s = ApproxValue ExactValue ___________ ___________ 0 0.63212056 0.63212056 1 0.36787944 0.36787944 2 0.26424112 0.26424112 3 0.20727664 0.20727665
MATLAB 数值计算实战
·106·
4 0.17089344 0.17089341
5 0.1455328 0.14553294
6 0.1268032 0.12680236
7 0.1123776 0.1123835
8 0.1009792 0.10093197
9 0.0911872 0.091612293
10 0.088128 0.08387707
11 0.030592 0.07735223
12 0.632896 0.071773261
13 -7.227648 0.066947937
14 102.18707 0.062732697
15 -1531.8061 0.059020996
由上述结果可知:从 n=11 开始,近似值与精确值之间的差异非常大。下面考察第 n
步的误差 nE :
* *
1 1 1 0(1 ) (1 ) !
n n n n n nE n n n E n E− − −= − = − − − = = =�I I I I
可见,此种算法中微小的误差 0E 能够迅速积累以致该算法完全失效,该算法称为不稳定
的算法。这时需要另外寻找更优的算法,此处将上述递推式稍加修改:
1
1n
n
n n− = −
II
这样可以先估计一个 IN,再反推要求的 ( )nn N<<I 。又因为
1 10 1
0 0
1 1 1 1e d e d 0 1
e e e( 1) 1
n n
n nx x x x
n n
< < ⇒ < < < <+ +∫ ∫I I
取 * 1 1 1
2 e( 1) 1N N
N N
= + ≈ + +
I I ,因此 *
15
1 1 10.042746233
2 e(15 1) 15 1
= + ≈ + +
I ,由上述修
正递推式即可推出 I0,I1,…,I14,…,编写如下语句:
1. syms x % 声明符号变量
2. I15a=str2double(char(vpa(1/2*(1/(15+1)/exp(1)+1/(15+1)),8)));
% I15的近似值
3. I=I15a*ones(16,1); % 存储 0~15次的近似值
4. Is=I0a*ones(16,1); % 存储 0~15次的精确值
5. for n=15:-1:1
6. m=1/n-I(n+1)/n; % 递推计算
7. I(n)=str2double(char(vpa(m,8))); % 将递推值存入向量 I中
8. Is(n+1)=str2double(char(vpa(int(x^n*exp(x-1),0,1),8)));
% 将精确值存入 Is中
9. end
10. s=table(I,Is,'VariableNames',{'ApproxValue','ExactValue'},...
% 返回表格形数据
11. 'RowNames',cellfun(@num2str,num2cell(0:15),'UniformOutput',false))
运行结果如下: s =
ApproxValue ExactValue
第 3章 数值分析概述
·107·
___________ ___________ 0 0.63212056 0.63212056 1 0.36787944 0.36787944 2 0.26424112 0.26424112 3 0.20727665 0.20727665 4 0.17089341 0.17089341 5 0.14553294 0.14553294 6 0.12680236 0.12680236 7 0.1123835 0.1123835 8 0.10093197 0.10093197 9 0.091612288 0.091612293 10 0.083877115 0.08387707 11 0.077351732 0.07735223 12 0.071779214 0.071773261 13 0.06687022 0.066947937 14 0.063816918 0.062732697 15 0.042746233 0.059020996
考察该种算法的误差有: * *
1 1 1
1 1 1(1 ) (1 )
N N N N N NE E
N N N− − −= − = − − − =I I I I ,依此类推,
对 n<N有:
1 2
1 1 1
1 ( 1)( 2) ( 1) ( 1)n n n N
E E E En n n n N N
+ += = = =+ + + + −
�
�
可见,该算法中的误差逐步递减,这样的算法称为稳定的算法。上述输出结果也说明了该
种算法的稳定性。
3.4 数值计算中应注意的问题
为了减小计算误差的影响,一般应注意如下问题:
1.避免两个相近的数相减
两个相近的数相减,其结果的有效数字的位数会大大损失,这在例 3-3中就有所体现。
为使 2018 2017− 具有更多位的有效数字,可以进行如下修改:
12018 2017
2018 2017
− =+
然后编写如下语句: >> format longG % 设置数字显示方式
>> a=sqrt(2018); b=sqrt(2017); % a和 b的精确值
>> a1=str2double(char(vpa(a,8))); % a的具有 8位有效数字的近似值
>> b1=str2double(char(vpa(b,8))); % b的具有 8位有效数字的近似值
>> n=getdigits(a-b,1/(a1+b1)) % 变换后的近似值具有 10位有效数字
n =
10
MATLAB 数值计算实战
·108·
下面从原理上进行简单分析。设* *
1 2,x x 分别是 x1, x2的近似值,则
* * *
1 2y x x= − 是 y= x1-x2
的近似值,此时有
* *
* * *1 2
1 2 1 2* * * *
1 2 1 2
( ) ( ) ( )r r r
x x
e x x e x e x
x x x x
− +− −
≤
可见,当* *
1 2,x x 很接近时,y
*的相对误差有可能会很大。
2.避免绝对值过小的数作为除数
设* *
1 2,x x 分别是 x1, x2的近似值,
*
* 1
*
2
xy
x= 是
1
2
xy
x= 的近似值,则由
*
*1 1
1 2 2* * 2
2 2 2
1( ) ( ) ( 0)
( )
x x
e e x e x x
x x x
+ ≠
≤
知,当 *
2x 太小时,可能导致商的绝对误差会很大。
3.避免大数“吃掉”小数
计算机上只能采用有限位数计算,因此,若参加运算的数量级差很大,则在它们的加、
减运算中,绝对值小的数往往被绝对值较大的数“吃掉”,造成计算结果失真。
【例 3-8】已知 a=109,b=1,c=-a,试利用单精度计算以下算式:
①(a+b)+c ②a+(b+c) ③(a+c)+b
【分析】显然,人为直接计算的话可得到三题的准确结果均为1,那么在计算机内的结
果到底怎样呢,见下面的语句:
>> a=single(1e9); b=single(1); c=-a;
>> (a+b)+c % b被 a吃掉
ans =
0
>> (b+c)+a % b被 c吃掉
ans =
0
>> (a+c)+b % a和 c相互抵消,不存在大数吃小数
ans =
1
这里解释一下上面出现不同结果的原因。在计算机内,109被存为 0.1×10
10
,1 存为
0.1×101。在做加法运算时,两个加数的指数先向大的指数对齐,再将浮点部分相加,即
在做 a+b运算时,1需写成 0.0000000001×1010
,利用单精度计算时有:
a+b=0.1×1010
+0.0000000001×1010
=0.10000000×1010
=109
因此计算结果为(a+b)+c=0,即 b被 a吃掉了。其他同理。
第 3章 数值分析概述
·109·
4.先化简再计算,减少计算步骤,避免误差积累
在处理一个数值计算问题中,若能减少其运算次数,不但可以节省计算时间,还可以
减少误差的积累,这在数值计算中是十分重要的。
【例 3-9】计算多项式1
1 1 0( ) n n
n nP x a x a x a x a
−−= + + + +� 在点 x=x0处的值。
【分析】本题若直接计算 ( 0,1,2, , )k
ka x k n= � ,再逐项相加,则一共需做
( 1)1 2
2
n n
n
++ + + =�
次乘法和 n次加法运算。编写如下函数语句: 1. function s=polyvalue(p,x0)
2. % POLYVALUE 多项式求值的直接算法
3. n=length(p); % 返回多项式系数最高次数 n-1
4. s=p(n); % 取多项式的常数项
5. for i=2:n % 加法 n-1次
6. t=p(n-i+1); % 多项式各系数
7. for j=1:i-1 % i-1次乘法
8. t=t*x0; % 求多项式的各项
9. end
10. s=s+t; % 累加求多项式的值
11. end
这样若要计算多项式5 3( ) 8 4 9 1p x x x x= + − + 在点 x=3处的值,只需在命令窗口中执行
如下语句: >> tic,s=polyvalue([8,0,4,0,-9,1],3),toc
s =
2026
Elapsed time is 0.073699 seconds.
对于多项式求值,我国数学家秦九韶早在南宋时期就提出了一种简化算法,称为秦九
韶算法。在西方被称为霍纳算法。该算法是一种将 n次多项式的求值问题转化为 n个一次
式的算法,大大简化了计算过程。将多项式 1
1 1 0( ) n n
n nP x a x a x a x a
−−= + + + +� 改写成如下
形式:
1
0
, ( 1, 2, ,1,0)
( )
n n
k k k
S a
S xS a k n n
P x S
+
= = + = − − =
�
该算法只需要执行 n次乘法和 n次加法即可。下面给出秦九韶算法的MATLAB实现
程序,代码如下: 1. function s=horners(p,x0)
2. % HORNERS秦九韶算法
3. n=length(p); % 多项式次数为 n-1次
4. s=p(1); % 最高次系数
5. for k=2:n
MATLAB数值计算实战
·110·
6. s=x0*s+p(k); % 递推式
7. end
在命令窗口中执行如下语句: >> tic, s=horners([8,0,4,0,-9,1],3),toc
s =
2026
Elapsed time is 0.000808 seconds.
由以上执行结果可以发现,利用秦九韶算法和直接代入法得到的结果是一样的,但显
然秦九韶算法所消耗的时间要少得多。
5.采用数值稳定性好的算法
前面的例 3-7已经充分说明好的算法决定数值计算的成与败。在计算过程中,由于原
始数据本身就存在误差,且每次又有可能产生舍入误差。这样由于误差的积累与传播则很
可能淹没真解,使得计算结果变得相当不可靠。因此,选择数值稳定性好的算法就显得尤
为重要。