Добавил:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

1-2 Моделирование / Matlab. Практический подход. Самоучитель

.pdf
Скачиваний:
762
Добавлен:
31.01.2021
Размер:
12.72 Mб
Скачать

Глава 5. Решение уравнений и оптимизация

ются эти самые вычисления с выполнения команды x=(b+a)/2, которой переменной x в качестве значения присваивается среднее значение для границ интервала. Это соответствует точке в центре интервала поиска решения. Далее реализуется такой алгоритм.

Проверяется длина интервала, на котором ищется корень. Если длина интервала не превышает удвоенное значение погрешности, процесс вычисления корня можно заканчивать. Для этого в условном операторе проверяется условие abs(b-a)<2*eps и в случае его истинности командой return завершается выполнение кода функции.

На заметку

Очевидно, что любая точка в пределах интервала поиска решения отстоит от центральной точки интервала не больше, чем на полдлины интервала. Поэтому если длина интервала поиска не превышает удвоенной точности вычисления корня, то поскольку корень находится в пределах интервала поиска, центральная точка отклоняется от центра интервала не больше, чем на погрешность. Другими словами, необходимая точность в вычислении корня выдержана.

Впротивном случае необходимо сместить одну из границ интервала поиска решения. Для этого в очередном условном операторе проверяется условие f(a)*f(x)>0. Выполнение этого условия означает, что в центре интервала и на соответствующей границе значения функции имеют одинаковые знаки. Поэтому данная граница смещается в центр, что и реализуется командой a=x. Если условие f(a)*f(x)>0 не выполнено, в центр нужно смещать другую границу (команда b=x).

Влюбом случае длина интервала поиска решения уменьшается вдвое, и задача, фактически, сводится к исходной: нужно найти корень на интервале, только интервал теперь в два раза меньше. Поэтому смело используем команду x=EqSolve(f,[a b],eps). Именно с этой командой связан рекурсивный вызов функции.

Вкомандном окне вводим и выполняем следующие инструкции (выделены жирным шрифтом):

>>f=@(x)((x-1)*(x-5)*(x-9));

>>x=EqSolve(f,[0 4],0.0001)

x =

0.9999

>>x=EqSolve(f,[2 4],0.0001)

??? Error using ==> EqSolve at 5

Неверно указаны границы интервала поиска корня!

>>x=EqSolve(f,[2 5.1],0.0001)

x =

5.0000

211

Самоучитель Matlab

>> x=EqSolve(f,[-100 15],0.0001) x =

9.0000

Командой f=@(x)((x-1)*(x-5)*(x-9)) мы создаем функцию-полином f (x) = (x −1)(x − 5)(x − 9) и решать будем, соответственно, уравнение f (x) = 0 . У уравнения три корня: x = 1 , x = 5 и x = 9 . Эти корни будем искать. Первый корень x = 1 с точностью 0.0001 находим командой x=EqSolve(f,[0 4],0.0001). Команда x=EqSolve(f,[2 4],0.0001)

дает пример того, как неправильно указывать интервал для поиска решения: на интервале от 2 до 4 корней нет.

Два других корня находим командами x=EqSolve(f,[2 5.1],0.0001) и x=EqSolve(f,[-100 15],0.0001). В последнем случае указан интервал, на котором находится все три корня. То, что в результате найден корень x = 9 , в известном смысле является случайностью. Гипотетически могли получить любой из трех корней – дело в том, какой корень будет "отсеян" при делении интервала поиска корня. Результаты вычислений показаны на рис. 5.11.

Рис. 5.11. Вычисление корня уравнения методом половинного деления

212

Глава 5. Решение уравнений и оптимизация

На заметку

Алгоритм, реализованный в функции EqSolve(), далеко не самый оптимальный. Он, скорее, отображает наше интуитивное представление о том, в какой последовательности или в соответствии с каким принципом вычисляется корень. Например, при рекурсивном вызове каждый раз проверяется необходимое условие для применимости метода, хотя на самом деле это условие нужно проверять только самый первый раз. Вообще, что касается использования рекурсивных вызовов, то они редко бывают эффективными, но скорее эффектными.

Следующий метод решения алгебраических уравнений, который мы рассмотрим, называется методом хорд. Он сильно напоминает метод половинного деления и состоит в следующем. Для поиска решения уравнения f (x) = 0 необходимо указать начальный интервал поиска. Необходимое условие, как и в методе половинного деления, состоит в том, чтобы на границах интервала поиска решения функция f (x) принимала значения разных знаков. Если это так, то точки на графике функции f (x) на границах интервала поиска решения соединяются отрезком (хордой). Определяется точка пересечения этой хорды с осью абсцисс, и вычисляется значение функции в этой точке. В нее смещается та из границ интервала поиска решения, для которой имеет место совпадение знаков функции. Процесс продолжается до достижения нужной точности. Процесс графически проиллюстрирован на рис. 5.12.

