Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
nestudent.ru_46905.doc
Скачиваний:
22
Добавлен:
12.09.2019
Размер:
2.07 Mб
Скачать

Косвенная рекурсия

Процедура также может вызывать другую процедуру, которая в свою очередь вызывает первую. Такие процедуры иногда даже сложнее анализировать, чем процедуры с множественной рекурсией. Алгоритм вычисления кривой Серпинского, который обсуждается в 5 главе, включает в себя четыре процедуры, которые используют как множественную, так и непрямую рекурсию. Каждая из этих процедур вызывает себя и другие три процедуры до четырех раз. После довольно сложных подсчетов можно показать, что этот алгоритм имеет сложность порядка O(4N).

Требования рекурсивных алгоритмов к объему памяти

Для некоторых рекурсивных алгоритмов важен объем доступной памяти. Можно легко написать рекурсивный алгоритм, который будет запрашивать

============7

небольшой объем памяти при каждом своем вызове. Объем занятой памяти может увеличиваться в процессе последовательных рекурсивных вызовов.

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

Приведенная ниже подпрограмма запрашивает память при каждом вызове. После 100 или 200 рекурсивных вызовов, процедура займет всю свободную память, и программа аварийно остановится с ошибкой «Out of Memory».

Sub GobbleMemory(N As Integer)

Dim Array() As Integer

ReDim Array (1 To 32000)

GobbleMemory N + 1

End Sub

Даже если внутри процедуры память не запрашивается, система выделяет память из системного стека (system stack) для сохранения параметров при каждом вызове процедуры. После возврата из процедуры память из стека освобождается для дальнейшего использования.

Если в подпрограмме встречается длинная последовательность рекурсивных вызовов, программа может исчерпать стек, даже если выделенная программе память еще не вся использована. Если запустить на исполнение следующую подпрограмму, она быстро исчерпает всю свободную стековую память и программа аварийно прекратит работу с сообщением об ошибке «Out of stack Space». После этого вы сможете узнать значение переменной Count, чтобы узнать, сколько раз подпрограмма вызывала себя перед тем, как исчерпать стек.

Sub UseStack()

Static Count As Integer

Count = Count + 1

UseStack

End Sub

Определение локальных переменных внутри подпрограммы также может занимать память из стека. Если изменить подпрограмму UseStack из предыдущего примера так, чтобы она определяла три переменных при каждом вызове, программа исчерпает стековое пространство еще быстрее:

Sub UseStack()

Static Count As Integer

Dim I As Variant

Dim J As Variant

Dim K As Variant

Count = Count + 1

UseStack

End Sub

В 5 главе рекурсивные алгоритмы обсуждаются более подробно.

==============8

Наихудший и усредненный случай

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

Function LocateItem(target As Integer) As Integer

For I = 1 To N

If Value(I) = target Then Exit For

Next I

LocateItem = I

End Sub

Если искомый элемент находится в конце списка, придется перебрать все N элементов для того, чтобы его найти. Это займет N шагов, значит сложность алгоритма порядка O(N). В этом, так называемом наихудшем случае (worst case) время выполнения алгоритма будет наибольшим.

С другой стороны, если искомое число в начале списка, алгоритм завершит работу практически сразу, совершив всего несколько итераций. Это так называемый наилучший случай (best case) со сложностью порядка O(1). Обычно и наилучший, и наихудший случаи встречаются относительно редко, и интерес представляет оценка усредненного или ожидаемого (expected case) поведения.

Если первоначально числа в списке распределены случайно, искомый элемент может оказаться в любом месте списка. В среднем потребуется проверить N/2 элементов для того, чтобы его найти. Значит, сложность этого алгоритма в усредненном случае порядка O(N/2), или O(N), если убрать постоянный множитель.

Для некоторых алгоритмов порядок сложности для наихудшего и наилучшего вариантов различается. Например, сложность алгоритма быстрой сортировки из 9 главы в наихудшем случае порядка O(N2), но в среднем его сложность порядка O(N*log(N)), что намного быстрее. Иногда алгоритмы типа быстрой сортировки бывают очень длинными, чтобы наихудший случай достигался крайне редко.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]