< Численное решение уравнений итерационными методами. >
(*
Численное решение уравнений итерационными методами.
Данная статья посвящена нахождению корней уравнения
F(x)=0, (1)
где функция F(x) может быть алгебраической либо трансцен-
дентной и должна удовлетворять условию дифференцируемости.
Как правило, чесленное решение уравнений состоит из двух
этапов: нахождение приближенного значения корня и его уточ-
нение до заданной точности. Начальное приближение часто
известно из физических соображений либо находится специаль-
ными методами, например, графически. Подробно будет рассмот-
рен второй этап решения уравнений: нахождение корня с задан-
ной точностью различными итерационными методами.
*)
{$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;
(*
1. Метод последовательных приближений.
В данном методе для удобства вычислений переходят от исход-
ного уравнения (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].
*)
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;
(*
2. Усовершенствованный метод последовательных приближений.
Формула данного итерационного метода имеет вид
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] определяется простым методом
последовательных приближений.
*)
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;
(*
3. Метод Ньютона-Рафсона, Бриге-Виетта.
Небольшая дальнейшая модификация усовершенствованного
метода последовательных приближений приводит к одному из
наиболее известных численных методов решения уравнений -
методу Ньютона-Рафсона. Формула метода для f(x), подчи-
няющегося соотношению (2), имеет вид
x[n+1] = (f(x[n])-x[n]*f'(x[n]))/(1-f'(x[n])), (9)
при этом сходимость метода обеспечивается, если:
1) x[0] выбрано достаточно близко к решению x = f(x);
2) Вторая производная f''(x) не становится слишком большой;
3) Производная f'(x) не слишком близка к 1.
Формула Ньютона-Рафсона для F(x), подчиняющегося соотно-
шению (1), имеет вид
x[n+1] = x[n] - F(x[n])/F'(x[n]), (10)
при этом условия сходимости принимают вид:
1) x[0] выбрано достаточно близко к решению x = F(x);
2) Вторая производная f''(x) не становится слишком большой;
3) Производная 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).
Тихо и незаметно мы пришли к методу Бриге-Виетта ;)
*)
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('DonNTU. e-moe aka Labinskiy Nikolay (c) 2005');
Writeln('MailTo: e-moe@ukr.net and visit www.sources.ru',#10);
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.
З.Ы. только не говорите мне что прога не работает на первом методе.
Все тут работает: пример подобран так, чтобы показать что этот
метод не всегда сходится и об этом нужно помнить ;)
< Численное решение систем линейных алгебраических уравнений >
Данная статья посвящена нахождению корней систем линейных
алгебраических уравнений. Методы численного решения таких
систем подразделяются на два типа: прямые (конечные) и
итерационные (бесконечные). Оба типа полезны и удобны
для практических вычислений и каждый из них имеет свои
преимущества и недостатки.
1. Метод исключений (метод Гаусса).
Данный метод является наиболее известным и широко приме-
няемым.
Рассмотрим систему из n уравнений с n неизвестными. Обозна-
чим неизвестные через x[1], x[2], ... , x[n] и запишем систему
в следующем виде:
a[1,1]*x[1] + a[1,2]*x[2] + ... + a[1,n]*x[n] = b[1]
a[2,1]*x[1] + a[2,2]*x[2] + ... + a[2,n]*x[n] = b[2]
....................................................
a[n,1]*x[1] + a[n,2]*x[2] + ... + a[n,n]*x[n] = b[n]
Предполагается, что в силу расположения уравнений, a[i,i] <> 0.
Если это не так, то, меняя уравнения местами, добиваемся выполне-
ния этого условия. Введем n-1 множителей:
m[i] = a[i,1]/a[1,1] , где i = 2, 3, ... , n
и вычтем из каждого i-го уравнения первое, умноженное на m,
обозначая
a'[i,j] = a[i,j] - m[i]*a[1,j]
b'[i] = - m[i]*b[i]
i = 2, 3, ... , n; j = 1, 2, ... , n.
Для всех уравнений, начиная со второго, получим:
a[i,1] = 0 , где i = 2, 3, ... , n.
Преобразованная система уравнений запишется в следующем виде:
a[1,1]*x[1] + a[1,2]*x[2] + ... + a[1,n]*x[n] = b[1]
0 + a'[2,2]*x[2] + ... + a'[2,n]*x[n] = b'[2]
....................................................
0 + a'[n,2]*x[2] + ... + a'[n,n]*x[n] = b'[n]
Продолжая таким же образом, исключаем x[2] из последних n-2
уравнений, затем x[3] из последних n-3 и т.д. На некотором
k-м этапе мы исключаем x[k] с помощью множителей:
m^(k-1)_[i] = a^(k-1)_[i,k]/a^(k-1)_[k,k] , i = k+1, ... , n,
незабывая, что a(k-1)[k,k] <> 0.
Примечание, ^(k-1) обозначает на k-1й итерации
_[i,k] - индекс эл-та.
Тогда
a^(k)_[i,j] = a^(k-1)_[i,j] - m^(k-1)_[i]*a^(k-1)_[k,j]
b^(k)_[i] = - m^(k-1)_[i]*b^(k-1)_[k]
i = k+1, k+2, ... , n; j = k, k+1, ... , n.
Окончательно, треугольная система уравнений записывается:
a[1,1]*x[1] + a[1,2]*x[2] + ... + a[1,n]*x[n] = b[1]
a[2,2]*x[2] + ... + a[2,n]*x[n] = b[2]
....................................................
a[n,n]*x[n] = b[n]
Обратная подстановка для нахождения значений неизвестных задается
формулами:
x[n] = b^(n-1)_[n] / a^(n-1)_[n,n]
x[n-1] = (b^(n-2)_[n-1] - a^(n-2)_[n-1,n]*x[n]) / a^(n-2)_[n-1,n-1]
...................................................................
x[j] = ( b^(j-1)_[j] - a^(j-1)_[j,n]*x[n] - ... -
- a^(j-1)_[j,j+1]*x[j+1] ) / a^(j-1)_[j,j]
j = n-2, n-3, ... , 1.
Перед началом процесса исключения очередного неизвестного может
понадобиться переставить уравнения в системе, чтобы избежать деления
на нуль. Кроме этого, при перестановке уравнений необходимо добиваться
того, чтобы
| a^(k-1)_[k,k] | >= | a^(k-1)_[i,k] |
Поэтому перед началом процесса исключения очередного неизвестного
необходимо переставить уравнения так, чтобы максимальный по модулю
коэффициент при x[k] попал на главную диагональ (данный способ ре-
шения часто называется методом главного элемента).
2. Итерационный метод Гаусса-Зейделя.
Данный метод отличается простотой и легкостью программирования,
малой ошибкой округления, но сходится при выполнении некоторых условий.
Рассмотрим все ту же систему из n уравнений с n неизвестными.
По прежнему полагаем, что a[i,i] <>0 для всех i. Тогда k-е приближение
к решению будет задаваться формулой:
x^(k)_[i] = ( b[i] - a[i,1]*x^(k)_[1] - ... - a[i,j-1]*x^(k)_[i-1] -
- a[i,j+1]*x^(k-1)_[i+1] - a[i,n]*x^(k-1)_[n] ) / a[i,i]
i = 1, 2, ... , n.
Как известно, любой итерационный процесс, как и настоящий мужик,
должен имееть свой конец :)
Поэтому вычисления нужно продолжать до тех пор, все x^(k)_[i] не
станут достаточно близки к x(k-1)[i], т.е. пока для всех i не
будет выполнятся неравенство
max| x^(k)_[i] - x^(k-1)_[i] | < Eps,
где Eps некоторое положительное число, задающее точность вычислений.
Итерационный метод Гаусса-Зейделя сходится, если удовлетворяется
достаточное условие сходимости:
для всех i модуль диагонального коэффициента должен быть не меньше
суммы модулей остальных коэффициентов данной строки
| a[i,i] | >= | a[i,1] | + | a[i,2] | + ... + | a[i,j-1] | +
+ | a[i,j+1] | + ... + | a[i,n] | ,
а хотябы для обной строки это неравенство должно выполнятся строго
| a[i,i] | > | a[i,1] | + | a[i,2] | + ... + | a[i,j-1] | +
+ | a[i,j+1] | + ... + | a[i,n] |.
3. Сравнение методов.
Метод исключений имеет то преимущество, что он конечен и теоретически,
с его помощью можно решить любую невырожденную систему. Итерационный метод
сходится только для специальных систем уравнений, однако когда итерационные
методы сходятся, они, обычно, предпочтительнее:
* Время вычислений пропорционально n2 на итерацию, в то время как для
метода исключений время пропорционально n3. Если для решения системы
требуется меньше чем n итераций, то общие затраты машинного времени будут
меньше.
* Как правило, ощибки округления в итерационном методе меньше. Часто, это
соображение может оказаться достаточно важным, что оправдывает дополнительные
затраты машинного времени. Многие систему, возникающие на практике, имеют
среди коэффициентов большое кол-во нулей. В этом случае итерационные методы,
если они сходятся, в высшей степени предпочтительны т.к. в методе исключений
получается треугольная система, которая в качестве коэффициентов нулей
обычно не содержит.
А вот и исходник по теме:
{
Writeln('Labinskiy Nikolay aka e-moe (c) 2005');
Writeln('See you later on forum.sources.ru ;)');
}
{$N+,G+,X+}
const
Comments: boolean = false; { нужно ли печатать пошаговые рез-ты расчета }
Eps = 0.00001; { Точность вычислений }
n_max = 4; { Кол-во ур-й и неизвестных }
const_AnB: array[1..n_max,1..n_max+1] of double =
(( -8, 2, 17, -5, -119.97),
{ -8*X[1] + 2*X[2] + 17*X[3] - 5*X[4] = -119.97 и т.д. ... }
( 4, -22, 6, 5, 55.73),
( 15, 3, -5, -5, 18.77),
( -4, -4, 5, 14, -79.42));
{ Описание типов матрицы уравнеиний и массива найденных Х }
Type TMatr = array[1..n_max,1..n_max+1] of double;
Type TXMarr = array[1..n_max] of double;
procedure PrintX(const X: TXMarr; n: byte; cnt: word);
{ Печатает найденные Х }
var
k, { Позиция заданного корня в массиве }
i: byte; { Номер текущего корня для вывода }
begin
if n = 0 then
exit;
k:=low(X);
if cnt = 0 then
begin
write(' N x1');
for i:=2 to n do
write(' x',i);
writeln;
end;
write(cnt:2);
for i:=1 to n do
begin
Write(' ',X[k]:3:5);
inc(k);
end;
writeln;
{
При решении систем, с большим кол-вом неизвестных, возможно,
прийдется переделать эту процедуру для корректного вывода
всех решений.
}
end; { PrintX }
procedure PrintMatr(var AnB: TMatr; n: byte);
{ Печатает заданную матрицу }
var
i,j: byte;
begin
for i:=1 to n do
begin
for j:=1 to n+1 do
write(AnB[i,j]:3:5,' ');
writeln;
end;
writeln;
{
При решении систем, с большим кол-вом неизвестных, возможно,
прийдется переделать эту процедуру для корректного вывода
всех решений.
}
end; { PrintMatr }
function Normalize(var AnB: TMatr; n: byte): byte;
{
Располагает на главной диагонали наибольшие элементы в столбцах
Если один из них равен нулю,то систему решить не получится ...
Возвращает 0 если систему заданными методами решить не получится,
1 - с-му можно решить только методом Гаусса
2 - с-му можно решать любым методом
}
var
max: double; { Переменная для поиска макс. эл-та }
imax: byte; { Переменная для поиска макс. эл-та }
tmp: double; { Переменная для обмена элементов }
i,j: byte; { Счетчики циклов }
begin
Normalize:=2;
for j:=1 to n do
begin
max:=Abs(AnB[1,j]);
imax:=1;
for i:=2 to n do
if Abs(AnB[i,j]) > max then
begin
max:=Abs(AnB[i,j]);
imax:=i;
end;
if max < Eps then
begin
Normalize:=0;
Writeln('Error: a[i,i]=0!');
exit;
end
else
if j <> imax then
for i:=1 to n+1 do
begin
tmp:=AnB[j,i];
AnB[j,i]:=AnB[imax,i];
AnB[imax,i]:=tmp;
end;
end;
for i:=1 to n do
begin
tmp:=0;
for j:=1 to n do
if i <> j then
tmp:=tmp+Abs(AnB[i,j]);
if Abs(AnB[i,i]) < tmp then
Normalize:=1;
end;
end; { Normalize }
procedure Metod1(const Matr; n: byte);
var
k : byte;
M_ : TMatr absolute Matr;
AnB : TMatr;
X,M : TXMarr;
function Calc(k: byte): boolean;
var
i,j : byte;
begin
Calc:= true;
for i:=k+1 to n do
begin
M[i]:= AnB[i,k]/AnB[k,k];
if M[i] > 1 then
begin
Calc:= false;
exit;
end;
end;
for i:=k+1 to n do
for j:=k to n+1 do
AnB[i,j]:=AnB[i,j]-M[i]*AnB[k,j];
if Comments then
PrintMatr(AnB,n);
end; { Calc }
function Summ(j: byte): double;
var
i : byte;
res : double;
begin
res:=AnB[j,n+1];
for i:=n downto j+1 do
res:=res - AnB[j,i]*X[i];
Summ:=res;
end; { Summ }
begin
Writeln('Метод Гаусса:');
AnB:=M_;
if Normalize(AnB,n) = 0 then
begin
writeln('Error: Невозможно решить систему уравнений!');
exit;
end;
for k:=1 to n-1 do
if not Calc(k) then
begin
Writeln('Error: Невыполняется условие | a^(k-1)_[k,k] | >= | a^(k-1)_[i,k] | !');
exit;
end;
X[n]:=AnB[n,n+1]/AnB[n,n];
for k:=n-1 downto 1 do
X[k]:=Summ(k)/AnB[k,k];
PrintX(X,n,0);
Writeln;
end; { Metod1 }
procedure Metod2(const Matr; n: byte);
var
M_ : TMatr absolute Matr;
AnB : TMatr;
X : TXMarr;
i,j: byte;
tmp: double;
delta: double;
cnt: word;
function Summ(i: byte): double;
var
j: byte;
res: double;
begin
res:=AnB[i,n+1];
for j:=1 to n do
if i <> j then
res:=res-AnB[i,j]*X[j];
Summ:=res;
end; { Summ }
begin
writeln('Метод Гаусса-Зейделя:');
AnB:=M_;
For i:=1 to n do
X[i]:=0;
if Normalize(AnB,n) <> 2 then
begin
writeln('Error: !');
exit;
end;
delta:=1;
cnt:=0;
while delta > Eps do
begin
if Comments then
PrintX(X,n,cnt);
delta:=0;
for i:=1 to n do
begin
tmp:=Summ(i)/AnB[i,i];
if delta < Abs(tmp-X[i]) then
delta:=Abs(tmp-X[i]);
X[i]:=tmp;
end;
inc(cnt);
end;
PrintX(X,n,cnt);
writeln('Заданная точность вычислений достигнута на ',cnt,' шаге.');
Writeln;
end; { Metod2 }
begin
Writeln(#10,'Численное решение систем линейных алгебраических уравнений:',#10);
Metod1(const_AnB,n_max);
Writeln('Press Enter to continue');
readln;
Metod2(const_AnB,n_max);
Writeln('Press Enter if you wanna DOS ;)');
readln;
end.
< Методы решения дифференциальных уравнений. >
Данная статья посвящена численным методам решения дифференциальных уравнений.
Мы будем рассматривать методы решения одного обыкновенного дифференциального
уравнения первого порядка с одним начальным условием:
y' = f(x,y) (1)
y(x[0]) = y[0] (2)
Методы, которые здесь рассматриваются, легко обобщаются для системы уравнений
первого порядка. А уравнения высших порядков можно свести к системе уравнений
первого порядка.
В основном существуют два широких класса методов:
* Одноступенчатые методы, в которых используется только информация о самой
кривой в одной точке и не производятся итерации. Один из методов - решение с
помощью рядов Тейлора, но на практике он не слишком удобен для использования.
Практически удобные методы этого класса - методы Рунге-Кутта. Эти методы
прямые (без итераций), но требуют многократных повторных вычислений функции.
При использовании данных методов трудно оценивать допускаемую ошибку.
* Многоступенчатые методы, в которых следующую точку кривой можно найти, не
производя так много повторных вычислений, но для достижения достаточной
точности требуются итерации. Большинство методов этого класса называются
методами прогноза и коррекции (метод Адамса-Бошфора). некоторые трудности,
связанные с использованием итерационной процедуры и с использованием
нескольких начальных точек уравновешиваются тем фактом, что оценку ошибки
при использовании этого метода легко получить в качестве побочного продукта
вычислений.
Как и во многих других случаях, эти два класса методов придется сочетать
разумным образом, учитывая их достоинства и недостатки.
1. Методы Рунге-Кутта.
Методы Рунге-Кутта обладают следующими отличительными свойствами:
* Эти методы одноступенчатые: чтобы найти y[m+1], нужна информация только о
предыдущей точке x[m],y[m].
* Они согласуются с рядом Тейлора вплоть до членов порядка h^p,
где p - различна для разных методов и называется порядком метода.
* Они не требуют вычисления производных от f(x,y), а требуют только вычисления
самой функции.
Именно благодаря 3) эти методы удобны для практических вычислений, однако для
вычисления одной последующей точки решения нам придется вычислять f(x,y)
несколько раз при различных x и y.
1.1. Метод Эйлера.
Этот метод, один из самых старых и широко известных, описывается формулой:
y[m+1] = y[m] + h*f(x[m],y[m]). (3)
Найденное по формуле (3) решение согласуется с разложением в ряд Тейлора
вплоть до членов порядка h, т.е. данный метод является методом Рунге-Кутта
первого порядка.
Этот метод имеет довольно большую ошибку приближения, кроме того, он очень
часто оказывается неустойчивым - изначально малая ошибка (происходящая от
приближения, округления или исходных данных) увеличивается с ростом x.
Для вычисления y[m+1] метод Эйлера использует наклон касательной только в
точке x[m],y[m]. Этот метод можно усовершенствовать множеством различных
способов. Рассмотрим два из них.
1.2. Исправленный метод Эйлера.
В исправленном методе Эйлера мы находим средний tg угла наклона касательной
для двух точек: [I]x[m],y[m] и x[m+1],y[m]+h*y'[m][/I]. Соотношения, описывающие
данный метод, имеют вид:
y[m+1] = y[m] + h*F(x[m],y[m],h) (4)
F(x[m],y[m],h) = ( y'[m] + f(x[m]+h, y[m]+h*y'[m]) )/2 (5)
y'[m] = f(x[m],y[m]) (6)
Исправленный метод Эйлера согласуется с разложением в ряд Тейлора вплоть до
членов степени h^2, являясь, т.о. методом Рунге-Кутта второго порядка.
1.3. Модифицированный метод Эйлера.
В данном методе мы находим tg угла наклона касательной в точке:
x = x[m] + h/2; y = y[m] + (h/2)*y'[m]
Соотношения, описывающие модифицированный метод Эйлера имеют вид:
y[m+1] = y[m] + h*F(x[m],y[m],h) (7)
F(x[m],y[m],h) = f( x[m]+h/2, y[m]+(h/2)*y'[m] ) (8)
y'[m] = f(x[m],y[m]) (9)
Модифицированный метод Эйлера также согласуется с разложением в ряд Тейлора
вплоть до членов степени h^2, и также является методом Рунге-Кутта второго
порядка.
1.4. Метод Рунге-Кутта четвертого порядка.
Данный метод является одним из самых употребительных методов интегрирования
дифференциальных уравнений. Этот метод применяется настолько широко, что в
литературе его просто называют "методом Рунге-Кутта" без всяких указаний на его
тип или порядок. Этот классический метод описывается системой следующих пяти
соотношений:
y[m+1] = y[m] + h*(k[1] + 2*k[2] + 2*k[3] + k[4])/6 (10)
k[1] = f(x[m], y[m]) (11)
k[2] = f(x[m]+h/2, y[m]+h*k[1]/2) (12)
k[3] = f(x[m]+h/2, y[m]+h*k[2]/2) (13)
k[4] = f(x[m]+h, y[m]+h*k[3]) (14)
Ошибка приближения для этого метода равна e[t]=k*h^5. Заметим, что при
использовании этого метода функцию необходимо вычислять четыре раза.
Один из серьезных недостатков методов Рунге-Кутта состоит в отсутствии
простых способов оценки их ошибки.
2. Методы прогноза и коррекции.
Отличительной чертой методов Рунге-Кутта является то, что при вычислении
следующей точки (x[m+1],y[m+1]) используется информация только об одной
предыдущей точке (x[m],y[m]), но не нескольких. Кроме того, для методов
Рунге-Кутта отсутствует достаточно простые способы оценки ошибки, что приводит
к необходимости рассмотрения некоторых дополнительных методов решения ДУ.
Отличительное свойство этих методов состоит в том, что с их помощью нельзя
начать решение уравнения т.к. в них необходимо использовать информацию о
предыдущих точках решения. Чтобы начать решение, имея только одну точку,
определяемую начальными условиями, или для того, чтобы изменить шаг (h),
необходим метод типа Рунге-Кутта. Поэтому приходится использовать разумное
сочетание этих двух методов.
Методы, которые мы рассмотрим, известны под общим названием методов прогно-
за и корректировки. Как ясно из названия вначале "предсказывается" значение
y[m+1], а затем используется тот или иной метод его "корректировки".
Среди множества возможных формул прогноза и коррекции выберем по одному
примеру, применимому ко многим практическим задачам.
2.1. Метод Адамса-Бошфора.
Для прогноза используем формулу второго порядка
y^(0)_[m+1] = y[m-1] + 2*h*f(x[m],y[m]) (15)
где (0) - означает исходное приближение y[m+1], т.е. предсказанное значение.
Непосредственно из написанной формулы следует, что с ее помощью нельзя вычис-
лить y[1], т.к. для его вычисления потребовалась бы точка, расположенная перед
начальной точкой y[0]. Чтобы начать решение с помошью метода прогноза и коррек-
ции, для нахождения y[1] необходимо использовать метод типа Рунге-Кутта.
Для коррекции возьмем формулу, похожую на исправленный метод Эйлера (4)-(6):
y^(i)_[m+1] = y[m] + h(f(x[m],y[m])+f(x[m+1],y^(i-1)_[m+1]))/2 (16)
для i=1,2,3, ...
Итерационный процесс прекращается, когда
| y^(i+1)_[m+1] - y^(i)_[m+1] | < Eps (17)
для некоторого Eps>0.
{
Writeln('Labinskiy Nikolay aka e-moe (c) 2005');
Writeln('See you later on forum.sources.ru ;)');
}
{$E+,N+,X+}
const
eps = 0.00001;
x0 = 3;
y0 = 3;
type
TFunc = function(x,y: extended): extended;
function func(x,y: extended): extended; far;
begin
func := y*cos(x) - 2*sin(2*x);
end;
function Euler(f: TFunc; x0_,x_end,y0_: extended; n: word): extended;
{ где x0_ и y0_ - начальное условие
x_end - точка, в которой необходимо вычислить результат
n - количество шагов для вычисления результата }
var
i : word; { счетчик цикла }
x,h : extended; { текущая точка и длина шага }
res : extended; { переменная для накопления конечного результата функции}
begin
h:= (x_end - x0_)/n; { Находим длину шага }
res:= y0_; { устанавливаем начальные значения}
x:=x0_;
for i:=1 to n do
begin { вычисляем результат по методу Эйлера }
res:=res+h*f(x,res);
x:=x+h; { переходим к следующей точке }
end;
Euler:=res; { присваиваем конечный результат функции }
end;
function Euler2(f: TFunc; x0_,x_end,y0_: extended; n: word): extended;
{ где x0_ и y0_ - начальное условие
x_end - точка, в которой необходимо вычислить результат
n - количество шагов для вычисления результата }
var
i : word; { счетчик цикла }
x,h : extended; { текущая точка и длина шага }
res : extended; { переменная для накопления конечного результата функции}
begin
h:= (x_end - x0_)/n; { Находим длину шага }
res:= y0_; { устанавливаем начальные значения}
x:=x0_;
for i:=1 to n do
begin { вычисляем результат по исправленному методу Эйлера }
res:=res+h*(f(x,res)+f(x+h,res+h*f(x,res)))/2;
x:=x+h; { переходим к следующей точке }
end;
Euler2:=res; { присваиваем конечный результат функции }
end;
function Euler3(f: TFunc; x0_,x_end,y0_: extended; n: word): extended;
{ где x0_ и y0_ - начальное условие
x_end - точка, в которой необходимо вычислить результат
n - количество шагов для вычисления результата }
var
i : word; { счетчик цикла }
x,h : extended; { текущая точка и длина шага }
res : extended; { переменная для накопления конечного результата функции}
begin
h:= (x_end - x0_)/n; { Находим длину шага }
res:= y0_; { устанавливаем начальные значения}
x:=x0_;
for i:=1 to n do
begin { вычисляем результат по модифицированному методу Эйлера }
res:=res+h*f(x+h/2,res+(h/2)*f(x,res));
x:=x+h; { переходим к следующей точке }
end;
Euler3:=res; { присваиваем конечный результат функции }
end;
function RungeKutt(f: TFunc; x0_,x_end,y0_: extended; n: word): extended;
{ где x0_ и y0_ - начальное условие
x_end - точка, в которой необходимо вычислить результат
n - количество шагов для вычисления результата }
var
i : word; { счетчик цикла }
x,h : extended; { текущая точка и длина шага }
res : extended; { переменная для накопления конечного результата функции }
k1,k2,k3,k4: extended; { вспомогательные переменные вычисления результата }
begin
h:= (x_end - x0_)/n; { Находим длину шага }
res:= y0_; { устанавливаем начальные значения}
x:=x0_;
for i:=1 to n do
begin { вычисляем результат по методу Рунге-Кутта 4го порядка }
k1:=f(x,res);
k2:=f(x+h/2,res+h*k1/2);
k3:=f(x+h/2,res+h*k2/2);
k4:=f(x+h,res+h*k3);
res:=res+h*(k1+2*k2+2*k3+k4)/6;
x:=x+h; { переходим к следующей точке }
end;
RungeKutt:=res; { присваиваем конечный результат функции }
end;
function AdmsBoshf(f: TFunc; x0_,x_end,y0_: extended; n: word): extended;
{ где x0_ и y0_ - начальное условие
x_end - точка, в которой необходимо вычислить результат
n - количество шагов для вычисления результата }
var
i : word; { счетчик цикла }
x,h : extended; { текущая точка и длина шага }
y1,y2 : extended; { переменные для вычисления следующей точки }
tmp : extended; { временная переменная для уточнения результата }
begin
h:= (x_end - x0_)/n; { Находим длину шага }
x:=x0_; { устанавливаем текущую точку }
y1:=y0_+h*f(x+h/2,y0_+(h/2)*f(x,y0_)); { находим начальное значение
модифицированным методом Эйлера }
repeat { уточняем(корректируем) начальное значение }
tmp:=y1;
y1:=y0_+h*(f(x,y0_)+f(x+h,y1))/2;
until abs(y1-tmp) < eps;
x:=x+h; { переходим к следующей точке }
y2:=y0_+2*h*f(x,y1); { прогнозируенм следующее значение }
repeat { уточнаяем наш прогноз }
tmp:=y2;
y2:=y1+h*(f(x,y1)+f(x+h,y2))/2;
until abs(y2-tmp) < eps;
for i:=3 to n do { начинаем счет с 3 т.к. две точки уже найдены }
begin
x0_:=x; { переходим к следующей точке }
x:=x+h;
y0_:=y1;
y1:=y2;
y2:=y0_+2*h*f(x,y1); { прогнозируенм следующее значение }
repeat { уточнаяем наш прогноз }
tmp:=y2;
y2:=y1+h*(f(x,y1)+f(x+h,y2))/2;
until abs(y2-tmp) < eps;
end;
AdmsBoshf:=y2; { присваиваем конечный результат функции }
end;
begin
writeln('Численное решение дифференциальных уравнений:');
writeln(#10,' y'' = y*cos(x) - 2*sin(2*x); y(0)=3; x_end=',(5*x0+3.5):5:5);
writeln(#10,'Метод Эйлера:');
writeln('n=5 000, result: ',Euler(func,x0,5*x0+3.5,y0,5000):5:5);
writeln('n=10 000, result: ',Euler(func,x0,5*x0+3.5,y0,10000):5:5);
writeln('n=25 000, result: ',Euler(func,x0,5*x0+3.5,y0,25000):5:5);
writeln(#10,'Исправленный метод Эйлера:');
writeln('n=50, result: ',Euler2(func,x0,5*x0+3.5,y0,50):5:5);
writeln('n=100, result: ',Euler2(func,x0,5*x0+3.5,y0,100):5:5);
writeln('n=250, result: ',Euler2(func,x0,5*x0+3.5,y0,250):5:5);
writeln(#10,'Модифицированный метод Эйлера:');
writeln('n=50, result: ',Euler3(func,x0,5*x0+3.5,y0,50):5:5);
writeln('n=100, result: ',Euler3(func,x0,5*x0+3.5,y0,100):5:5);
writeln('n=250, result: ',Euler3(func,x0,5*x0+3.5,y0,250):5:5);
writeln(#10,'Метод Рунге-Кутта:');
writeln('n=50, result: ',RungeKutt(func,x0,5*x0+3.5,y0,50):5:5);
writeln('n=100, result: ',RungeKutt(func,x0,5*x0+3.5,y0,100):5:5);
writeln('n=250, result: ',RungeKutt(func,x0,5*x0+3.5,y0,250):5:5);
write(#10,'Press Enter to continue.');
readln;
writeln(#10,'Метод Адамса-Бошфора:');
writeln('n=50, result: ',AdmsBoshf(func,x0,5*x0+3.5,y0,50):5:5);
writeln('n=100, result: ',AdmsBoshf(func,x0,5*x0+3.5,y0,100):5:5);
writeln('n=250, result: ',AdmsBoshf(func,x0,5*x0+3.5,y0,250):5:5);
end.
