Циклы: for, do, end do, while и break
В цикле выполняются многие команды Maple: sum, численное интегрирование, fsolve, численное решение дифференциальных уравнений (dsolve(...type=numeric)) и др.
Пример циклов был в разделе Нелинейная динамика и хаос. Собственный цикл пишется в разных случаях, и, чтобы разговор был более конкретным, напишем его для суммирования квадратов целых чисел от 1 до 100 с помощью команды sum:
- sum(i^2,i=1..100);
Эта команда содержит цикл внутри себя. Сделанный вручную цикл должен будет работать так же.
Во-первых, надо решить, где будет храниться ответ, и присвоить этой переменной начальное значение 0:
- answer:=0;
В этой переменной накапливаются результаты последовательного суммирования квадратов целых чисел от 1 до 100. Далее пишется обычный цикл суммирования в синтаксисе Maple for..from..to..do..enddo.
do означает «начало цикла», enddo означает «конец цикла».
- for i from 1 to 100 do
- answer:=answer + i^2;
- enddo;
(См. выдачу.) Может, этой информации много, но она понадобится, когда цикл по неизвестной причине не работает и приходится искать ошибку. Чтобы убрать избыточную информацию, ставьте после enddo двоеточие, а не ;, а после цикла вызовите answer:
- answer:=0;
- for i from 1 to 100 do
- answer:=answer + i^2;
- enddo:
- answer;
А если понадобится суммировать нечетные числа с 1 до 99? Применяем by с 1 до 99, задавая шаг = 2:
- answer:=0;
- for i from 1 by 2 to 99 do
- answer:=answer + i^2;
- enddo:
- answer;
Бывает, что неизвестно конкретное число, до которого следует «крутить» цикл, а его прекращение происходит при определенном условии.
Например: что произойдет, если вычислять косинус числа снова и снова. Пусть Maple сделает это 20 раз и покажет, что происходит:
- x:=5.;
- forifrom 1 to 20 do
Берем косинус от x, результат заносим в него же и повторяем:
- x:=cos(x);
- end do;
Похоже, что результат к чему-то сходится. Подумав, поймете, что сходится к решению уравнения cos(x) = x. Но 20 шагов явно недостаточно, чтобы получить 10 правильных знаков. Для получения приемлемой точности, например 1e-8, надо построить циклы.
Выберите начальное значение x и начальное значение «предыдущего значения» xold.
- x:=5.;xold:=0;
«Крутите» цикл while до тех пор, пока |x–xold| >1e-8 (вот почему нужно начальное значение xold).
Замечание: прежде чем начинать выполнять цикл while, прочтите команды и комментарии к ним:
- while abs(x-xold)>1e-8 do
Во временную переменную tempx заносите вычисленное значение cos(x):
- tempx:=cos(x):
Текущее значение х заносите в xold, чтобы иметь возможность сказать, когда надо выходить из цикла:
- xold:=x:
В х заносите новое значение х (называвшееся tempx):
- x:=tempx;
- print(x); # print the new x each iteration
Возврат на начало и повторение вычислений
- enddo: # подавление печати столбца
Это часто применяется в машинных расчетах и называется последовательным интегрированием. Посмотрите внимательно на структуру цикла, в особенности на то, как вычисляется каждая переменная и где она хранится (эти приемы уже применялись раньше, и они могут пригодиться в дальнейшем).
Есть две часто используемых разновидности циклов. В задачах вы потренируетесь работе с ними.
Напишите цикл, вычисляющий разложение косинуса в степенной ряд: для нечетных n. Вне цикла задайте значение х, используя by для получения нечетных номеров, и продолжайте цикл, пока значение последнего добавляемого в сумму члена не станет меньше чем 1e-8. Для этого нужен цикл со сложением – см. цикл суммирования.
Метод секущих применяется для решения сложных уравнений с одной переменной в дифференциальных уравнениях. Кстати, внутри команды fsolve применяется нечто подобное.
Идея метода
Сложное уравнение вида f(x) = 0 надо решить относительно х. Метод секущих начинается с двух достаточно близких величин x1 и x2, значения которых «угаданы» заранее (как – это отдельная большая тема). По ним вычисляются f1 = f(x1) и f2 = f(x2). Уравнение прямой, проходящей через эти две точки: Это формула аппроксимации функции f(x) прямой линией. Ее можно применять, чтобы получить x как приближенное решение сложного уравнения. Для этого нужно решить уравнение прямой, получить новое значение x, при котором у = 0, назвать его x3 и рассматривать как следующее приближение к точному решению.
- restart;
- Eqline:=0-f2=(f2-f1)/(x2-x1)*(x-x1);
- x3:=solve(Eqline,x);
Это хорошее алгебраическое выражение, но для численных расчетов оно плохое, поскольку при приближении к корню избранный способ выбора дает близкие значения x1 и x2; f1 и f2. В результате при вычислении x3 получится нечто похожее на деление 0/0. В арифметике с плавающей точкой это вызовет затруднения, поэтому лучше несколько изменить формулу:
Из нее, добавив небольшую величину к предыдущей, получаем уточненное значение х3, не получая при этом проблем. В ней малы (x2 – x1) и (f2 – f1), поэтому снова получается /0, но значение f2 в числителе тоже мало, поскольку близко f(x) = 0. В итоге получается нечто вроде тоже малое.
В Maple есть обучалка \ «Calculus – SingleVariable», которая красиво анимирует этот метод. Посмотрите.
Напишите цикл для решения уравнения tan(x) – 2x = 0 для корня вблизи x = 1. (Сделайте свой цикл похожим на цикл с последовательной подстановкой.) В цикле предусмотрите выход по условию f2 = f1 при достижении приемлемой точности (в Maple это обычно 10 значащих цифр; см. Maple help, Digits). Это убережет вас от 0/0. Делать этот цикл с while трудно, но можно, если применить команду break.
Пример работы с break (глупый):
Пусть цикл идет от 1 до 100, но выйти из него надо тогда, когда i = 20:- for i from 1 to 100 do
- if i=20 then break end if;
- enddo;
Проверьте, какое значение имеет i после выхода из цикла.
- i;
Можно использовать этот прием в цикле метода секущих. Для проверки необходимости сначала примените while, повторяя вычисления, пока не достигните |f2| <= 10-14, а деление на 0 станет возможным. Затем поставьте break для прерывания цикла.