Показать сообщение отдельно
Старый 09.04.2014, 14:36   #2 (permalink)
Vladimir_S
Специалист
 
Регистрация: 27.08.2008
Адрес: Санкт-Петербург
Сообщений: 27,807
Сказал(а) спасибо: 340
Поблагодарили 583 раз(а) в 208 сообщениях
Репутация: 113184
По умолчанию

(продолжение)

3. Метод касательных (Ньютона)

Начинаем с того, что выбираем стартовую точку x0. Этой точкой может быть один из концов интервала (a или b), а может быть просто точка вблизи предполагаемого корня (чем ближе, тем лучше).
(Лирическое отступление. Некоторые идиоты-"преподаватели" требуют от студентов, чтобы этой точкой непременно был один из концов интервала, причем не любой, а именно тот, где знаки функции и ее производной совпадают. Заявляю ответственно: подобные "украшательства" есть полная чушь, свидетельствующая лишь о некомпетентности этих самых...).
Далее, через точку (x0, F(x0)) проводим касательную к кривой y=F(x) до пересечения этой касательной оси абсцисс в точке х1. Таким образом, имеем прямоугольный треугольник с длинами катетов вертикального – F(x0), и горизонтального – расстояние между х0 и х1. Гипотенузой является отрезок касательной.
Если угол между касательной и осью абсцисс, отсчитанный от положительного направления последней, есть α, то из геометрии следует, что (x0-x1)=F(x0)/tg(α). Но тангенс угла наклона касательной есть ни что иное, как производная в точке х0, а потому, окончательно
x1 = x0 – F(x0)/F'(x0)
Далее берем функцию и производную в точке х1 и по той же формуле получаем х2, потом х3 и т.д. Точки х0, х1, х2, х3, ... и будут последовательными приближениями искомого корня.
К сожалению, метод не всегда устойчив. Например, если в стартовой точке производная близка к нулю, то точка х1 рискует оказаться далеко за пределами интервала и попасть в область, где функция либо не определена вовсе, либо успела пройти один или даже несколько экстремумов, и в таком случае процедура будет не приближать, а отдалять получаемые значения х1, х2 и т.д. от корня. Тем важнее становится этап предварительного исследования, о котором писалось выше.
Код:
Var
 Eps,Res:Real;
 i:Integer;

Function F(z:real):Real;
begin
 F:=Sqr(Sin(z)+Cos(z))/Exp(Ln(33.5)*2/3)+Sqrt(3/7)-z;
end;

Function G(z:real):Real;
begin
 G:=(Sin(z)+Cos(z))*2*(Cos(z)-Sin(z))/Exp(Ln(33.5)*2/3)-1;
end;

Procedure EQRoot(e:real; var R:real);
var
 x1,x2,d:real;
begin
 x1:=0.0;
 Repeat
  x2:=x1-F(x1)/G(x1);
  d:=abs(x1-x2);
  x1:=x2;
 Until d<e;
 R:=x2;
end;

Begin
 Eps:=0.1;
 for i:=1 to 10 do
  begin
   EQRoot(Eps,Res);
   Writeln(Res:12:10);
   Eps:=Eps/10;
  end;
 Readln
End.
Результат (обратите внимание, насколько быстро в методе Ньютона итерации сходятся к конечному значению):
Код:
0.8476303072
0.8463956635
0.8463953790
0.8463953790
0.8463953790
0.8463953790
0.8463953790
0.8463953790
0.8463953790
0.8463953790
-------------
0.8463953790 - accurate value
4. Метод простой итерации