Рис. 5.12. Решение уравнения методом хорд

Для решения уравнений методом хорд создадим специальную функцию, программный код которой приведен ниже:

function x=EqSolve2(f,intl,count,state) n=100;

a=intl(1);

b=intl(2);

if(state) l=b-a;

z=a-0.1*l:1.2*l/n:b+0.1*l;

213

Самоучитель Matlab

plot(z,f(z),'LineWidth',3,'Color','red'); grid on;

title('Решение уравнения вида f(x)=0 методом хорд',...

'BackgroundColor','yellow','FontSize',12,'FontWeight','bold',...

'Color','red','EdgeColor','red'); hold on;

end

for i=1:count if(state) L1y=[0,f(a)]; L1x=[a,a]; L2y=[0,f(b)]; L2x=[b,b]; Ly=[f(a),f(b)]; Lx=[a,b];

plot(L1x,L1y,'LineWidth',2,'Color','blue');

plot(L2x,L2y,'LineWidth',2,'Color','blue');

plot(Lx,Ly,'LineWidth',2,'Color','green'); end

c=(a*f(b)-b*f(a))/(f(b)-f(a)); if(f(a)*f(c)>0) a=c;

else b=c; end

end x=c;

if(state)

plot(intl(1),0,'ks','MarkerFaceColor','k','MarkerSize',5);

plot(x,0,'ko','MarkerFaceColor','k','MarkerSize',7);

plot(intl(2),0,'ks','MarkerFaceColor','k','MarkerSize',5); hold off;

end end

Функция называется EqSolve2(), у нее четыре аргумента, и она возвращает в качестве результата корень уравнения (соответствующая переменная обозначена как x). Функция, которая определяет решаемое уравнение, передается в виде указателя (обозначен как f) первым аргументом функции EqSolve2(). Второй аргумент (обозначен intl) функции EqSolve2() представляет собой список из двух элементов – через него реализуется интервал поиска корня. Третьим аргументом (переменная count) функции EqSolve2() передается целое число итераций, выполняемых при поиске решения уравнения. Четвертый, логический аргумент state, позволяет переходить в режим, при котором помимо вычисления корня также отображается график функции уравнения (с некоторыми дополнительными геометрическими построениями).

214

Глава 5. Решение уравнений и оптимизация

На заметку

Обычно задача состоит в том, чтобы найти корень уравнения с определенной точностью. Оценка точности поиска корня методом хорд выполняется не так просто, как в случае метода половинного деления. Главное преимущество метода хорд состоит в том, что он обеспечивает более быструю сходимость. Чтобы не углубляться в несущественные в данном случае математические подробности, продолжать итерационный процесс будем не до достижения точности, а определенное количество раз.

Командой n=100 вводится переменная, определяющая количество точек, на основании которых будут строиться кривые. Переменным a и b командами a=intl(1) и b=intl(2) присваиваются начальные значения границ интервала поиска решения. Затем запускается условный оператор, в котором в качестве условия проверяется аргумент state. Равенство этого аргумента значению true означает, что помимо вычисления корня будем выполнять еще и геометрические построения. В данном случае строится график функции, выполняются настройки этого графика, а также выделяются вертикальными отрезками границы интервала поиска решения и первая хорда, которая соединяет граничные точки на графике функции.

Командой l=b-a вычисляется длина интервала поиска решения. Затем командой z=a-0.1*l:(b-a+0.2*l)/n:b+0.1*l формируется список со значениями аргумента функции для уравнения. При этом диапазон изменения аргумента для отображения на графике на 20% больше длины интервала поиска решения. Поэтому начальная точка для диапазона изменения аргумента вычисляется как a-0.1*l, а конечная – как b+0.1*l. Шаг дискретности равен 1.2*l/n. График строится командой plot(z, f(z),'LineWidth',3,'Color','red'). Опции, указанные при вызове функции plot(), означают, что толщина линии равна 3 и она красного цвета. Координатная сетка отображается командой grid on. С помощью функции title() задается заголовок графика. Первым аргументом функции передается текст 'Решение уравнения вида f(x)=0 методом хорд', который и отображается в качестве заголовка. Значение 'yellow' опции 'BackgroundColor' означает, что заголовок будет отображаться в области желтого цвета. Опция 'FontSize' со значением 12 задает размер шрифта для отображения заголовка. О том, что шрифт будет жирным, свидетельствует значение 'bold' для опции 'FontWeight'. Опцией 'Color' задается цвет шрифта, а опция 'EdgeColor' определяет цвет рамки вокруг области, в которой отображается заголовок. Значения этих опций указаны с помощью ключевого слова 'red', что означает красный цвет. Таким образом, заголовок будет отображаться красным цветом, жирным шрифтом, на желтом фоне в красной рамке.

