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

Задачник-практикум по основам программирования. Учебное пособие по курсу «Информатика»

.pdf
Скачиваний:
0
Добавлен:
12.08.2026
Размер:
1 Мб
Скачать

При выполнении тела процедуры сформируются следующие два оператора:

SumRec( 4, Sm ); Sm:=Sm+1/5;

Новый оператор процедуры SumRec(4, Sm)заставляет снова обра- титься к выполнению этой же процедуры, что приводит к появлению новой пары операторов:

SumRec( 3, Sm ); Sm:=Sm+1/4; Sm:=Sm+1/5;

После выполнения еще двух обращений ситуация окажется следующей:

SumRec( 1, Sm ); Sm:=Sm+1/2; Sm:=Sm+1/3; Sm:=Sm+1/4; Sm:=Sm+1/5;

ЗатемприочередномвыполненииоператорапроцедурыSumRec(1, Sm) выполнится оператор присваивания Sm:=1 и рекурсивные обращения прекратятся. В результате сформируется следующая последовательность операторов:

Sm:=1;

Sm:=Sm+1/2;

Sm:=Sm+1/3;

Sm:=Sm+1/4;

Sm:=Sm+1/5.

Эта последовательность операторов и дает результат вычисления суммы: сумма = 2.28333333

Рекурсивную процедуру SumRec следует рассматривать как иллюс- трацию аппарата рекурсии. Итеративная (нерекурсивная) процедура для нахождения суммы гораздо проще, эффективнее и естественнее:

procedure Sum( n: integer; var S: real); var i: integer;

begin S=1;

for i:=2 to n do S:=S+1/n

end;

141

Пример 8.2. Описать рекурсивную функцию для возведения числа в целую положительную степень:

1,

 

если n = 0

xn =

xn1,

если n > 0

x

Используя эту функцию, вычислить 23. program Rec_2_1;

function Pow( x:real; n:integer): real; begin

if n=0 then Pow:=1

else Pow:=x*Pow(x,n-1)

end; begin

writeln(‘Pow(2,3)=’,Pow(2,3))

end.

Результат работы программы Rec_2_1: Pow (2, 3) = 8

Можно исследовать, как выполнятся рекурсивная функция: сколько прямых и обратных шагов выполняется и при каких значениях параметров. При изменении функции в список параметров процедуры Pow добавляется параметр i номер вызова функции. Значения параметров будут выводиться в начале очередного выполнения тела функции (прямой ход) и перед вы- ходом из него (обратный ход).

Программа с измененной функцией: program Rec_2_2;

var i: integer;

function Pow1(var i: integer; x: real; n: integer):

real;

 

 

begin

 

 

i:=i+1;

i=’,i,’

x=’,x,’

writeln(‘прямой ход’,’

n=’,n);

 

 

if n=0 then begin

writeln(‘

--------------------------------’);

writeln(‘завершение

рекурсивных вызовов’,

i=’,i,’

n=’,n);

142

writeln(‘ --------------------------------’); Pow1:=1

end else

Pow1:=x*Pow1(i,x,n-1);

writeln(‘обратный ход’,’ i=’,i,’ n=’,n); i:=i-1 end;

begin

i:=0; writeln(‘Pow1(i,2,3)=’,Pow1(i,2,3)) end.

Результат работы программы Rec_2_2:

прямой ход

i = 1

x = 2

n = 3

прямой ход

i = 2

x = 2

n = 2

прямой ход

i = 3

x = 2

n = 1

прямой ход

i = 4

x = 2

n = 0

–––––––––––––––––––––––––––––––––––––––––––

завершение рекурсивных вызовов i = 4 n = 0

–––––––––––––––––––––––––––––––––––––––––––

обратный ход

i = 4

n = 0

обратный ход

i = 3

n = 1

обратный ход

i = 2

n = 2

обратный ход

i = 1

n = 3

Pow1(i, 2, 3) = 8

Упражнения

1)Проверить, как будет вести себя программа, если использовать вызов функции Pow1(0,2,3).

2)Какое исправление нужно внести в описание функции, чтобы вызов Pow1(0,2,3) не давал сообщений об ошибке?

