1.5.2 减小运算误差
在数值计算过程中,由于计算工具只能对有限位数进行运算,因而在运算过程中不可避免地要产生误差。如果能够掌握产生误差的规律,就可以把误差限制在最小的范围之内。而实际上,在运算过程中所产生的误差的大小,通常又与运算步骤有关。一般来说,在分析运算误差时,要考虑以下一些原则。
(1)两个相近的数相减,会严重损失有效数字
对这个问题可通过相对误差的概念加以说明。设
y=x-A
其中,A和x均为准确值。为计算简单,设A运算时不发生误差,而x有误差,其近似值为x*,由此可估计当用x*近似代替x时,y的相对误差
由此可以看出,在x的绝对误差ε(x*)不变时,x*越接近A,则y的相对误差εr(y*)会变得越大,而相对误差的增大必然会导致有效数字位数的减少。
从数值计算实例看,已知2.01和2皆为准确数,计算,使其有3位有效数字。当取和都是3位有效数字时,有
此时计算结果只有1位有效数字,为避免两个相近的数相减造成有效数字位数的减少,往往需要对具有减法运算的公式改变计算方法,如通过因式分解、分母有理化、三角公式、泰勒展开等,防止相近的数减法运算的出现。例如,对u的计算进行如下处理
这样用3位有效数字进行计算,其结果也有3位有效数字。
下面是一些常见的公式变换的例子。
当x1和x2接近时,
当x接近0时,
当x充分大时,
当f(x*)和f(x)很接近,但又需要进行f(x)-f(x*)运算时,为避免有效数字的丢失,可用泰勒展开式
取右端的有限项近似左端。
如果计算公式不能改变,则可采用增加有效数字位数的方法。上例中当和都取6位有效数字时,结果有3位有效数字,即
(2)防止大数“吃掉”小数
计算机的位数有限,因此在进行加减法运算时,要对阶和规格化。对阶是以大数为基准,小数向大数对齐,即比较相加减的两个数的阶,将阶小的尾数向右移,每移一位阶码加1,直到小数阶码与大数阶码一致时为止,并将移位后的尾数多于字长的部分进行四舍五入,然后对尾数进行加减运算,最后将尾数变为规格化形式。当参加运算的两个数的数量级相差很大时,若不注意运算次序,就有可能把数量级小的数“吃掉”。例如,在四位浮点机上进行运算
0.7315×103+0.4506×10-5
对阶是0.7315×103+0.0000×103,规格化是0.7315×103,结果是大数“吃掉”了小数。又如
0.8153+0.6303×103
对阶是0.0008×103+0.6303×103,规格化是0.6311×103,结果是大数“吃掉”了部分小数。再如,已知A=105,B=5,C=-105。若按(A+B)+C进行计算,则结果接近于零,结果失真;若按(A+C)+B进行计算,则结果接近于正确的结果5。
防止大数吃掉小数特别要防止重要的物理量被“吃掉”。
例如,求解方程x2-(105+1)x+105=0,从因式分解可知其两个根分别是105和1。若用5位机求解时,b=-(105+1),对阶和规格化后是-105,再按求根公式求出两个根是105和0。在有些情况下,允许大数“吃掉”小数,如在计算105这个根时。而在另一些情况下则不允许,如计算另一个根0时,可将计算公式加以改变。例如,用求解,即,从而对这个根进行了“保护”。
例1-16 在5位十进制计算机上计算
其中0.1≤δi≤0.9。
解
大数“吃掉”了小数,结果显然不可靠。
如果改变计算顺序,先把数量级相同的1000个δi相加,再和52492相加,就不会出现大数“吃掉”小数的情况,这时
于是有
0.001×105+0.52492×105≤A≤0.009×105+0.52492×105
52592≤A≤53392
因此要注意运算顺序,防止大数“吃掉”小数。如多个数相加,应按绝对值由小到大的顺序相加。
(3)绝对值太小的数不宜为除数
设x1,x2的近似值分别是的近似值,有算术运算误差
显然,当除数很小时,近似值z*的绝对误差e(z*)有可能很大,除法运算中应尽量避免除数的绝对值远远小于被除数的绝对值。当x1,x2都是准确值时,由于很大,会使其他较小的数加不到中,而引起严重后果,或者会使计算机计算时“溢出”,导致计算无法进行下去。
在数值计算中,除数的绝对值远小于被除数的绝对值,将会使商的数量级增加,甚至会在计算机中造成“溢出”停机,而且绝对值很小的除数稍有一点误差,就会对计算结果影响很大。
例如,=3141.6,当分母变为0.0011,即分母只有0.0001的变化时,有
商却起了巨大变化。因此,在计算过程中,还应注意避免用绝对值小的数当除数。
(4)简化计算步骤,减少运算次数
运算过程的每一步都有可能产生误差,而且这些误差都还有可能传递到下一步去,这种传递有时是增大的,有时是减小的。同时,运算过程的每一步产生的误差,也可能会积累到最终的结果中去,只不过这种误差的积累有时是增加的,有时则因互相抵消而减少。总而言之,在运算过程中都有可能引起导致结果误差增大的误差传播或误差积累问题。因此,在数值计算中,必须考虑尽量简化计算步骤。这样一方面可以减小计算量,另一方面由于减少了运算次数,从而减少了产生误差的机会,也可能使误差积累减小。
例如计算x255,如果逐个相乘,要进行254次乘法,但若改变成
x 255 =xx 2 x 4 x 8 x 16 x 32 x 64 x 128
只要14次乘法运算。当改成x255=(((((((x2)2)2)2)2)2)2)2/x,只要8次乘法和1次除法。
又如计算多项式
p(x)=anxn+an-1xn-1+…+a1x+a0
的值,若直接计算akxk再逐项相加,一共需进行
次乘法和n次加法。若采用从后往前计算的方法,即x的k次幂等于其k-1次幂再乘上x,从后往前k+1项的部分和uk等于后k项的部分和再加上前一项akxk,这样,逐项求和的方法可以归结为如下的递推关系
其初值
这时,利用初值,对k=1,2,…,n反复利用递推关系进行计算,最后可得un=p(x)。为了计算一个x点处的函数值p(x),利用这种方法共需进行2n次乘法和n次加法。还能不能减少乘法的次数呢?如果采用秦九韶算法,即从多项式前n项提出x,则有
p(x)=(anxn-1+an-1xn-2+…+a1)x+a0
经过这个手续,括号内得到的是一个n-1次多项式(注意降了一次)。如果对括号内再施以同样的手续,进一步有
p(x)=((anxn-2+an-1xn-3+…+a2)x+a1)x+a0
这样每进行一步,最内层的多项式降低一次,最终可加工成如下嵌套形式
p(x)=(…(anx+an-1)x+…+a1)x+a0
利用上式结构上的特点,从里往外一层一层地计算。设用vk表示第k层(从里面数起)的值
vk=xvk-1 +an-k,k=1,2,…,n
作为初值,这里令v0=an。
写成递推形式
这样,多项式函数求值只要用一个简单的循环就能完成,在这个循环当中一共只要进行n次乘法和n次加法就够了,充分利用递推公式,对提高计算效率往往很有好处。
为便于理解递推形式(1-17)的计算过程,将f(x)按降幂排列的系数写在第一行,把要求值点x0及vkx0写在第二行,第三行为第一、二两行相应之和vk,最后得到的vn即为所求f(x0)的值,即
例1-17 求f(x)=2-x2+3x4在x0=2的值。
解
多项式求值的这种算法称为秦九韶算法,它是我国宋代数学家秦九韶最先提出的。秦九韶算法的特点在于它通过一次式的反复计算,逐步得出高次多项式的值。具体地说,它将一个n次多项式的求值问题,归结为重复计算n个一次式来实现。这种化繁为简的处理方法在数值分析中是屡见不鲜的。
又如要计算和式的值,如果直接逐项求和,运算次数多且误差积累,但可进行化简处理
则整个计算只要一次求倒数和一次减法。
为了减少计算时间还应考虑充分利用耗时少的运算。例如,k+k比2k,a×a比a2,b×0.25比要节省运算时间。