215

Самоучитель Matlab

На заметку

Три точки ... в программном коде используются для обозначения перехода к новой строке. Обычно к разбивке кода (в пределах одной команды) на несколько строк прибегают для повышения его читабельности.

С помощью команды hold on переходим в режим "удержания графика" – последующие графические построения будут выполняться в том же самом окне.

Затем запускается оператор цикла, в котором индексная переменная i пробегает значения от 1 до count. Эта индексная переменная нумерует итерации в вычислении корня. В первую очередь при выполнении итерации проверяется значение переменной state. При истинности значения выполняется группа команд, которыми формируются списки точек для отображения отрезков, выделяющих границы текущего интервала поиска решения, и хорды, соединяющей граничные точки на графике. Команды такие: L1y=[0,f(a)] (y - координаты отрезка, выделяющего левую границу интервала поиска решения), L1x=[a,a] (x - координаты отрезка, выделяющего левую границу интервала поиска решения), L2y=[0,f(b)] (y - координаты отрезка, выделяющего правую границу интервала поиска решения), L2x=[b,b] (x - координаты отрезка, выделяющего правую границу интервала поиска решения), Ly=[f(a),f(b)] (y - координаты границ хорды, соединяющей крайние точки на графике) и Lx=[a,b] (x - координаты границ хорды, соединяющей крайние точки на графике).

На заметку

Здесь следует учесть, что отрезки, которыми выделяется диапазон поиска решения, имеют такие координаты. Левый отрезок соединяет точки с координатами (a, 0) и (a, f (a)) , а правый отрезок соединяет точки (b, 0) и (b, f (b)). Хорда – это отрезок, который соединяет точки (a, f (a)) и (b, f (b)). Чтобы отобразить отрезок, достаточно указать его начальную и конечную точки. Технически это сводится к созданию двух списков с координатами начальной и конечной точек отрезка.

Командами plot(L1x,L1y,'LineWidth',2,'Color','blue'), plot (L2x,L2y,'LineWidth',2,'Color','blue') и plot(Lx,Ly,'Line Width',2,'Color','green') отображаются отрезки для выделения границ интервала поиска решения и хорда соответственно. Все линии отображаются с толщиной линии 2, вертикальные отрезки синим цветом, а хорда – зеленым. Напомним, что описанные выше построения выполняются в том случае, если значение аргумента state равно true. Следующая команда выполняется в любом случае: c=(a*f(b)-b*f(a))/(f(b)-f(a)). Этой командой вычисляется точка пересечения хорды с осью абсцисс. Затем необходимо в эту точку сместить одну из границ интервала поиска решения. Для этого в условном

216

Глава 5. Решение уравнений и оптимизация

операторе проверяем результат выражения f(a)*f(c)>0. Если результат истинен, то знак функции слева на границе интервала поиска решения и в "центре" (точке пересечения хорд с осью абсцисс) и сдвигается левая граница – для этого выполняем команду a=c. В противном случае сдвигается правая граница интервала поиска решения с помощью команды b=c. Блок оператора цикла по реализации итерационного процесса на этом завершается.

На заметку

Стоит обратить внимание на два обстоятельства. Во-первых, в начале выполнения алгоритма по поиску корня не проверяется необходимое условие: знаки функции на границах интервала поиска решения должны быть разными. Вовторых, за каждый итерационный цикл при вычислении корня отображаются оба отрезка на границах интервала поиска решения, и это при том, что по сравнению с предыдущей итерацией изменяется положение только одной границы. Это не очень рациональный подход, но на конечном результате он не сказывается.

В качестве результата функцией EqSolve2() возвращается значение, полученное как значение точки пересечения хорды с осью абсцисс на последней итерации. Поэтому выполняем команду x=c.

