Рекурсивный алгоритм, вызывающий себя несколько раз, называется много-
кратной рекурсией (multiple recursion). Процедуры множественной рекурсии слож-
нее анализировать, чем однократные алгоритмы, кроме того, они могут сделать ал-
горитм гораздо сложнее.
Следующая процедура аналогична процедуре Count Down, только она вызы-
вает саму себя дважды.
procedure DoubleCountDown(N : Integer) ;
begin
if (N<=0) then exit;
DoubleCountDown(N-l) ;
DoubleCountDown(N-l) ;
end;
Поскольку процедура вызывается дважды, можно было бы предположить, что
ее рабочий цикл будет вдвое больше, чем цикл процедуры CountDown. При этом
сложность была бы равна 2 * O(N) = O(N). В действительности ситуация гораздо
сложнее.
Если количество итераций процедуры при входном значении N равно T(N), то
легко заметить, что Т(0) равно 1. Если процедура вызывается с параметром 0, то
программа просто закончит свою работу с первого шага.
Для больших значений N процедура запускается дважды с параметром N - 1.
Количество ее итераций при этом равно 1 + 2 * T(N - 1). В табл. 1.1 приведены
некоторые значения сложности алгоритма в соответствии с уравнениями Т(0) - 1
и T(N) = 1 + 2 * T(N - 1). При внимательном рассмотрении этих значений можно
заметить, что если T(N) = 2^(N+1) - 1, то рабочий цикл процедуры будет равен O(2^N).
Несмотря на то, что процедуры CountDown и DoubleCountDown выглядят почти
одинаково, DoubleCountDown выполняется гораздо дольше.
Таблица 1.1. Значения длительности рабочего цикла
для процедуры DoubleCountDown
Косвенная рекурсия
Рекурсивная процедура может выполняться косвенно, вызывая вторую про-
цедуру, которая, в свою очередь, вызывает первую. Косвенную рекурсию даже
сложнее анализировать, чем многократную. Алгоритм кривых Серпинского, рас-
сматриваемый в главе 5, включает в себя четыре процедуры, которые являются
одновременно и многократной и косвенной рекурсией. Каждая из этих процедур
вызывает себя и три другие процедуры до четырех раз. Такой значительный объем
работы выполняется в течение времени O(4^N).
Объемная сложность рекурсивных алгоритмов
Для некоторых рекурсивных алгоритмов особенно важна объемная сложность.
Очень просто написать рекурсивный алгоритм, который запрашивает небольшой
объем памяти при каждом вызове. Объем занятой памяти может увеличиваться
в процессе последовательных рекурсивных вызовов. По этой причине необходимо
провести хотя бы поверхностный анализ объемной сложности рекурсивных про-
цедур, чтобы убедиться, что программа не исчерпает при выполнении все доступ-
ные ресурсы.
Следующая процедура выделяет больше памяти при каждом вызове. После 100
или 200 рекурсивных обращений процедура займет всю свободную память компь-
ютера, и программа аварийно остановится, выдав сообщение об ошибке Out of
Memory (Недостаточно памяти).
procedure GobbleMemory;
var
x : Pointer;
begin
GetMem(х,100000); // Выделяет 100000 байт.
GobbleMemory;
end;
|