3)Поставить в функции Pow1 строку выдачи

writeln(‘прямой ход’,’ i=’,i,’ x=’,x,’ n=’,n);

непосредственно перед рекурсивным вызовом и проанализировать изменения в выводе.

4)Поставить в функции Pow1 строку выдачи

writeln(‘прямой ход’,’ i=’,i,’ x=’,x,’ n=’,n);

после рекурсивного вызова и проанализировать изменения в выводе. 5) Поставить в функции Pow1 строку выдачи

143

writeln(‘обратный ход’,’ i=’,i,’ n=’,n); i:=i-1

непосредственно перед рекурсивным вызовом и проанализировать изменения в выводе.

6)Описать рекурсивную функцию Pow(x,n) от вещественного x (x 0) и целого n, которая вычисляет величину xn по формуле

1

xn = 1/ x n

x xn1

при

n = 0

при

n < 0

при

n > 0

Пример 8.3. Для решения задачи, рассмотренной в Примере 8.2., можно использовать более эффективное рекурсивное определение xn, ох- ватывающее случай n < 0:

 

 

1,

 

 

 

 

 

 

 

если

n = 0,

 

 

 

n

 

 

n / 2

)

2

,

если

n > 0,

n

−четное,

x

(x

 

 

 

 

 

=

xn1,

если

n > 0,

n

−нечетное,

 

 

x

 

 

 

x

n

,

 

 

если

n < 0.

 

 

 

 

1

 

 

 

 

 

function Power(x: real; n: integer): real; begin

if n=0 then Result:=1 else

if n<0 then Result:=Power(x,-n) else

if odd(n) then Result:=x*Power(x,n-1) else Result:=sqr(Power(x,n div 2))

end;

Недостаток этой функции состоит в том, что переменная x не меняется, но передается в каждый рекурсивный вызов.

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

Для этого следует вложить в нерекурсивную функцию Power рекур- сивную функцию Power0(n):

function Power(x: real; n: integer): real; function Power0(n: integer): real;

begin

144

if n=0 then Result:=1 else if n<0 then

Result:=Power(x,-n) else if odd(n) then

Result:=x*Power0(n-1) else

Result:=sqr(Power0(n div 2)) end;

begin Result:=Power0(n)

end;

Пример 8.4. Описать и использовать функцию вычисления числа Фибоначчи с номером n.

Как известно (см. Пример 3.4.), определение чисел Фибоначчи ре- курсивно [17]:

fn

1, n = 0, 1

=

+ fn2 , n > 1.

 

fn1

program Rec_4;

var n, f: integer;

function Fib(n: integer): integer; begin

if (n=0) or (n=1) then Fib:=1 else Fib:= Fib(n-1)+ Fib(n-2)

end; begin

write(‘Введите целое число ‘); readln(n);

f:= Fib(n);

writeln(n, ‘-е число Фибоначчи равно ‘, f) end.

Однако такое решение крайне неэффективно, поскольку содержит большое количество повторяющихся вычислений.

Например, дерево рекурсивных вызовов для вызова Fib (7) будет иметь следующий вид:

145

Из рисунка видно, что, Fib(5) вычисляется дважды, Fib(4) триж- ды, Fib(3) – 5 раз и т. д., т. е. количество повторных вызовов представляет собой также последовательность чисел Фибоначчи!

Пример 8.5. Выдать цифры заданного натурального числа в порядке их следования (слева направо).

program Rec_5; var n: integer;

procedure Digit_rec(n: integer); begin

if n <> 0 then begin

Digit_rec(n div 10); write(n mod 10,’ ‘)

end end; begin

writeln(‘Введите натуральное число’); readln(n);

Digit_rec(n); writeln

end.

Упражнения

1)Изменить процедуру Digit_rec для выдачи цифр заданного на- турального числа в обратном порядке (справа налево).

2)Выдать все нечетные цифры заданного натурального числа в по- рядке их следования (слева направо).

146

Пример 8.6. Вводится с клавиатуры последовательность ненулевых целых чисел, за которыми следует 0. Используя рекурсивную процедуру без параметров, выдать сначала все отрицательные числа, затем все поло- жительные.

