Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ОСНОВЫ АЛГОРИТМИЗАЦИИ.doc
Скачиваний:
188
Добавлен:
16.03.2015
Размер:
1.82 Mб
Скачать

8.4. Рекурсия

Рекурсивным называется объект, который частично определяется через самого себя. Рассмотрим функцию n!

n!:= 1 * 2 * 3 *..* n;

Такое произведение можно вычислить с помощью интерактивных циклических конструкций.

f:=l;

for i:=l to n do f := f * i;

Однако существует другое определение факториала, в котором используется рекуррентная формула. Тогда, факториал определяется следующим образом:

  1. 0! = 1;

  2. для любого n > 0 n! = n* (n-1)!;

Организация вычисления по рекуррентным формулам можно и без использования рекурсий. Однако при этом встаёт вопрос о качестве программы и доказательстве её эквивалентности формы.

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

Так, прямая формула чисел Фибоначчи:

  1. F(1)=l;

  2. F(2)=l;

  3. для любого n > 0 F(n) = F(n-l) + F(n-2);

8.4.1. Вычисление факториала

Для написания рекурсивных алгоритмов необходимо и достаточно наличие понятия процедуры и функции.

Рекурсия - это такой способ организации вычислительного процесса, при котором подпрограмма в ходе выполнения составляющих её операторов обращается сама к себе.

Рассмотрим классический пример — вычисление факториала. Программа вводит с клавиатуры целое число N и выводит на экран значение n!, которое вычисляется с помощью рекурсивной функции Fасt. Для выхода из программы необходимо либо ввести достаточно большое целое число, чтобы вызвать переполнение при умножении чисел с плавающей запятой, либо нажать Ctrl-Z и Enter.

При выполнении правильно организованной рекурсивной подпрограммы осуществляется многократный переход от некоторого текущего уровня организации алгоритма к нижнему уровню последовательно до тех пор, пока, наконец, не будет получено тривиальное решение поставленной задачи. В ниже приведённом примере решение при n=0 тривиально и используется для остановки рекурсии.

Пример 8.3.

Листинг программы

Program Factorial;

{$S+} {Включаем контроль переполнения стека}

var n : integer;

function Fасt (n : integer) :real;

{Рекурсивная функция, вычисляющая n!}

begin

if n < 0 then writeln ('Ошибка в задании N')

else

if n = 0 then Fact := 1

else Fact := n * Fact (n-1);

end;

begin

repeat

readln (n);

writeln ('n! = ', Fact (n));

until EOF;

end.

Рекурсивная форма организации алгоритма обычно выглядит изящнее итерационной и даёт более компактный текст программы, но при выполнении, как правило, медленнее и может вызвать переполнение стека (при каждом входе в программу её локальные переменные размещаются в особым образом организованной области памяти, называемой программным стеком). Переполнение стека особенно ощутимо сказывается при работе с сопроцессором: если программа использует арифметический сопроцессор, результат любой вещественной функции возвращается через аппаратный стек сопроцессора, рассчитанный всего на 8 уровней. Если, например, попытаться заменить тип real функции Fасt на Extended, программа перестанет работать уже при n = 8. Чтобы избежать переполнения стека сопроцессора, следует размещать промежуточные результаты во вспомогательной переменной. Правильный пример для работы с типом Extended:

Пример 8.4.

Листинг программы

Program Factorial; {$S+, $N+, $E+}

{Включаем контроль переполнения стека и работу сопроцессора}

var n : integer;

function Fact (n : integer) :real;

var F : extended;

{Рекурсивная функция, вычисляющая n!}

begin

if n < 0 then writeln ('Ошибка в задании N')

else

if n = 0 then Fact := 1

else

begin

F:= Fact (n-1);

Fact := F * n;

end;

end;

begin

repeat

readln (n);

writeln ('n! = ', Fact (n));

until EOF;

end.

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

Например,

Uses crt;

Procedure pop;

Begin

Writeln ('\У попа была собака...');

Pop;

End;

Begin

Pop;

End.

Но, если в теле процедуры изменить последовательность операторов:

Например,

Uses crt;

Procedure pop;

Begin

Pop;

Writeln ('У попа была собака...');

End;

Begin

Pop;

End.

Такое качество алгоритма вытекает из того, что рекурсивная процедура указывает, что нужно делать.

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