В книге Структура и интерпретация компьютерных программ содержит рекурсивную процедуру вычисления показателей с использованием последовательного возведения в квадрат.Превращение рекурсивной процедуры в итеративную процедуру - упражнение SICP 1.16
(define (fast-expt b n)
(cond ((= n 0)
1)
((even? n)
(square (fast-expt b (/ n 2))))
(else
(* b (fast-expt b (- n 1))))))
Сейчас в упражнении 1.16:
Упражнение 1.16: Разработка процедуры, которая эволюционирует итерационный процесс возведения в степень, которая использует последовательную квадратуру и использует логарифмическое число шагов, как это делает фаст-эксп. (Подсказка: используя наблюдение, что (b (^ n/2))^2 = (b (^ 2))^n/2 , держите вместе с показателем n и базой b дополнительное состояние переменную a и определить преобразование состояния таким образом, чтобы произведение ab^n не менялось из состояния в состояние. В начале процесса a принимается равным 1, а ответ задается значением a в конец процесса. В общем, метод определения инвариантной величины, который остается неизменным из состояния в состояние, является мощным способом думать об дизайне итеративных алгоритмов.)
Я провел неделю, и я абсолютно уверен, t, как сделать эту итеративную процедуру, поэтому я сдался и искал решения. Все решения, которые я нашел это:
(define (fast-expt a b n)
(cond ((= n 0)
a)
((even? n)
(fast-expt a (square b) (/ n 2)))
(else
(fast-expt (* a b) b (- n 1)))))
Теперь я могу понять
(fast-expt a (square b) (/ n 2)))
используя подсказки из книги, но мой мозг взорвался, когда п нечетно. В рекурсивной процедуре я получил почему
(* b (fast-expt b (- n 1))))))
работы. Но в итерационной процедуре, он становится совершенно другим,
(fast-expt (* a b) b (- n 1)))))
Это прекрасно работает, но я совершенно не понимаю, как прийти к этому решению по себе. это кажется чрезвычайно умным.
Может кто-нибудь объяснить, почему итеративное решение такое? И каков общий способ думать о решении этих проблем?
ок я понимаю инвариантную часть. Но с точки зрения решений я думал, что логика для итеративных и рекурсивных процедур эквивалентна друг другу. Две реализации этой проблемы, похоже, не имеют такой же логики. Я прав? – morbidCode
На самом деле они делают то же логику. Оба решения в конечном итоге умножают значение на 'b', которое не изменяется. В рекурсивной версии мы умножаем 'b' на результат рекурсивного вызова, тогда как в итеративной версии результат рекурсивного вызова накапливается в параметре, но все равно - мы просто избегаем ждать рекурсии для возврата, передав его результат в параметр. Подумайте, что вы делаете, когда вы изменяете локальную переменную в цикле 'for' на императивном языке, _it здесь же, здесь мы просто используем параметры, так как мы будем использовать локальную переменную. –
Я все еще не понимаю, если n нечетно. В рекурсивной версии, если n нечетно, мы делаем b * другой вызов функции с n-1, теперь n четно. Я думаю, что b * добавит еще одну базу. Например, если n равно 9, мы делаем 2 * fastExp 9-8, а затем продолжаем с вызовами n равно. Я думаю, что 2 * есть для девятого 2 размножаться. Но я не вижу b * в итеративной версии. Я действительно запутался в том, что происходит, если n нечетно. – morbidCode