数学中,一个十分基本的问题是求给定方程的解。我们如果掌握了计算机编程解方程的手段,则对于能列出方程式的各类题目,都能得以解决。本节将讨论如何在计算机上求解各种形式的方程。如下列方程: 4X5-3X4+2X3+6X2+5X-7=0
X+SinX-3=0
X10-10X=0
LgX-X2+7X+5=0
它们的一般形式可表示为:
f(x)=0
方程的根或解是使f(x)等于0的那些X的值。这里,我们仅考虑X为实数的情况。方程f(x)=0可能无根,可能有单根、多根,甚至有无穷多根。我们在这里所采用的方法,都为叠代求根过程,这个过程一直持续到两个近似值足够接近,从实用上可以认为是相等时为止。运用这种方法求方程的根,已经有几百年的历史,只是到计算机不断发展的今天,才真正认识它的潜力。因为这种叠代求根的方法非常适用于计算机。下面我们讨论解方程的三种叠代方法。在整个讨论中,我们假定函数y=f (x)是连续不断的曲线,即在所有的x值上都有定义。
一、对分法
对分法能保证产生f(x)=0的某个根的近似值序列,在这个意义上,对分法是一种可靠的方法。
对分法要求将方程改写成f(x)=0的形式,然后y=f(x)。先选取x1和x2两点,使f(x1)和f(x2)异号。由于y=f(x)是连续不断的曲线,故在x1和x2之间至少有一个根存在。
我们将可变区间[x1、x2]两等分,求出其中点,即x3=(x1+x2)/2,然后求出函数在中点处的值f(x3)。若f(x3)与f(x1)同号,如图所示,则新的可变区间就是[x3,x2],x3与x2也为异号,方程的根仍在这新区间中,而新区间正好是原区间长度的一半。然后再求出[x3,x2]的中点x4,根据f(x4)的符号,判明应该用x4来代替x2,还是x3:如果f(x4)与f(x2)同号,则x4代替x2;反之f(x4)与f(x2)异号,则用x4代替x3。如此重复叠代下去,直至可变区间之长度小于某个预先给定的值为止,或者达到f(x) = 0,如可变区间的长度小于δ,则方程的根的误差小于ε=δ/ 2。
在编写对分法的计算机程序时,上述过程可作两点细致的改进:
①只用x1、x2、xb三个变量,其中xb = (x1+x2)/2,而不是用一系列的下标变量x1、x2、x3、x4……,来表示逐次近似值,这样会更方便和更节省。每次叠代,根据f(xb)的符号,将xb赋值给x1或x2。
②不管f(xb)和f(x1)是否同号,可以方便地由乘积f(xb)*f(x1)的符号来确定,是将xb赋值给x1还是x2。如果f(xb)*f(x1) > 0,则x1 := xb;反之x2 := xb。
例 2-2-1 求方程 X10-10X=0在区间[1,2]之间的一个根。
var
x1,x2,xb : real;
function f(x : real) : real;
begin
f := exp(10*ln(x))-exp(x*ln(10));
end;
begin
x1 := 1;
x2 := 2;
repeat
xb := (x1+x2)/2;
if f(xb)*f(x1) > 0
then x1 := xb
else x2 := xb;
until (abs(x2-x1) < 0.00000001) or (f(xb) = 0);
writeln('X = ',xb:0:5);
writeln('F(x) = ',f(xb):0:5);
readln;
end.
例2-2-2 有形如:ax3+bx2+cx+d=0这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在 -100至100之间),且根与根之差的绝对值≥1。
要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后5位。
提示:记方程f(x)= 0,若存在2个数x1和x2,且x1<x2,f(x1)*f(x2)<0,则在(x1,x2)之间一定有一个根。
输入:a,b,c,d
输出:三个实根(根与根之间留有空格)
输入输出样例:
输入:1 -5 -4 20
输出:-2.00000 2.00000 5.00000
var
a,b,c,d,x1,x2,xb : real;
i : shortint;
function F(x : real) : real;
begin
F := ((a*x+b)*x+c)*x+d;
end;
begin
write('Input a,b,c,d : ');
readln(a,b,c,d);
write('Output : ');
for i := -100 to 99 do begin
x1 := i; x2 := i+1;
if abs(F(x1)) < 0.00000001 then write(' ',x1:0:5);
if F(x1)*F(x2) < 0 then begin
xb := (x1+x2)/2;
repeat
if F(x1)*F(xb) > 0 then x1 := xb else x2 := xb;
xb := (x1+x2)/2;
until abs(x2-x1) < 0.00000001;
write(' ',xb:0:5);
end;
end;
if abs(F(100)) < 0.00000001 then write(' 100.00000');
writeln; readln;
end.
本方法的优点是能产生切实的解,且解题思路清淅;其不足之处在于要输入x1和x2两个值,而且使f(x1)和f(x2)异号,操作上较麻烦。
二、逐次逼近法
第二种叠代方法是先把方程改写成x = f(x)的形式,以使变量x能明显地出现。例如方程:
X2 – X1/2- 3 = 0
我们可将之改写成:
X = (3 + X1/2)1/2
一般地说将方程改写成x = f(x)的形式,存在着多种方法。
而从方程x = f(x)的一个根的初始近似值x1出发,可以定义
x2 = f(x1)
x3 = f(x2)
.
.
.
xn+1 = f(xn)
若逐次近似值x1、x2、x3、x4、……最后趋于数值α,使得
α = f(α)
则α就是方程的根。
例 2-2-3 用逐次逼近法求方程 X2 – X1/2- 3 = 0 的根。
改写成x = f(x)形式,则为:X = (3 + X1/2)1/2
var
x1,x2 : real;
i,n : byte;
function f1(x : real) : real;
begin
f1 := x*x-sqrt(x)-3;
end;
function f2(x : real) : real;
begin
f2 := sqrt(3+sqrt(x));
end;
begin
write('Input times : ');
readln(n);
x1 := 2;
i := 0;
repeat
i := i+1;
x2 := f2(x1);
x1 := x2;
until i = n;
if abs(x2-x1) > 0.00000001
then writeln('No solution !')
else begin
writeln('X = ',x2:0:5);
writeln('f(x) = ',f1(x2):0:0);
end;
readln;
end.
本方法的优点是处理简单,只需赋一个初值x1,输入叠代次数N;但不足之处是有时会没解。
如图一所示,保证有解的条件是:对于包含全部逐次逼近的值x1、x2、x3、x4、……的区间上所有的点f(x)的斜率绝对值,应小于1。由于将方程改写成x = f(x)有多种形式,因而有时需用试用几种不同的改写方式,使其有解。
三、牛顿-拉富生法
到此为止,我们讨论的两种方法,往往需要较多的叠代次数,才能保证正常的精确度的要求,这对于对分法而言确定如此,但逐次逼近法有时也能很快求得其解,但不会都是如此。
牛顿-拉富生法只要函数可求得导数,就往往能用较少的叠代次数,求得确定的解。
这种方法简述如下:
先取f(x) = 0 的根的近似值x1,然后定义下式来改进这个近似值:
x2 = x1 – d
= x1 – f(x1) / tg(t)
= x1 – f(x1) / f’(x1)
导数f’(x1) 就是 tg(t)
因此我们得到牛顿-拉富生法叠代求解的通用公式:
Xn+1 = Xn – f(Xn) / f’(Xn)
例 2-2-4 采用牛顿-拉富生法,求方程 X2 - 3 = 0的一个大于0的根。
var
x1,x2 : real;
i,n : byte;
function f1(x : real) : real;
begin
f1 := x*x-3;
end;
function f2(x : real) : real;
begin
f2 := x-(x*x-3)/(2*x);
end;
begin
x1 := 2;
repeat
x2 := f2(x1);
if abs(x1-x2) < 0.00000001 then break;
x1 := x2;
until false;
writeln('X = ',x2:0:8);
writeln('f(x) = ',f1(x2):0:8);
readln;
end.
牛顿-拉富生法虽然需要求函数的导数知识,但其叠代次数较少,程序较简单,不失为一种优良的解方程的方法。此法实际上就是计算机用子程序求平方根、立方根等等的方法。