Мегаобучалка Главная | О нас | Обратная связь


Устранение рекурсии в общем случае



2019-07-03 246 Обсуждений (0)
Устранение рекурсии в общем случае 0.00 из 5.00 0 оценок




Функции факториала, наибольшего общего делителя, и BigAdd можно упростить устранением хвостовой рекурсии. Функцию, вычисляющую числа Фибоначчи, можно упростить, используя таблицу значений или переформулировав задачу с использованием подхода снизу вверх.

Некоторые рекурсивные алгоритмы настолько сложны, то применение этих методов затруднено или невозможно. Достаточно сложно было бы написать нерекурсивный алгоритм для построения кривых Гильберта или Серпинского с нуля. Другие рекурсивные алгоритмы более просты.

Ранее было показано, что алгоритм, который рисует кривые Гильберта или Серпинского, должен включать порядка O(N4) шагов, так что исходные рекурсивные версии достаточно хороши. Они достигают почти максимальной возможной производительности при приемлемой глубине рекурсии.

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

Основной подход при этом заключается в том, чтобы рассмотреть порядок выполнения рекурсии на компьютере и затем попытаться сымитировать шаги, выполняемые компьютером. Затем новый алгоритм будет сам осуществлять «рекурсию» вместо того, чтобы всю работу выполнял компьютер.

Поскольку новый алгоритм выполняет практически те же шаги, что и компьютер, можно поинтересоваться, возрастет ли скорость вычислений. В Visual Basic это обычно не выполняется. Компьютер может выполнять задачи, которые требуются при рекурсии, быстрее, чем вы можете их имитировать. Тем не менее, оперирование этими деталями самостоятельно обеспечивает лучший контроль над выделением памяти под локальные переменные, и позволяет избежать глубокого уровня вложенности рекурсии.

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

Рассмотрим следующую обобщенную рекурсивную процедуру:

 

Sub Subr(num)

<1 блок кода>

Subr(<параметры>)

<2 блок кода>

End Sub

 

Поскольку после рекурсивного шага есть еще операторы, вы не можете использовать устранение хвостовой рекурсии для этого алгоритма.

 

=====105

 

Вначале пометим первые строки в 1 и 2 блоках кода. Затем эти метки будут использоваться для определения места, с которого требуется продолжить выполнение при возврате из «рекурсии». Эти метки используются только для того, чтобы помочь вам понять, что делает алгоритм — они не являются частью кода Visual Basic. В этом примере метки будут выглядеть так:

 

Sub Subr(num)

1  <1 блок кода>

   Subr(<параметры>)

2  <2 блок кода>

End Sub

 

Используем специальную метку «0» для обозначения конца «рекурсии». Теперь можно переписать процедуру без использования рекурсии, например, так:

 

Sub Subr(num)

Dim pc As Integer ' Определяет, где нужно продолжить рекурсию.

 

pc = 1 ' Начать сначала.

Do

   Select Case pc

      Case 1

          <1 блок кода>

          If (достигнуто условие остановки) Then

              ' Пропустить рекурсию и перейти к блоку 2.

              pc = 2

          Else

              ' Сохранить переменные, нужные после рекурсии.

              ' Сохранить pc = 2. Точка, с которой продолжится

              ' выполнение после возврата из "рекурсии".

              ' Установить переменные, нужные для рекурсии.

              ' Например, num = num - 1.

                  :

              ' Перейти к блоку 1 для начала рекурсии.

              pc = 1

          End If

      Case 2 ' Выполнить 2 блок кода

          <2 блок кода>

          pc = 0

      Case 0

          If (это последняя рекурсия) Then Exit Do

          ' Иначе восстановить pc и другие переменные,

          ' сохраненные перед рекурсией.

   End Select

Loop

End Sub

 

 

======106

 

Переменная pc, которая соответствует счетчику программы, сообщает процедуре, какой шаг она должна выполнить следующим. Например, при pc = 1, процедура должна выполнить 1 блок кода.

Когда процедура достигает условия остановки, она не выполняет рекурсию. Вместо этого, она присваивает pc значение 2, и продолжает выполнение 2 блока кода.

