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

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

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

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

На заметку

Производная от полинома также является полиномом. Так, если некоторый

полином

P (x)

описывается коэффициентами a ,

a

2

, ..., a

n

, a

n +1

(то есть

 

n

 

 

 

 

 

1

 

 

 

 

P (x) = a xn +a xn−1

+... +a

n

x +a

n +1

), то производная от этого поли-

n

1

2

 

 

 

 

 

 

 

 

 

 

 

нома P′(x) = na xn−1

+(n −1)a xn−2 +... + 2a

 

 

x +a

n

. Это полином

n

 

1

 

 

2

 

 

n−1

 

 

 

 

степени на единицу меньшей, чем исходный. Если коэффициенты исходного полинома формируют список a = [a1,a2,...,an,an +1 ], то коэффициенты полинома-производной формируют список b = [b1,b2,...,bn−1,bn ], причем имеет место соотношение bk = (n k +1)ak для всех индексов k = 1,2,...,n . Встроенная функция polyder() по списку коэффициентов a исходного полинома вычисляет список b коэффициентов полинома-производной.

Командой dz=-polyval(a,z)/polyval(b,z) вычисляется приращение для переменной z. Это взятое со знаком минус отношение функции в текущей точке z к значению производной в этой же точке. Для вычисления значения полинома в точке мы использовали встроенную функцию polyval(). Первым аргументом функции передается список коэффициентов полинома. Второй аргумент функции – точка, в которой вычисляется значение полинома.

На заметку

Вторым аргументом можно указать список значений. Тогда полином будет вычислен для каждого из этих значений.

После этого запускается оператор цикла. Проверяется условие abs(dz)>eps, которое состоит в том, что добавка dz (по абсолютной величине) к текущему значению переменной z превышает погрешность eps.

На заметку

Строго говоря, это не означает, что корень вычислен с нужной точностью. Другим словами, переменная eps в общем случае не определяет точность вычисления корня уравнения. Более того, такой алгоритм не обеспечивает гарантированного завершения итерационного процесса – если начальное приближение для корня выбрать неудачно, можем получить бесконечный цикл. Но такую экзотику здесь исследовать не будем.

В операторе цикла командой z=z+dz вычисляется новое приближение для корня, а затем командой dz=-polyval(a,z)/polyval(b,z) на основе этого нового приближения вычисляется добавка для следующего итерационного шага. После завершения оператора цикла в переменную x записываем полученный результат (команда x=z). На рис. 5.16 показано окно редактора m-файлов с кодом созданной функции.

221

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

Рис. 5.16. Код функции EqSolve3() в окне редактора m-файлов

РезультатывычислениякорнейполиномаспомощьюфункцииEqSolve3() приведены в документе на рис. 5.17.

Рис. 5.17. Результат вычисления корней полинома с помощью функции EqSolve3()

Здесь командой a=[1,-15,59,-45] создается список для исходного полинома – это в терминах переменной x выражение x3 −15x2 + 59x − 45 .

222

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

У этого полинома три корня: x = 1 , x = 5 и x = 9 . В последнем убедиться несложно – достаточно вычислить полином в соответствующих точках. Так, в результате выполнения команды polyval(a,[1,5,9]) получаем список из трех нулей.

Три корня полинома находим последовательно с помощью команд

EqSolve3(a,2,0.001), EqSolve3(a,6,0.001) и EqSolve3(a,12, 0.001). Результат, как видим, вполне приемлемый.

На заметку

Стоит напомнить, что в Matlab есть встроенная функция roots() для вычисления корней полинома. В этом отношении написание собственных функций для вычисления корней полиномов представляется малоперспективным.

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

function x=EqSolve4(f,x0,eps) z=x0;

h=eps/100;

n=1000; for i=1:n

df=(f(z+h)-f(z-h))/2/h; dz=f(z)/df;

z=z-dz; if(abs(dz)<eps) break; end

x=z; end

У функции три аргумента: указатель на функцию уравнения f, начальное приближение для корня x0, а также параметр eps, влияющий на точность, с которой вычисляется корень.

Переменной z присваивается в качестве значения начальное приближение для корня. Переменная h определяет шаг приращения аргумента для вычисления производной от функции уравнения. Значение этой переменной на два порядка меньше значения аргумента eps. Переменная n определяет максимально возможное количество итераций, выполняемых при поиске корня уравнения. Именно эта переменная указана в качестве верхней границы диапазона изменения индексной переменной.

