1-2 Моделирование / Matlab. Практический подход. Самоучитель
.pdf
Глава 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
