Программирование: логика, циклы, процедуры

Как написать свою программу в Maple, используя логику, циклы и процедуры

Версия для печати

Циклы: 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: # подавление печати столбца

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

Есть две часто используемых разновидности циклов. В задачах вы потренируетесь работе с ними.

Задача 8.3 о разложении cos в ряд

Напишите цикл, вычисляющий разложение косинуса в степенной ряд: для нечетных n. Вне цикла задайте значение х, используя by для получения нечетных номеров, и продолжайте цикл, пока значение последнего добавляемого в сумму члена не станет меньше чем 1e-8. Для этого нужен цикл со сложением – см. цикл суммирования.

Задача 8.4 о методе секущих

Метод секущих применяется для решения сложных уравнений с одной переменной в дифференциальных уравнениях. Кстати, внутри команды 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, не получая при этом проблем. В ней малы (x2x1) и (f2f1), поэтому снова получается /0, но значение f2 в числителе тоже мало, поскольку близко f(x) = 0. В итоге получается нечто вроде тоже малое.

В Maple есть обучалка \ «Calculus – SingleVariable», которая красиво анимирует этот метод. Посмотрите.

Задача 8.5 о нахождении корней уравнения

Напишите цикл для решения уравнения 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 для прерывания цикла.