Чтобы рекурсия не зациклилась, внутри рекурсивной процедуры/функции ОБЯЗАТЕЛЬНОдолжен присутствовать оператор IF. Общая схема построения рекурсивных программ такова:
P = IF B THEN P[S,P] END; или P = P[S, IF B THEN P END]
Пусть n – параметр рекурсии. Тогда:
P(n) = IF n>0 THEN P[S,P(n-1)] END или P(n) = P[S, IF n>0 THEN P(n-1) END]
Если есть возможность заменить рекурсию циклом – заменяйте! Например, факториал можно спокойно вычислить в обычном цикле без рекурсии:
FUNCTION fact(n:WORD):LONGINT; VAR I:WORD; BEGIN fact := 1; FOR I := 1 TO n DO fact := fact * n END;
Такой алгоритм снимает ограничения по памяти (экспериментальная проверка показала, что при рекурсии n£608, затем возникает ошибка переполнения памяти).
В то же время в ряде случаев очень легко написать рекурсивную программу, а превратить ее в обычную не так-то просто. Яркий пример рекурсивного "по своей природе" алгоритма – вычисление чисел Фибоначчи. Числа Фибоначчи определяются как:
fn+1= fn + fn-1 при n>0; f1=1; f0=0
( 12.1)
Проще говоря, каждое число Фибоначчи является суммой двух предыдущих. Последовательность имеет вид 1, 1, 2, 3, 5, 8, 13, 21, 34, 55… Числа Фибоначчи играют важную роль в технике и даже в биологии (в соответствии с законом Фибоначчи растет численность живых организмов).
Рекурсивно вычислять числа Фибоначчи очень просто:
FUNCTION Fib(n:WORD):WORD; BEGIN IF n=0 THEN Fib := 0 ELSE IF n=1 THEN Fib := 1 ELSE Fib := Fib(n-1) + Fib(n-2) END;
Алгоритм получается простым и очевидным. Увы, он сильно расходует память, и много чисел Фибоначчи так не вычислишь. Попробуем сделать нерекурсивную реализацию:
FUNCTION Fib(n:WORD):WORD;
VAR I,x,y:WORD;
BEGIN I :=1; x := 1; y := 0; WHILE (I<n) DO BEGIN x := x+y; y := x-y; INC(I) END; Fib := x END;
Алгоритм заметно усложнился, пришлось ввести дополнительные переменные x и y для хранения двух предыдущих чисел Фибоначчи. Зато снялись ограничения по памяти.
Рекурсия широко используется в алгоритмах сортировки. Самый лучший на сегодняшний день алгоритм, который так и называется QuickSort(быстрая сортировка), является рекурсивным и был предложен С. Хоаром (C. Hoare) в 1970. Лежащая в основе QuickSort идея проста: для большей скорости работы лучше сначала производить перестановки элементов на большие расстояния. Алгоритм можно описать следующим образом:
1. Берем любой элемент массива х 2. Ищем в массиве слева элемент ai>x 3. Ищем в массиве справа элемент aj<x 4. Меняем местами ai и aj. 5. Повторить с п. 1
Рассмотрим пример выполнения быстрой сортировки (Рис. 12.2).
Рис. 16.2. Выполнение быстрой сортировки.
Возьмем произвольный элемент массива x=42. Будем искать в массиве слева первый элемент, больший 42. Он равен 44. Теперь ищем в массиве справа первый элемент, меньший 42. Он равен 18. Меняем 44 и 18 местами. Повторяем процедуру, теперь меняются 6 и 55. Массив постепенно сортируется.
Рекурсивная реализация QickSort довольно сложна:
PROCEDURE QuickSort; PROCEDURE Sort(L,R:WORD); VAR I,j:WORD; w,x: INTEGER; BEGIN I := L; j : =R; X: = a[(L+R) DIV 2]; REPEAT WHILE (A[I]<X) DO INC(I); WHILE (X<A[J]) DO DEC(J); IF I<=J THEN BEGIN W:=A[I]; A[I] := A[J]; a{J}:=W; INC(I); DEC(J) END UNTIL I>J; IF L<J THEN Sort(L,J); IF (I<R) THEN Sort(I,R) END; BEGIN
sort(1,n)
END;
Несмотря на сложность, это самый быстрый из имеющихся алгоритм сортировки массивов.