Если процедура не достигла условия остановки, она выполняет «рекурсию». Для этого она сохраняет значения всех локальных переменных, которые ей понадобятся позже после завершения «рекурсии». Она также сохраняет значение pc для участка кода, который она будет выполнять после завершения «рекурсии». В этом примере следующим выполняется 2 блок кода, поэтому она сохраняет 2 в качестве следующего значения pc. Самый простой способ сохранения значений локальных переменных и pc состоит в использовании стеков, подобных тем, которые описывались в 3 главе.

Реальный пример поможет вам понять эту схему. Рассмотрим слегка измененную версию функции факториала. В нем переписана только подпрограмма, которая возвращает свое значение при помощи переменной, а не функции, для упрощения работы.

 

Private Sub Factorial(num As Integer, value As Integer)

Dim partial As Integer

1  If num <= 1 Then

      value = 1

   Else

      Factorial(num - 1, partial)

2     value = num * partial

   End If

End Sub

 

После возврата процедуры из рекурсии, требуется узнать исходное значение переменной num, чтобы выполнить операцию умножения value = num * partial. Поскольку процедуре требуется доступ к значению num после возврата из рекурсии, она должна сохранять значение переменных pc и num до начала рекурсии.

Следующая процедура сохраняет эти значения в двух стеках на основе массивов. При подготовке к рекурсии, она проталкивает значения переменных num и pc в стеки. После завершения рекурсии, она выталкивает добавленные последними значения из стеков. Следующий код демонстрирует нерекурсивную версию подпрограммы вычисления факториала.

 

Private Sub Factorial(num As Integer, value As Integer)

ReDim num_stack(1 to 200) As Integer

ReDim pc_stack(1 to 200) As Integer

Dim stack_top As Integer     ' Вершина стека.

Dim pc As Integer

 

pc = 1

Do

   Select Case pc

      Case 1

          If num <= 1 Then    ' Это условие остановки.                         value = 1

              pc = 0          ' Конец рекурсии.

          Else                ' Рекурсия.

               ' Сохранить num и следующее значение pc.

          stack_top = stack_top + 1

              num_stack(stack_top) = num

              pc_stack(stack_top) = 2 ' Возобновить с 2.

               ' Начать рекурсию.

              num = num - 1

               ' Перенести блок управления в начало.

              pc = 1

          End If

      Case 2

          ' value содержит результат последней

          ' рекурсии. Умножить его на num.

          value = value * num

          ' "Возврат" из "рекурсии".

          pc = 0

      Case 0

          ' Конец "рекурсии".

          ' Если стеки пусты, исходный вызов

          ' подпрограммы завершен.

          If stack_top <= 0 Then Exit Do

          ' Иначе восстановить локальные переменные и pc.

          num = num_stack(stack_top)

          pc = pc_stack(stack_top)

          stack_top = stacK_top - 1

      End Select

   Loop

End Sub

 

Так же, как и устранение хвостовой рекурсии, этот метод имитирует поведение рекурсивного алгоритма. Процедура заменяет каждый рекурсивный вызов итерацией цикла While. Поскольку число шагов остается тем же самым, полное время выполнения алгоритма не изменяется.

Так же, как и в случае с устранением хвостовой рекурсии, этот метод устраняет глубокую рекурсию, которая может переполнить стек.



2019-07-03 246 Обсуждений (0)
Устранение рекурсии в общем случае 0.00 из 5.00 0 оценок









Обсуждение в статье: Устранение рекурсии в общем случае

Обсуждений еще не было, будьте первым... ↓↓↓

Отправить сообщение

Популярное:
Генезис конфликтологии как науки в древней Греции: Для уяснения предыстории конфликтологии существенное значение имеет обращение к античной...
Как распознать напряжение: Говоря о мышечном напряжении, мы в первую очередь имеем в виду мускулы, прикрепленные к костям ...
Модели организации как закрытой, открытой, частично открытой системы: Закрытая система имеет жесткие фиксированные границы, ее действия относительно независимы...



©2015-2024 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (246)

Почему 1285321 студент выбрали МегаОбучалку...

Система поиска информации

Мобильная версия сайта

Удобная навигация

Нет шокирующей рекламы



(0.007 сек.)