====== Численное решение уравнений итерационными методами ====== Данная статья посвящена //нахождению корней уравнения// F(x)=0, (1) , где функция //F(x)// может быть алгебраической либо трансцендентной и должна удовлетворять условию дифференцируемости. Как правило, численное решение уравнений состоит из двух этапов: нахождение приближенного значения корня и его уточнение до заданной точности. Начальное приближение часто известно из физических соображений либо находится специальными методами, например, графически. Подробно будет рассмотрен второй этап решения уравнений: нахождение корня с заданной точностью различными итерационными методами. ==== Метод последовательных приближений ==== function Metod1(f: TFunc; x0: double): double; В данном методе для удобства вычислений переходят от исходного уравнения (1) к уравнению x=f(x). (2) Данный переход можно осуществить множеством способов, например, прибавить к обеим частям (1) //x//.\\ Суть метода последовательных приближений состоит в том, что начальное приближение //x[0]// подставляется в правую часть формулы (2) и вычисляется значение //x[1]//. Затем полученное //x[1]// снова подставляется в правую часть формулы (2) и вычисляется //x[2]//, потом //x[3]// и т.д. Рабочая формула метода последовательных приближений имеет вид x[n]=f(x[n-1]). (3) Вычисления продолжаются до тех пор, пока не будет достигнута заданная точность //Eps//, т.е. | x[n]-x[n-1] | < Eps. (4) Основной проблемой при работе с итерационными методами является **проблема сходимости**. Достаточным условием сходимости метода последовательных приближений является выполнение условия | f'(x[n]) | < 1 (5) для всех значений x[n]. ==== Усовершенствованный метод последовательных приближений ==== function Metod2(f: TFunc; x0: double): double; Формула данного итерационного метода имеет вид x[n+1] = x[n] + A*( f(x[n]) - x[n] ), (6) где A определяется по формулам A = 1/(1 - f'(s)) (7) f'(s) = ( f(x[n]) - x[n] )/( x[n] - x[n-1] ) (8) при этом на первом шаге //x[1]// определяется простым методом последовательных приближений. ==== Метод Ньютона-Рафсона, Бриге-Виетта ==== function Metod3(f: TFunc; x0: double): double; Небольшая дальнейшая модификация усовершенствованного метода последовательных приближений приводит к одному из наиболее известных численных методов решения уравнений - //методу Ньютона-Рафсона//. Формула метода для //f(x)//, подчиняющегося соотношению (2), имеет вид x[n+1] = (f(x[n])-x[n]*f'(x[n]))/(1-f'(x[n])), (9) при этом //сходимость метода обеспечивается//, если: - //x[0]// выбрано достаточно близко к решению //x = f(x)//; - Вторая производная от //f(x)// не становится слишком большой; - Производная //f'(x)// не слишком близка к 1. Формула Ньютона-Рафсона для //F(x)//, подчиняющегося соотношению (1), имеет вид x[n+1] = x[n] - F(x[n])/F'(x[n]), (10) при этом условия сходимости принимают вид: - //x[0]// выбрано достаточно близко к решению //x = F(x)//; - Вторая производная// f''(x)// не становится слишком большой; - Производная //f'(x)// не слишком близка к 0. Применим метод Ньютона-Рафсона согласно формуле (10), при этом вычисление //F(x)// будем осуществлять по //правилу Горнера// == с использованием рекуррентных формул: == b[m] = a[m] b[j] = a[j] + x[n]*b[j+1], j=m-1, ..., 0 (11) Таким образом находим //F(x) = b[0]//. F'(x) представляет собой многочлен степени //m-1//. Воспользовавшись для его вычисления теми же рекуррентными формулами, имеем: c[m] = b[m] c[j] = b[j] + x[n]*c[j+1], j=m-1, ..., 1 (12) и соответственно //F'(x) = c[1]//. Подставляя найденные значения //F(x)// и //F'(x)// в формулу (10) для метода Ньютона-Рафсона, получаем x[n+1] = x[n] - b[0]/c[1], (13) где //b[o]// и //c[1]// вычислены по формулам (11) и (12). Тихо и незаметно мы пришли к //методу Бриге-Виетта// ;) { DonNTU. e-moe aka Labinskiy Nikolay (c) 2005 MailTo: e-moe@ukr.net and visit www.sources.ru } {$N+,G+,X+} const Eps = 0.00001; { Точность вычислений } anMax = 3; { Степень полинома } { Коэффициенты полинома F(x) = a[0]+a[1]*x+a[2]*x^2+ ... +a[n]*x^n } An: array[0..anMax] of double = (-5.372,1.2493,0.559,-0.13); { a[0] a[1] a[2] a[3] } type { Описание функции для передачи ее в кач-ве параметра функциям } TFunc = function(x: double; px: byte; d: shortint): double; var x0,x1,x2: double; { Переменные для вычисления корней } function Func(x: double; px: byte; d: shortint): double; far; (* Функция для вычисления полинома в точке 'x' x - в этой точке будет происходить расчет px - если 1, то результат будет = F(x)+x d - если 0, то F(x), 1 - F'(x), -1 - F(x)/F'(x) *) var bn, { Это будет F(x) } cn: double; { Это будет F'(x) } j: byte; { Счетчик цикла ;) } begin if px=1 then { Если px=1, то добавляем к рез-ту x } An[1]:=An[1]+1; { Далее идет расчет по правилу Горнера } bn:=An[anMax]; cn:=bn; for j:= anMax-1 downto 1 do begin bn:=An[j]+x*bn; cn:=bn+x*cn; end; bn:=An[0]+x*bn; { В зависимости от d возвращаем результат } case d of -1: Func:=bn/cn; 0: Func:=bn; 1: Func:=cn; else Func:=bn; end; { Забираем обратно свой x } if px=1 then An[1]:=An[1]-1; end; function Sign(x: double): shortint; { Функция определения знака } begin if x < - Eps then Sign:=-1 else if x > Eps then Sign:=1 else Sign:=0; end; function Get_X0(f: TFunc): double; (* Поиск начального приближения: Идет начальный просмотр функции и определение двух точек, между которыми функция меняят знак (проходит через 0). Где-то между этими точками лежит корень. *) const LookFrom = -10.0; { Поиск начинается с этой точки } step = 1; { Шаг, с которым производится поиск } begin x0:=LookFrom; x1:=x0+eps; while Sign(f(x1,0,0)) = Sign(f(x0,0,0)) do begin x0:=x1; x1:=x1+step; end; Get_X0:= (x1+x0)/2; end; function Metod1(f: TFunc; x0: double): double; var cnt: word; function TestFunc(x: double): boolean; begin if abs(f(x,1,1)) >= 1 then begin writeln(' Ошибка: Невозможно расчитать результат!'); writeln(' Модуль производной в точке ',x:0:6,' больше 1',#10); TestFunc:= false; end else TestFunc:= true; end; begin Writeln('Метод последовательных приближений:'); if TestFunc(x0) then x1:=f(x0,1,0) else exit; { x2:=f(x1,0)+x1;} cnt:=1; while abs(x1-x0) > eps do begin x0:=x1; if TestFunc(x0) then x1:=f(x0,1,0) else exit; inc(cnt) end; Metod1:=x1; Writeln('x=', x1:0:6); Writeln('Метод сошелся на ',cnt,' шаге.',#10); end; function Metod2(f: TFunc; x0: double): double; var alph: double; cnt: word; begin Writeln('Усовершенствованный метод последовательных приближений:'); x1:=f(x0,1,0); alph:=1/(1-f(x1,0,0)/(x1-x0)); x2:=x1+alph*f(x1,0,0); cnt:=2; while abs(x2-x1) > eps do begin x0:=x1; x1:=x2; alph:=1/(1-f(x1,0,0)/(x1-x0)); x2:=x1+alph*f(x1,0,0); inc(cnt) end; Metod2:=x2; Writeln('x=', x2:0:6); Writeln('Метод сошелся на ',cnt,' шаге.',#10); end; function Metod3(f: TFunc; x0: double): double; var cnt: word; begin Writeln('Метод Бирге-Виетта:'); x1:=x0-f(x0,0,-1); cnt:=1; while abs(x1-x0) > Eps do begin x0:=x1; x1:=x0-f(x0,0,-1); inc(cnt); end; Metod3:=x1; Writeln('x=', x1:0:6); Writeln('Метод сошелся на ',cnt,' шаге.',#10); end; begin x0:=Get_X0(Func); Writeln; Writeln('F(x)= -5.372 + 1.2493*x + 0.559*x^2 - 0.13*x^3'); Writeln('Eps = ',eps:0:5,' x0= ',x0:0:6,#10); Metod1(Func,x0); Metod2(Func,x0); Metod3(Func,x0); Write('Press Enter to exit'); Readln; end. Автор: e-moe aka Labinskiy Nikolay (c) 2005, DonNTU