1. Структурное программирование
на АЯ Pascal
2. Табуляция функций
3. Приближённое интегрирование функций
4. Рекуррентные вычисления
5. Суммирование степенных рядов
6. Цепные дроби
Управляющие конструкции АЯ Pascal структурированные и это создаёт прекрасные предпосылки для использования метода пошаговой детализации. Для выражения абстрактных понятий в АЯ Pascal существует специальный тип управляющих конструкций - процедуры. Их изучение требует времени, а пока воспользуемся так называемым псевдокодом, который позволяет использовать уже известные нам управляющие конструкции с описанием вложенных в них операций на естественном языке. Рассмотрим пример применения псевдокода.
Задача. Определить среднее по значению из трёх различных чисел входа.
Практически сразу мы можем написать такую абстрактную программу:
program Middle;
var a,b,c,r: Integer;
begin
ReadLn(a,b,c);
{1. r := Среднее по значению из переменных
a, b и c с различными значениями.};
WriteLn(r);
end.
Далее детализируем блок 1:
if a > b then
{1.1. r := Среднее по значению из переменных a, b и c
с различными значениями. Причём a > b.}
else
{1.2. r := Среднее по значению из переменных b, a и c.
Причём b > a.}
Заметим, что блоки 1.1 и 1.2 c с точностью до обозначений переменных одинаковы, что позволяет нам в последующем сосредоточиться на детализации одного блока. Пусть это будет 1.1.
Так как известно, что a > b, то возможны
следующие варианты соотношения значений
переменных:
1) с > a > b, 2) a > c > b, 3) a > b > c.
Анализ этих соотношений приводит к следующей
детализации блока 1.1:
if c > a then
r := a
else
{1.1.1. r := max(b,c)}
Детализация блока 1.1.1 очевидна. Далее выполняем необходимые подстановки и собираем блок 1.1:
if c > a then
r := a
else
if b > c then r := b else r := c
Блок 1.2 получаем из блока 1.1 перестановкой местами имён переменных a и b. Далее собираем блок 1:
if a > b then
if c > a then
r := a
else
if b > c then r := b else r := c
else
if c > b then
r := b
else
if a > c then r := a else r := c
Теперь остаётся поставить этот блок на своё место и мы получим почти готовую программу. Останется только добавить некоторые технические детали улучшающие наглядность ввода и вывода данных.
Пусть некоторая функция y = f(x) должна быть протабулирована на отрезке (a,b) с шагом h = (b - a) / n, где n - количество интервалов табуляции. Эта задача может быть решена с помощью абстрактной программы Tabula:
program Tabula;
var a,b,x,y,h: Real;
n,i: Integer;
begin
ReadLn(a,b,n);
h:=(b-a)/n;
i:=0; x:=a;
while i<=n do
begin
{1. y:=f(x)};
WriteLn(x,y);
i:=i+1; x:=x+h;
end;
end.
Остаётся вместо блока 1 подставить фрагмент программы вычисления значения реальной функции. Пример детализации блока 1 для вычисления значения функции y = Sin(x)/x:
if Abs(x)<1e-5 then
y:=1
else
y:=Sin(x)/x
Здесь условие Abs(x) < 1e-5 определяет близость переменной x к нулю и введено для исключения возможности появления ошибки "Деление на нуль". В рабочей программе, естественно, надо предусмотреть необходимые меры по наглядному представлению данных на её входе и выходе. Окончательный вариант программы Tabula:
program Tabula;
const eps = 1e-5;
var a,b,x,y,h: Real;
n,i: Integer;
begin
Write('Введите a,b и n: ');
ReadLn(a,b,n);
h:=(b-a)/n;
i:=0; x:=a;
WriteLn('x':8,'y':8);
WriteLn('----------------');
while i<=n do
begin
if Abs(x)<eps then
y:=1
else
y:=Sin(x)/x;
WriteLn(x:8:3,y:8:3);
i:=i+1; x:=x+h;
end;
end.
Определённый интеграл от функции y = f(x)
на интервале интегрирования [a,b] можно
приближенно найти методом прямоугольников по
формуле:
S = ( (y[1]+y[2] + ... + y[n] ) * h, где y[i] = f(x[i]), i = 1..n и h = (b - a) / n.
Здесь n - число равных отрезков, на которые
делится интервал интегрирования [a,b], причём
x[1] = a, x[i] = x[i - 1] + h для i = 2..n.
За основу можно взять следующую абстрактную программу:
program Integral;
var a,b,x,y,h,S: Real;
n,i: Integer;
begin
ReadLn(a,b,n);
h:=(b-a)/n;
i:=1; x:=a; S:=0;
while i<=n do
begin
{1. y:=f(x)};
S:=S+y;
i:=i+1; x:=x+h;
end;
S:=S*h;
WriteLn(S);
end.
Рекуррентные вычисления основаны на многократном повторном использовании одной и той же формулы. Такие вычисления называют также итерационными. Для их реализации используют циклические программы. Известна итерационная формула для вычисления квадратного корня из x: y[0] = x, y[i+1] = ( y[i] + x / y[i] ) / 2 для i > 0. Вычисления можно прекратить, когда будет достигнута необходимая точность: Abs( y[i+1] - y[i] ) <= Eps. Следующая программа решает поставленную задачу.
program MySqrt;
const Eps =1e-5;
var x: Real;
y: Real; {Текущее значение}
ys: Real; {Следующее значение}
begin
Write('Entry x: ');
ReadLn(x);
ys:=x;
repeat
y:=ys;
WriteLn('y = ', y);
ys:=(y + x/y)/2;
until Abs(ys-y)<Eps;
WriteLn('MySqrt(', x,') = ', ys);
WriteLn(' Sqrt(', x,') = ', Sqrt(x));
end.
Рассмотрим на примере разложения функции y = Exp(x)
в степенной ряд Тейлора:
y = 1 + x + x^2/2! + x^3/3! + ... + x^i/i! + ... .
Обозначим через a[i] i-ый член ряда. Для
степенных рядов a[i] обычно легко выражается через
a[i-1] по общей формуле a[i] = K*a[i-1]. Определим K для
нашего примера. K = a[i] / a[i-1] = (x^i / i!) / (( x^(i-1) /
(i-1)!) = x / i . Отсюда следует, что a[i] = x * a[i-1] / i, то есть
мы получили итерационную формулу для вычисления
текущего члена ряда. В общем случае мы приходим к
следующей абстрактной программе вычисления
суммы степенного ряда:
program SumRow;
var s,a,x: Real;
i: Integer;
begin
ReadLn(x);
i:=1;
{1. a:=A1};
{2. s:=S0};
while i < 20 do
begin
i:=i+1;
{3. a:=K*a};
s:=s+a;
end;
WriteLn(s);
end.
В этой программе A1 - это значение первого члена ряда, для которого справедливо a[2] = K*a[1]; S0 - сумма начальных членов ряда по A1 включительно. Для нашего примера абстрактные операторы программы SumRow будут иметь такую детализацию: 1.: a := x ; 2.: s := 1 + x ; 3.: a := a*x / i .
В качестве примера приведём
представление функции y = tg(x) в виде цепной дроби:
y = x / (1 - x^2 / (3 - x^2 / (5 - x^2 / (7 - x^2 / (9 - ...) ) ) ) ) .
Составим программу MyTan для приближённого
вычисления тангенса на основе этой цепной дроби.
Если представить i-ый знаменатель дроби как d[i] -
v[i], то соответственно (i-1)-ый : d[i-1] - v[i-1] . Очевидно,
что d[i-1] = d[i] - 2 , а v[i-1] = x^2 / d[i] - v[i] . Полагая d[5] = 9 и v[5]
= 0 последовательно получим
d[4] = 7 и v[4] = x^2 / 9,
d[3] = 5 и v[3] = x^2 / (7 - x^2 / 9),
d[2] = 3 и v[2] = x^2 / (5 - x^2 / (7 - x^2 / 9)),
d[1] = 1 и v[1] = x^2 / (3 - x^2 / (5 - x^2 / (7 - x^2 / 9))).
Таким образом мы видим, что рекуррентные
соотношения для d и v работают правильно.
Очевидно, что окончательный результат можно
определить по формуле x / (d[1] - v[1]) . Воплотим теперь
этот подход в программе MyTan:
program MyTan;
var x,x2,xg,y,v: Real;
d: Integer;
begin
Write('Entry angle[grad]: ');
ReadLn(xg);
x:=xg*Pi/180;
x2:=sqr(x);
d:=9;
v:=0;
repeat
v:=x2/(d-v);
d:=d-2;
until d = 1;
y := x/(d-v);
WriteLn('MyTan(', x, ') = ', y);
WriteLn(' Tan(', x, ') = ', Sin(x)/Cos(x));
end.
При желании увеличить точность вычислений по этой программе достаточно увеличить начальное значение для d. Но при этом, очевидно, увеличится и время вычислений.
Copyright г Барков Валерий Андреевич, 2000