Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
29
Добавлен:
31.03.2015
Размер:
363.01 Кб
Скачать

36

1. Рекурсивные алгоритмы

1.1. Основные определения

Объект называется рекурсивным, если он содержит сам себя или определен с помощью самого себя. Например,

1. Натуральные числа:

(а) 1 есть натуральное число

(б) целое число, следующее за натуральным, есть натуральное число.

2. Древовидные структуры:

(а) есть дерево (называемое пустым деревом),

(б) если t1 и t2 деревья, то t1 t2 есть дерево.

Рекурсия является мощным средством в математических определениях. Мощ-

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

Рекурсия в программировании используется как для записи алгоритма («cодержит сам себя»), так и для определения типа данных («определен с помощью самого себя»).

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

P

Прямой называется рекурсия, если процедура P содержит явное обращение к самой себе:

P

Q

Косвенной называется рекурсия, если процедура P содержит обращение к процедуре Q, которая содержит обращение к процедуре P:

В общем виде рекурсивную процедуру P можно изобразить как композицию К базовых структур Si (не содержащих P) и самой P:

P = К [Si , if B then P]. (1)

Чтобы работа процедуры P завершалась, необходимо, чтобы рекурсивное обращение к процедуре P подчинялось некоторому условию В.

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

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

Рассмотрим простейшую задачу накопления произведения чисел.

Спецификация задачи.

1. Задача. Найти произведение натуральных чисел от 1 до n:

P = n! = 12...n

2. Входные данные.

цел n - граница накопления, n  0

3. Выходные данные.

цел P - произведение

4. Аномалии.

n < 0 (в программе не рассматриваются)

5. Метод.

Возможны два способа математической записи произведения:

1) P = 1  2 ... n = ;

2a) 0! = 1,

2б) если n > 0, то n! = n  (n-1)!

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

Способ 2 с помощью конечного высказывания задает строгий метод вычисления произведения любого количества чисел с использованием рекурсии.

Опишем способ 2 в виде рекурсивной процедуры и рекурсивной функции.

Наиболее надежный способ обеспечить окончание процедуры - это связать с процедурой Р параметр - значение n и рекурсивно вызывать Р со значением этого параметра n-1. Тогда замена условия В из формулы (1) на n>0 гарантирует окончание работы: P(n) = K [Si , if n>0 then P(n-1)]

{ вычисление P=n! } { вычисление P=n! }

procedure fact (n:integer; var P:integer); function P (n:integer):integer;

begin begin

if n > 0 then if n > 0 then

begin P := n * P(n-1)

fact(n-1,P); else

P:=P*n P := 1

end end;

else

P:=1

end;

На рис.1 отображен рекурсивный процесс выполнения процедуры fact(n,P)

для n=3.

Рекурсивный процесс включает прямой и обратный ход.

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

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

Обратный ход. Начинается с вычислений не рекурсивной части (P=1 в ветви else), после чего процедура удаляется и становится доступной предыдущая копия процедуры. Выполняется оператор, следующий за оператором рекурсивного вызова (P=P*1) , выход из этой копии процедуры и удаление этой копии и т.д.

Рекурсивные процедуры требуют значительно больше машинных ресурсов, чем обычные процедуры и труднее отлаживаются. Но для многих задач рекурсивные методы более естественны и дают простую и наглядную запись алгоритма (например, алгоритм быстрой сортировки). Существуют также задачи, которые могут быть решены только рекурсивными методами.

Прямой ход: глубина рекурсии равна 4.

n=3 P=6 Создаются в памяти 4 копии про-

цедуры по принципу стека:

fact(3,P) if 3>0 then n=2 P=2 последняя - сверху.

begin

fact(2,P) if 2>0 then n=1 P=1

P:=P*3 begin

end fact(1,P) if 1>0 then n=0 P=1

. . . P:=P*2 begin

end fact (0,P) if 0>0 then

. . . P:=P*1 begin

Обратный ход. end . . .

Производятся вычисле- . . . end

ния, начиная с последней копии. else

При завершении процедуры освобождается P:=1

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

точку ее вызова предыдущей копии.

Рис.1.

Соседние файлы в папке METOD2