Можно заменить рекурсию итерацией плюс неограниченную память .
Если у вас есть только итерация (скажем, в то время как циклы) и ограниченный объем памяти, то все, что у вас есть, это конечный автомат. При ограниченном объеме памяти вычисление имеет конечное число возможных шагов, поэтому можно смоделировать их все с помощью конечного автомата.
Неограниченная память меняет дело. Эта неограниченная память может принимать много форм, которые оказываются эквивалентными выразительной силой. Например, на машине Тьюринга все просто: есть одна лента, и компьютер может двигаться вперед или назад на ленте только на один шаг за раз - но этого достаточно, чтобы делать все, что вы можете делать с рекурсивными функциями.
Машина Тьюринга может рассматриваться как идеализированная модель компьютера (конечного автомата) с некоторой дополнительной памятью, которая увеличивается по требованию. Обратите внимание, что крайне важно, чтобы не только не было конечной границы на ленте, но даже учитывая входные данные, вы не можете надежно предсказать, сколько ленты потребуется. Если бы вы могли предсказать (т. Е. Вычислить), сколько ленты требуется из входных данных, то вы могли бы решить, остановится ли вычисление, рассчитав максимальный размер ленты и затем обработав всю систему, включая ленту с конечным результатом, как конечный автомат ,
Другой способ моделирования машины Тьюринга с компьютерами заключается в следующем. Имитируйте машину Тьюринга с помощью компьютерной программы, которая хранит начало ленты в памяти. Если вычисление достигает конца той части ленты, которая помещается в памяти, замените компьютер на больший компьютер и снова запустите вычисление.
Теперь предположим, что вы хотите смоделировать рекурсивные вычисления с помощью компьютера. Методы выполнения рекурсивных функций хорошо известны: каждый вызов функции имеет часть памяти, называемую кадром стека . Важно отметить, что рекурсивные функции могут распространять информацию посредством нескольких вызовов, передавая переменные. С точки зрения реализации на компьютере это означает, что вызов функции может получить доступ к кадру стека (grand-) * родительского вызова.
Компьютер - это процессор - конечный автомат (с огромным количеством состояний, но мы здесь занимаемся теорией вычислений, поэтому все, что имеет значение, это то, что он конечен) - в сочетании с конечной памятью. Микропроцессор запускает один гигантский цикл while: «при включенном питании прочитайте инструкцию из памяти и выполните ее». (Реальные процессоры намного сложнее, но это не влияет на то, что они могут вычислить, только на то, насколько быстро и удобно они это делают.) Компьютер может выполнять рекурсивные функции только с помощью этого цикла while для обеспечения итерации, плюс механизм для доступ к памяти, в том числе возможность увеличения объема памяти по желанию.
Если вы ограничиваете рекурсию примитивной рекурсией, то вы можете ограничить итерацию ограниченной итерацией. То есть вместо использования циклов while с непредсказуемым временем выполнения вы можете использовать циклы for, в которых число итераций известно в начале цикла¹. Число итераций может быть неизвестно в начале программы: оно само может быть вычислено предыдущими циклами.
Я не собираюсь даже набрасывать здесь доказательство, но есть интуитивная связь между переходом от примитивной рекурсии к полной рекурсии и переходом от циклов for к циклам while: в обоих случаях это означает, что вы заранее не знаете, когда стоп. При полной рекурсии это делается с помощью оператора минимизации, который продолжается до тех пор, пока вы не найдете параметр, удовлетворяющий условию. С циклами while это выполняется путем продолжения работы до тех пор, пока условие цикла не будет выполнено.
¹ циклы в C-подобных языках могут выполнять неограниченную итерацию точно так же , как это принято, ограничивать их ограниченной итерацией. Когда люди говорят о «для циклов» в теории вычислений, это означает только циклы, которые насчитывают от 1 до n (или эквивалент). for
while
N