В операторе цикла выполняется несколько простых команд. Командой df=(f(z+h)-f(z-h))/2/h вычисляется значение производной для функции в точке с аргументом z.

223

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

На заметку

Напомним, что по определению производной от функции f (z) называется предел f ′(z) = lim f (z +dz) − f (z) . В данном случае мы использовали

dz →0

dz

приближенное выражение

для производной f ′(z) ≈

f (z + h) − f (z h)

.

 

 

 

2h

В принципе, чем меньше параметр h , тем точнее выражение для производной.

Командой dz=f(z)/df вычисляется величина декремента для корня уравнения. Командой z=z-dz вычисляется новое итерационное значение для корня уравнения. После этого выполняется условный оператор if(abs(dz)<eps) break. Суть этой команды в том, что если изменение значения корня на какой-то итерации становится меньше параметра eps, итерационный процесс прекращается. Таким образом, итерационный процесс продолжается до тех пор, пока изменение корня не станет достаточно малым или пока не будет выполнено n итераций.

Полученное в качестве итерационного процесса значение z возвращается результатом функции EqSolve4(). Окно редактора m-файлов с кодом функции представлено на рис. 5.18.

Рис. 5.18. Код функции EqSolve4() в окне редактора m-файлов

Примеры вызова функции EqSolve4() представлены в документе на рис. 5.19.

Командой f=@(x)(x^3-15*x^2+59*x-45) мы определяем функцию уравнения вида f (x) = x3 −15x2 + 59x − 45 . Это все тот же полином, у которого три корня: x = 1 , x = 5 и x = 9 . Находим все три корня этого уравнения командами EqSolve4(f,2,0.00001), EqSolve4(f,6,0.00001)

224

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

Рис. 5.19. Решение уравнения с помощью функции EqSolve4()

и EqSolve4(f,12,0.00001). С удовлетворением наблюдаем, что корни найдены.

Метод Ньютона, с некоторыми модификациями, может быть расширен для поиска решений систем алгебраических уравнений. Например, допустим, что нам нужно решить систему из n уравнений f1(x1,x2,...,xn ) = 0 ,

f2(x1,x2,...,xn ) = 0 , ..., fn(x1,x2,...,xn ) = 0 относительно переменных x1 , x2 , ..., xn . Для поиска решения системы необходимо знать начальные при-

ближения для каждой из переменных. На основе нулевых (начальных) приближений вычисляется первое приближение, затем второе, и так далее. Рассмотрим этот процесс подробнее. Допустим, что имеется k -е приближение для решения системы x1 = x1(k) , x2 = x2(k) , ..., xn = xn(k) . Для расчета следующего приближения необходимо вычислить матрицу производных:

 

 

f

x

 

 

f

x

 

 

...

f

x

n−1

f

 

x

n

 

 

 

1

 

1

 

1

 

2

 

 

1

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

f2

x1

 

f2

x2

 

...

f2

xn−1

f2

 

xn

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ˆ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

...

 

 

...

 

 

...

 

...

 

 

 

...

 

 

A =

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

f

x

 

f

x

 

...

f

x

 

f

 

x

 

 

 

1

2

n−1

 

 

 

 

n−1

 

 

n−1

 

 

 

n−1

 

 

n−1

 

 

n

 

 

f

x

 

 

f

x

 

 

...

f

x

 

 

f

 

x

 

 

 

 

1

 

2

 

n

−1

 

n

 

 

 

n

 

 

n

 

 

 

n

 

n

 

 

 

Матрица вычисляется в точке k -го приближения. Другими словами, мы вы-

 

f (x

(k),x

(k),...,x(k))

числяем квадратную матрицу, элементы которой a =

i

1

2

n

.

 

xj

 

ij

 

 

 

 

 

 

 

 

225

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

Введем в рассмотрение вектор

x(k)

(k)

x1(k) = x2

...

xn(k)

Это вектор задает значение решения на k -м итерационном шаге. Следую-

 

 

 

 

 

 

 

(k +1)

(k)

ˆ−1

 

(k)

). Здесь че-

щую итерацию можно вычислить так: x

 

 

= x

 

A

f

