
Рекурсия является важным инструментом решения вычислительных математических задач. Она широко используется для определения синтаксиса языков программирования, а также в структурах данных при разработке алгоритмов сортировки и поиска для списков и деревьев. Математики применяют рекурсию в комбинаторике для разного рода подсчетов и вычислен] вероятностей. Рекурсия – важный раздел, имеющий теоретическое и практическое применение в общей теории алгоритмов, моделях исследования ог раций, теории игр и теории графов.
В данной главе мы даем общее введение в рекурсию и иллюстрируем применение различными приложениям. В следующих главах рекурсия будет использоваться для изучения деревьев и сортировки.
Понятие рекурсии
Для большинства людей рекурсивное мышление не является характерным Если вас, допустим, попросят определить степенную функцию хп, где х -* действительное число, a n — неотрицательное целое, то типичным ответом будет следующий:
xn=x´x´x´x´¼´x.
Функция S(n) вычисляет сумму первых n положительных целых — задача, которая решается путем многократного сложения.
S(n)=
Рекурсия имеет место, когда вы решаете какую-то задачу посредством разбиения ее на меньшие подзадачи, выполняемые с помощью одного и того же алгоритма. Процесс разбиения завершается, когда мы достигаем простейших возможных решаемых подзадач. Мы называем эти задачи условиями останова. Рекурсия действует по принципу “разделяй и властвуй”.
Алгоритм определен рекурсивно, если это определение состоит из:
Одного или нескольких условий останова, которые могут быть вычислены для определенных параметров.
Шага рекурсии, в котором текущее значение в алгоритме может быть определено в терминах предыдущего значения. В конечном итоге шаг рекурсии должен приводить к условиям останова.
Например, рекурсивное определение степенной функции имеет единственное условие останова (n=0, т.к. х0 = 1). Шаг рекурсии описывает общий случай хn = x´ хn–1 при n>0.
Рекурсивные задачи
Сила рекурсии обеспечивает весьма простые и изящные решения ряда проблем. Приведем обзор задач и методов их решения, использующих рекурсию.
Ханойская башня — тяжелое испытание для решателя головоломок. Между тем опытный программист видит быстрое рекурсивное решение. Начнем, сосредоточившись на перемещении верхних n–1 дисков на шпиль B и последующего перемещения самого большого диска на шпиль C.
Осталась более простая задача перемещения только n–1 дисков со шпиля A на шпиль С. Применяя тот же самый алгоритм, сосредоточим внимание на верхних n–2 дисках и вытащим их из стопки. Затем перенесем самый большой диск со шпиля В на шпиль С. Осталась еще меньшая стопка дисков. Процесс продолжается до тех пор, пока не останется 1 диск, который, в конечном счете, перемещается на шпиль С.
Построение рекурсивных функций
Структура рекурсивной функции иллюстрируется задачей вычисления факториала неотрицательных целых чисел. Мы рассмотрим эту структуру, разработав как рекурсивное, так и итерационное определение функции.
Факториал неотрицательных целых чисел, Factorial(n), определяется как произведение всех положительных целых чисел, меньших или равных n. Число, обозначаемое n!, представляется следующим образом:
n!=n´(n–1)´(n–2)´¼´2´1.
Итерационная версия этой функции реализуется посредством возврата 1, если n=0, и циклом перемножения членов последовательности в противном случае
// итерационная форма факториала
long Factorial{long n)
{
int prod=1, i;
// для n==0 вернуть prod=i, в противном случае
// вычислить prod=1*2* *n
if {n>0)
for(i=1; i<=n; i++)
prod*=i;
return prod;
}
При вычислении n! нужно четко различать случай 0!, представляющий условие останова, и другие случаи (n>0), представляющие шаги рекурсии. Это различие является фундаментальным для построения рекурсивного алгоритма. Программист реализует распознавание данной ситуации с помощью оператора IF ... ELSE. Блок IF обрабатывает условие останова, а блок ELSE выполняет шаг рекурсии. Для факториала блок IF вычисляет единственное условие останова n=0 и возвращает единицу. Блок ELSE выполняет шаг рекурсии, вычисляя выражение n*(n–1)1, и возвращает результат.
// Рекурсивная форма факториала
long Factorial (long n)
{
// условием останова является n==0
if{n==0)
return 1;
else
// шаг рекурсии
return n*Factorial(n-1);
}
На рис. 2 описана последовательность вызовов функции при вычислении Factorial(4). Предположим, что первоначально функция вызывается из главной программы. Внутри блока функции выполняется оператор ELSE с параметрами 3, 2, 1 и 0. На последнем вызове выполняется оператор IF с n = 0. По достижении условия останова рекурсивная цепочка вызовов прерывается и начинается серия вычислений в порядке 1*1, 2*1, 3*2 и 4*6. Последнее значение 24 возвращается в главную программу.