program Rec_6; procedure Print; var x: integer; begin

read(x);

if x<>0 then begin

if x<0 then write(x,’ ‘); Print;

if x>0 then write(x,’ ‘) end

end; begin

writeln(‘Введите последовательность ненулевых ‘, ‘целых чисел, за которыми следует 0’);

Print; writeln

end.

Пример 8.7. Последовательность положительных вещественных чи- сел, за которыми следует 0, вводится с клавиатуры. Используя рекурсивную функцию без параметров, найти их сумму.

program Rec_7; function Sum: real; var x: real;

begin read(x);

if x=0 then Sum:=0 else Sum:=x+Sum

end; begin

writeln(‘Введите последовательность положительных ‘, ‘чисел, за которыми следует 0’);

147

writeln(Sum);

end.

Пример 8.8. Вводится с клавиатуры без ошибок формула следующего вида:

<формула> ::= <цифра> | (< формула > <знак> < формула >) <знак> ::= + | – | * <цифра> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Используя рекурсивную функцию без параметров, вычислить значение вводимой формулы.

program Rec_8;

function Formula: integer;

var c, op: char; { op – знак операции } x, y: integer;

begin read(c);

if (c>=’0’) and (c<=’9’) then {цифра - формула} Formula:=ord(c)-ord(‘0’)

else { начало формулы вида (x op y) } begin

x:=Formula;

read(op);

y:=Formula; case op of

‘+’: Formula:=x+y; ‘-’: Formula:=x-y; ‘*’: Formula:=x*y;

end;

read(c) {пропуск скобки ‘)’} end

end; begin

writeln(‘Введите формулу’); writeln(‘Значение = ‘,Formula)

end.

148

Контрольные вопросы

 

1)

5

Значение = 5

2)

(( 2 – 4 ) * 6 )

Значение = –12

3)

((( 2 + 4 )*3) – 5 )

Значение = 13

Пример 8.9. Вычислить квадратный корень y = x из заданного положительного числа x.

Для вычисления приближенного значения y можно использовать рекуррентную формулу [17]:

yn

= yn1 +

1

(

x

yn1 ), n = 1, 2, ...

2

 

 

 

 

yn1

y0 произвольное положительное число (например, 1, x, x / 2 ). Вычисления можно прекратить при выполнении условия

|yn – yn- 1|< eps/2.

Рекуррентную формулу и условие окончания процесса вычислений можно преобразовать:

yn =

1

(

x

+ yn1 ),

2

 

 

 

 

yn1

 

x

 

yn1

 

< eps .

 

 

 

 

 

 

 

yn1

 

 

 

 

 

program Rec_9;

var x, y, eps: real;

function Sq(x,y,eps: real):real; begin

if abs(x/y-y)<eps then Sq:=y

else

Sq:=Sq(x,(x/y+y)/2, eps)

end; begin

writeln(‘Введите x и eps’); readln(x,eps); y:=Sq(x,x/2,eps);

writeln(‘Квадратный корень=’,y:8:4) end.

149

Пример 8.10. Описать рекурсивную функцию, которая n раз вызывает заданную функцию f (x):

fn ( n, x )=f ( f (…f (x) ) )

Для сравнения описать нерекурсивную функцию, n раз вызывающую заданную функцию f(x).

Используя рекурсивную и нерекурсивную функции, вычислить зна- чение ((x2)2)2.

program Rec_10;

type func = function (x:real):real; var x:real;

function f(x:real):real; begin

f:=x*x

end;

function Fn(n:integer;x:real; f:func):real; begin

if n>1 then Fn:=f(Fn(n-1,x,f))

else Fn:=f(x) end;

function Fn1(n:integer;x:real; f:func):real; var i:integer;

begin

for i:=1 to n do x:=f(x);

Fn1:=x

end; begin

write(‘Введите x ‘); readln(x);

writeln(‘ Обращение к рекурсивной функции:’); writeln(Fn(3,x,f));

writeln(‘ Обращение к нерекурсивной функции:’); writeln(Fn1(3,x,f))

end.

150

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]