Следующая группа команд выполняется, если значение переменной state равно true. В частности, на графике на оси абсцисс выделяются точки начального интервала поиска решения и точка, найденная в качестве корня уравнения. Для отображения левой границы начального интервала поиска корня уравнения используем команду plot(intl(1),0,'ks','Marker FaceColor','k','MarkerSize',5. Здесь, поскольку в процессе поиска корня переменные a и b меняли свое значение, значение левой границы получаем инструкцией intl(1). Это x -координата отображаемой точки. Ее y -координата равна нулю. Инструкция 'ks' означает, что график (состоящий в данном случае из одной точки) отображается черным цветом и в качестве маркеров используются квадраты. Цвет заполнения маркера задается опцией 'MarkerFaceColor', значение 'k' означает черный цвет. Опцией 'MarkerSize' задается размер маркеров – в данном случае это 5.

Точка, найденная как корень уравнения, выделяется с помощью команды plot(x,0,'ko','MarkerFaceColor','k','MarkerSize',7). Используется маркер черного цвета круглой формы (инструкция 'ko'). Размер маркера установлен равным 7. Командой plot(intl(2),0,'ks', 'MarkerFaceColor','k','MarkerSize',5) правая граница интервала поиска решения выделяется так же, как и левая. Наконец, командой hold off отменяется режим удержания графика.

На рис. 5.13 показано командное окно с командами, которыми проверяется работоспособность созданного программного кода.

217

Самоучитель Matlab

Рис. 5.13. Командное окно с вызовом функции EqSolve2()

Рассмотрим команды подробнее. Сначала командой f=@(x)((x-1).* (x-5).*(x-9)) определяется функция, для которой ищется корень уравнения. В данном случае речь идет о функции f (x) = (x −1)(x − 5)(x − 9).

На заметку

Функция EqSolve2() описана так, что ее первый аргумент, являющийся ссылкой на функцию решаемого уравнения, должен применяться к аргументусписку. Такое предположение неявно подразумевалось при использовании команды plot(z,f(z),'LineWidth',3,'Color','red'), когда инструкцией f(z) вызывалась функция уравнения с аргументом-списком. Поэтому при определении функции уравнения мы использовали оператор поэлементного умножения *.

Командой EqSolve2(f,[0 4],100,false) определяется корень уравнения f (x) = 0 на интервале от 0 до 4 на основе 100 итераций без создания графика. В этом интервале находится корень x = 1 , который, собственно,

ивычисляется. Чтобы увидеть еще и графические построения, используем команду EqSolve2(f,[0 4],4,true). В данном случае корень вычисляем всего четырьмя итерациями – исключительно чтобы проследить последовательность построений. При большем числе итераций большинство вспомогательных линий будет на графике неразличимо. Помимо вычисления корня (с небольшой, откровенно говоря, точностью), создается еще

ирисунок, который представлен на рис. 5.14.

Метод, который рассмотрим далее, называется методом Ньютона или методом касательных. Он также применяется для решения уравнений вида f (x) = 0 и состоит в следующем.

Для поиска корня берется начальное значение для корня. В этой точке для графика функции f (x) строится касательная. Точка пересечения этой ка-

218

Глава 5. Решение уравнений и оптимизация

Рис. 5.14. Графика создана в результате вычисления корня методом хорд

сательной с осью абсцисс выбирается как следующее приближение для искомого корня.

Фактически речь идет о процессе, подразумевающем уточнение значения корня на основе значения, вычисленного на предыдущей итерации. Чтобы реализовать этот метод, необходимо знать не только функцию f (x), но и ее производную f ′(x). Кроме того, метод применим далеко не всегда.

На заметку

С математической точки зрения при известной функции можно полагать известной и производную (если такая производная существует). С автоматическим вычислением такой производной дела обстоят хуже. Конечно, можно вычислять производную в числовом виде, но, несмотря на кажущуюся простоту, это не самая тривиальная задача. Желательно, по крайней мере, иметь представление, к какому классу относится дифференцируемая функция.

Что касается применимости метода, то искать корень методом касательных можно в той области, для которой знак функции совпадает со знаком производной.

Если на n -м итерационном шаге получено приближение xn для корня уравнения, то на следующей итерации приближение для корня определя-

219

Самоучитель Matlab

ется как x

n +1

= x

n

f (xn )

 

. Процедура поиска корня уравнения методом

f ′(xn )

 

 

 

 

 

 

 

 

 

 

касательных иллюстрируется на рис. 5.15.

Рис. 5.15. Решение уравнения методом касательных

Рассмотрим программный код функции, предназначенной для решения методом касательных уравнений вида f (x) = 0 , где функция f (x) является полиномом. Ниже приведен программный код этой функции:

function x=EqSolve3(a,x0,eps) z=x0;

b=polyder(a); dz=-polyval(a,z)/polyval(b,z); while(abs(dz)>eps)

z=z+dz; dz=-polyval(a,z)/polyval(b,z); end

x=z; end

Функция называется EqSolve3() и у нее три аргумента: массив a с коэффициентами исходного полинома, начальное приближение для корня x0 и параметр eps, имеющий отношение к точности вычисления корня. В качестве результата функцией возвращается корень уравнения (переменная x).

Локальной переменной z командой z=x0 в качестве значения присваивается начальное приближение для корня уравнения. Командой b=polyder(a) вычисляется список b, который представляет собой набор коэффициентов для производной от полинома, заданного списком a. Здесь мы использовали встроенную функцию polyder(), предназначенную для вычисления производной от полинома.

220