Тут хочу отметить сразу: метод, в общем случае, неустойчив, и выдаст ли программа желаемый результат или пойдет "вразнос" – это уж как повезет. Есть некие критерии сходимости, но они сложны и, в основном, избыточны, а потому рассматривать мы их не будем, а остановимся на самом алгоритме.
Первое, что нужно сделать, это преобразовать исходное уравнение F(x)=0 к виду х=Ф(х). Для уравнения, взятого в качестве примера, такое преобразование очевидно (функция Ф(х) там представлена). Дальше, нужно постараться найти число х0, близкое к искомому корню, и подставить его в ПРАВУЮ часть уравнения х=Ф(х). Тогда то, что получится слева, будет следующим приближением х1, каковое мы снова подставляем в правую часть и получаем х2, ну и т.д.
Код:
Var
 Eps,Res:Real;
 i:Integer;

Function F(z:real):Real;
begin
 F:=Sqr(Sin(z)+Cos(z))/Exp(Ln(33.5)*2/3)+Sqrt(3/7);
end;

Procedure EQRoot(e:real; var R:real);
var
 x1,x2,d:real;
begin
 x1:=0.0;
 Repeat
  x2:=F(x1);
  d:=abs(x1-x2);
  x1:=x2;
 Until d<e;
 R:=x2;
end;

Begin
 Eps:=0.1;
 for i:=1 to 10 do
  begin
   EQRoot(Eps,Res);
   Writeln(Res:12:10);
   Eps:=Eps/10;
  end;
 Readln
End.
Результат:
Код:
0.8468813641
0.8463839519
0.8463839519
0.8463956466
0.8463953728
0.8463953728
0.8463953792
0.8463953792
0.8463953790
0.8463953790
-------------
0.8463953790 - accurate value
5. Метод "десятичного деления"

Вообще-то официально такого не существует, более того, это метод выдумал я сам для своих целей. Вряд ли, конечно, это "открытие века", скорее всего он имеется в природе под каким-то другим названием, просто я не нашел. Мне он очень нравится, поэтому расскажу о нем здесь: вдруг кого-то заинтересует.
Возьмем в качестве стартовой точки нижнюю границу интервала a (верхняя b нам не понадобится) и еще зададим шаг h. Вот тут важно: этот шаг непременно должен быть степенью десяти (положительной, отрицательной либо нулевой), т.е. 10, 1000, 0.1, 1, 0.000001 и т.п. – в зависимости от задачи. Теперь, стартовав с x=a, начинаем увеличивать x с шагом h до тех пор, пока знак функции F(x) не изменится. Как только это произойдет, "отступим" на один шаг, уменьшим h ровно в 10 раз и повторим процедуру. Потом еще раз. И так будем повторять, пока не добьемся требуемой точности. В чем "изюминка" данного подхода? А в том, что каждое прохождение добавляет значащую цифру следующего разряда. То есть сколько нам надо их иметь, столько раз и следует прокручивать цикл. По-моему, очень даже удобно.
Метод абсолютно устойчив.

Код:
Var
 Eps,Res:Real;
 i:Integer;

Function F(z:real):Real;
begin
 F:=Sqr(Sin(z)+Cos(z))/Exp(Ln(33.5)*2/3)+Sqrt(3/7)-z;
end;

Procedure EQRoot(e:real; var R:real);
var
 x,a,h:real;
begin
 x:=0.0;
 a:=0.0;
 h:=1.0;
 Repeat
  repeat
   x:=x+h;
  until F(a)*F(x)<0;
  x:=x-h;
  h:=h/10;
 Until h<e;
 R:=x;
end;

Begin
 Eps:=0.1;
 for i:=1 to 10 do
  begin
   EQRoot(Eps,Res);
   Writeln(Res:12:10);
   Eps:=Eps/10;
  end;
 Readln
End.
Результат:
Код:
0.8000000000
0.8400000000
0.8460000000
0.8463000000
0.8463900000
0.8463950000
0.8463953000
0.8463953700
0.8463953790
0.8463953790
-------------
0.8463953790 - accurate value
Vladimir_S вне форума   Ответить с цитированием
Ads

Яндекс

Member
 
Регистрация: 31.10.2006
Сообщений: 40200
Записей в дневнике: 0
Сказал(а) спасибо: 0
Поблагодарили 0 раз(а) в 0 сообщениях
Репутация: 55070