- •Лекция 9. Рекурсия Введение
- •Рекурсивные описания алгоритмов.
- •Реализация рекурсии
- •Примеры рекурсивных и нерекурсивных описаний
- •Var I: Integer; y: Real;
- •Var X:Integer;
- •Var s: Sequence;
- •Var I: Integer;
- •Var j : integer; Buf: Char;
- •Var s : Sequence;
- •Var j : integer;
- •Техника рекурсивных описаний
- •Var I, j, y, z : Integer;
- •Var a, X : Vector; b : Integer; I : Integer;
- •Преимущества и недостатки рекурсивных алгоритмов
Var s : Sequence;
Buf : char; i : Integer;
Procedure WriteSequence;
begin
For i := 1 to n do Write(S[i]); Writeln
end;
Procedure P(k : Integer);
Var j : integer;
Procedure Swap(i, j : Integer);
begin
Buf := S[j]; S[j] := S[i]; S[i] := Buf
end;
Begin
if k > 1 then P(k - 1);
For j := k - 1 downto 1 do begin
Swap(j, k); {Прямая перестановка}
WriteSequence; P(k - 1);
Swap(k, j) {Обратная перестановка}
end
End;
Begin
{Генерация исходного набора S}
WriteSequence;
P(n)
End.
Важнейшую роль в реализации рекурсивных алгоритмов играет контроль выхода из рекурсии (ее окончания).
Если не предусмотреть выхода из рекурсии, цепочка рекурсивных вызовов станет (теоретически) бесконечной. На практике это приведет к переполнению той части памяти компьютера, в которой рекурсия реализуется.
Рекурсивный алгоритм всегда имеет так или иначе реализованное условное ветвление, одна или несколько ветвей которого - нерекурсивные действия, а другая (другие) - рекурсивный вызов. Логическое значение условия, управляющего этим ветвлением, зависит от параметров рекурсии.
При определенных значениях параметров алгоритм не вызывает новую рекурсивную копию, а выполняет нерекурсивные действия, тем самым обрывая цепочку рекурсивных вызовов. Эти нерекурсивные действия будем называть базисом рекурсии. Вторую ветвь естественно назвать шагом рекурсии, а условие - условием рекурсии.
В примере 2 это ветвление имеет вид
If n = 1 {условие рекурсии}
then F := 1 {базис рекурсии}
else F := F(n - 1) * n {шаг рекурсии}
В примере 3 это выглядит так:
If x <> y { условие рекурсии }
Then If x < y { шаг рекурсии }
then GCD:= GCD(x, y - x)
else GCD := GCD(x - y, y)
Else GCD := x { базис рекурсии }
В примере 3 ветвление реализовано оператором Case.
В примере 4 выбор управляется условием k > 1, а ветвление реализовано с помощью арифметического цикла, который не исполнится, если k <= 1. Базис рекурсии вырожден: при выходе из рекурсии никакие действия не выполняются.
Важную роль в понимании процесса исполнения рекурсивного алгоритма играет представление о том, как и в каком порядке осуществляются рекурсивные вызовы. Первоначальное представление об этом механизме мы получили, рассмотрев рекурсивный алгоритм вычисления N! Полная картина рекурсивных вызовов представляется в виде цепочки рекурсивных копий длины N.
Аналогично будет выглядеть полная картинка рекурсивных вызовов в примере 2. Нарисуем ее при исходных значениях параметров x = 26, y = 16. { НОД(26, 16) }
Линейность картинки рекурсивных вызовов обусловлена тем, что в соответствующей рекурсивной функции возможен только один рекурсивный вызов.
Если в рекурсивной функции (процедуре) возможны два и более вызовов, осуществляемых последовательно, полная картинка рекурсии будет ветвящейся.
Пример 6. Последовательность Фибоначчи. Требуется построить алгоритм вычисления n-того числа последовательности F(n), определенной рекурсивными соотношениями:
F(0) = F(1) = 1; F(n+2) = F(n+1) + F(n).
Переведя определение F(n) на Паскаль, получим:
Function F(n: Integer) : Integer;
Begin
Case n of
0,1: F := 1
else F:=F(n-1)+F(n-2) {Два последовательных вызова}
end
End;
Тело функции F содержит два рекурсивных вызова, выполняемых последовательно. Поэтому полная картинка рекурсии имеет вид дерева! Построим ее при n = 5.
Каждая рекурсивная копия, если ее параметр k >= 2, вызывает две новых копии. Полная картинки вызовов имеет вид дерева, из каждого ветвления которого выходят две ветви. (с параметрами k-1, k-2).
В каждый момент времени в памяти компьютера хранится только один путь - тот, который соответствует состоянию процесса вычислений.
В памяти компьютера, отведенной под рекурсивные вычисления, хранятся те копии, путь через которые лежит к исполняемой.
Время исполнения этого алгоритма пропорционально общему числу рекурсивных копий,
Максимальная память, требуемая для хранения данных, пропорциональна длине максимального пути - от первой копии до копии нижнего уровня.
Древовидная картинка наглядно показывает схему рекурсивных вычислений. На ней видно, что необходимыми являются только вычисления, стоящие в самом левом пути. Все остальные вычисления - только повторения уже проделанных.