(x

−1

 

 

 

 

 

 

 

 

 

 

ˆ

 

 

(k)

 

ˆ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

) обозначе-

рез A

обозначена матрица, обратная к матрице A. Через f (x

 

 

(k)

вектор-функция

 

 

 

 

 

 

 

на вычисленная в точке x

 

 

 

 

 

 

 

 

 

 

 

1

 

(k)

(k)

 

 

(k)

 

 

 

 

 

 

 

(x

1

,x

2

,...,x

n

 

 

 

 

 

 

 

 

 

f

 

 

 

)

 

 

 

 

 

 

 

 

(k)

(k)

 

(k)

 

 

 

 

 

 

 

 

 

 

(x

 

 

 

 

 

 

 

 

f

 

,x

 

,...,x

 

)

 

 

 

 

f (x

) =

2

 

1

 

2

 

 

n

 

 

 

 

 

 

 

(k)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

...

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(k)

(k)

 

(k)

 

 

 

 

 

 

 

 

 

 

(x

 

 

 

 

 

 

 

 

 

f

1

,x

2

,...,x

n

)

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

 

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

function x=NSolve(f,x0,N)

%Начальные приближения для поиска корней: z=x0';

%Шаг изменения аргумента для вычисления производной: h=0.00001;

%Количество независимых переменных:

n=length(x0);

%Единичная матрица: E=eye(n);

%Нулевой вектор-столбец: func=zeros(n,1);

%Нулевая матрица: A=zeros(n);

%Вложенные операторы цикла: for k=1:N

for i=1:n

F=f{i}; % указатель на функцию получаем из списка указателей func(i,1)=F(z'); % вычисление значения функции

for j=1:n

A(i,j)=(F(z'+h*E(:,j)')-F(z'))/h; % заполнение матрицы производных end

end

226

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

% Новое приближение для вычисления корня: z=z-A\func;

end

x=z'; % результат вычислений end

У функции NSolve() три аргумента:

Список f с указателями на функции, которые задают систему алгебраических уравнений. Это особый список – список ячеек. Его мы будем обсуждать отдельно. Пока же для нас важно, что элементами списка f являются указатели на функции.

Список x0 представляет собой набор с начальными значениями переменных, относительно которых решается система уравнений.

Аргумент N задает количество итераций, которые выполняются при поиске решения уравнения.

Результатом функцией возвращается список x, элементами которого являются значения переменных, полученные как решение системы уравнения.

Первой командой в функции мы объявляем локальную переменную и присваиваем ей значение: z=x0'. Переменная z получает в качестве значения транспонированный вектор (список) начальных значений x0. При присваивании значения вектор x0 транспонируется.

На заметку

При вычислениях мы используем векторы-столбцы – например, вектор значений независимых переменных z или вектор значений функций func (описывается далее). Вместе с тем, аргументами функции NSolve() передаются векторы-строки. Это делается для удобства ввода команд при вызове функции NSolve(). Поэтому периодически при вычислениях используется процедура транспонирования векторов.

Переменная h задает значение приращения аргумента при вычислении производной.

На заметку

С одной стороны, значение переменной должно быть достаточно маленьким, чтобы повысить точность вычисления производной. С другой стороны, всегда следует помнить о конечной точности машинных вычислений.

Командой n=length(x0) в переменную n записывается размер списка начальных значений, то есть количество независимых переменных, относительно которых решается система уравнений. При этом неявно предполагается, что количество уравнений в системе такое же, как и количество независимых переменных.

227

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

Командой E=eye(n) создаем единичную матрицу, размер которой определяется количеством независимых переменных системы. Из этой единичной матрицы мы при вычислении частных производных будем извлекать векторы-столбцы (с единицей в соответствующей позиции). Также нам придется создать вектор-столбец со значениями функций системы уравнений. В качестве нулевого приближения создаем вектор-столбец, заполненный нулями. Достигается это командой func=zeros(n,1). Командой A=zeros(n) создается нулевая матрица – квадратная, размер задается количеством независимых переменных системы. После этого запускаются вложенные операторы цикла. Индексная переменная внешнего цикла пробегает значения от 1 до N и нумерует итерации вычисления корня. Первым внутренним циклом, индексная переменная которого пробегает значения от 1 до n (количество независимых переменных и характерный размер векторов и матриц, используемых в вычислениях), заполняется вектор-столбец со значениями функций системы и матрицы частных производных.

Во внутреннем операторе цикла командой F=f{i} переменной F в качестве значения присваивается указатель на функцию, индекс которой в списке указателей определяется значением индексной переменной i внутреннего цикла. Обратите внимание: вместо круглых скобок для индексирования элементов списка f используются фигурные скобки. Поступаем так, поскольку предполагаем (и не без оснований), что список f представляет собой список ячеек.

На заметку

Главная особенность списка ячеек состоит в том, что элементами такого списка могут быть объекты разной природы – текст, числа, другие списки, и так далее – в том числе и указатели на функции. Другими словами, список ячеек представляет собой список, элементами которого являются ячейки. В ячейку, в свою очередь, записывается то или иное значение. Таким образом, реальное содержимое оказывается как бы упакованным в ячейку. Такой подход необходим при работе со сложными типами данных. Создание списка указателей также предполагает создание списка ячеек. Доступ к элементам такого списка осуществляется путем индексирования, но индекс указывается не в круглых, а в фигурных скобках. В этом случае получаем доступ к реальному содержимому соответствующей ячейки. Если выполнить индексирование с использованием круглых скобок, получим доступ к ячейке.

С помощью команды func(i,1)=F(z') вычисляется значение соответствующей функции системы при заданных значениях независимых переменных. При этом аргумент, который представляет собой список значений независимых переменных, необходимо транспонировать. Полученный результат записывается в качестве соответствующего элемента списка func. Затем запускается еще один внутренний оператор цикла, в рамках которого заполняется матрица частных производных.

228

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

Основу тела цикла составляет команда A(i,j)=(F(z'+h*E(:,j)')-

F(z'))/h. Здесь для вычисления частной производной F(x1,x2,...,xn )

xj

от функции

F(x1,x2,...,xn )

xj

F(x1,x2,...,xn )

использована приближенная

формула

F(x1,...,xj

+ h,...,xn ) −F(x1,...,xj,...,xn )

.

Другими

 

 

 

 

h

 

словами, для вычисления частной производной по аргументу xj вычисляем значение функции с этим аргументом, увеличенным на шаг приращения h (и фиксированными прочими аргументами), отнимаем значение функции при всех фиксированных аргументах и полученный результат делим на шаг приращения h .

Чтобы решить первую задачу – вычислить значение функции с увеличенным на шаг дискретности одним аргументом, используем инструкцию F(z'+h*E(:,j)'). Здесь интерес представляет аргумент функции, который представляет собой сумму z'+h*E(:,j)'. Первое слагаемое тривиально – это транспонированный вектор текущих значений независимых переменных. Второе слагаемое – результат транспонирования вектора h*E(:,j). Инструкцией E(:,j) возвращается j-й столбец единичной матрицы E. У этого вектора-столбца все элементы нулевые, кроме j-го, который равен 1. После умножения на h получаем вектор-столбец со всеми нулевыми элементами, кроме j-го, который равен h. Поэтому в результате выполнения инструкции z'+h*E(:,j)' получаем вектор-столбец, у которого по сравнению с вектором z' элемент в j-й строке получает приращение h, чего мы и добивались. Прочие вычисления тривиальны.

Наконец, зная вектор значений функции и матрицы производных, можем вычислить следующее приближение для вектора значений независимых переменных. Этой цели служит команда z=z-A\func. После того, как все необходимые итерации выполнены, результат, после транспонирования, записываем в переменную x с помощью команды x=z'. На этом вычисление решения системы уравнений заканчивается. Окно редактора m-файлов с кодом созданной функции представлено на рис. 5.20.

Теперь воспользуемся созданной функцией для решения системы из двух уравнений: x2 + 2xy x −14 = 0 и 3y + xy2 −27 = 0 . У этого уравнения есть два решения. Первое x = 2 и y = 3 , а также еще одно x ≈ 7.2353

иy ≈ −2.1502 . Эти решения и попытаемся найти. На рис. 5.21 представлено командное окно с примером создания списка указателей на функции

ивызовами функции NSolve() для решения соответствующей системы нелинейных алгебраических уравнений.

229

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

Рис. 5.20. Окно редактора m-файлов с кодом функции

Рис. 5.21. Командное окно с примерами использования функции NSolve() для решения системы нелинейных уравнений

230