Факториальная рекурсия пролога
у меня возникли проблемы с пониманием следующей факторной программы
fact1(0,Result) :-
Result is 1.
fact1(N,Result) :-
N > 0,
N1 is N-1,
fact1(N1,Result1),
Result is Result1*N.
, когда fact1
называется вложенный в секунду!--1-->, не означает ли это, что последняя строка,Result is Result1*N.
, никогда не вызывается? Или в Prolog последняя строка выполняется перед рекурсивным вызовом?
9 ответов
нет, рекурсивный вызов происходит впервые! Иначе последнее предложение бессмысленно. Алгоритм разбивается на:
factorial(0) => 1
factorial(n) => factorial(n-1) * n;
Как вы можете видеть, Вам нужно вычислить результат рекурсии перед умножением, чтобы вернуть правильное значение!
ваша реализация пролога, вероятно, имеет способ включить трассировку, которая позволит вам увидеть весь алгоритм работает. Это может тебе помочь.
кстати, как только вы поняли основную рекурсию, попробуйте достичь хвостовой рекурсии, когда это возможно, здесь было бы:
factorial(N, R) :- factorial(N, 1, R).
factorial(0, R, R) :- !.
factorial(N, Acc, R) :-
NewN is N - 1,
NewAcc is Acc * N,
factorial(NewN, NewAcc, R).
хвостовая рекурсия, в отличие от рекурсии, которую вы использовали ранее, позволяет интерпретатору / компилятору очищать контекст при переходе к следующему шагу рекурсии. Итак, предположим, вы вычисляете factorial(1000)
, ваша версия будет поддерживать 1000 контекстов, в то время как моя будет поддерживать только 1. Это означает, что ваша версия в конечном итоге не вычислит желаемый результат, а просто авария на Out of call stack memory
ошибка.
вы можете подробнее об этом на Википедии.
вообще говоря, @м09 это!--10--> в основном прав относительно важности хвостовой рекурсии.
большой N
, расчет продукта по-разному побед! Подумайте "двоичное дерево", а не"линейный список"...
давайте попробуем в обоих направлениях и сравнить время выполнения. Во-первых, @м09 это factorial/2
:
?- time((factorial(100000,_),false)). % 200,004 inferences, 1.606 CPU in 1.606 seconds (100% CPU, 124513 Lips) false.
Далее, мы делаем это дерево-стиль-с помощью мета-предикат reduce/3
вместе с лямда выражения:
?- time((numlist(1,100000,Xs),reduce(\X^Y^XY^(XY is X*Y),Xs,_),false)). % 1,300,042 inferences, 0.264 CPU in 0.264 seconds (100% CPU, 4922402 Lips) false.
наконец, давайте определим и используем выделенный вспомогательный предикат x_y_product/3
:
x_y_product(X, Y, XY) :- XY is X*Y.
что это даст? Давайте!--27-->задать секундомер!
?- time((numlist(1,100000,Xs),reduce(x_y_product,Xs,_),false)). % 500,050 inferences, 0.094 CPU in 0.094 seconds (100% CPU, 5325635 Lips) false.
factorial(1, 1).
factorial(N, Result) :- M is N - 1,
factorial(M, NextResult), Result is NextResult * N.
объявлен базовый регистр. Условия, что N должно быть положительным и умножить на предыдущий член.
factorial(0, 1).
factorial(N, F) :-
N > 0,
Prev is N -1,
factorial(Prev, R),
F is R * N.
запуск:
факториал(-1, X).
Я бы сделал что-то вроде:
fact(0, 1).
fact(N, Result):-
Next is N - 1,
fact(Next, Recursion),
Result is N * Recursion.
и хвостовая версия будет выглядеть так:
tail_fact(0, 1, 0). /* when trying to calc factorial of zero */
tail_fact(0, Acc, Res):- /* Base case of recursion, when reaches zero return Acc */
Res is Acc.
tail_fact(N, Acc, Res):- /* calculated value so far always goes to Acc */
NewAcc is N * Acc,
NewN is N - 1,
tail_fact(NewN, NewAcc, Res).
Так для вас называть:
не хвост рекурсивный метод: Факт(3, Итог).
хвост рекурсивный метод: tail_fact(3, 1, результат).
Это может помочь ;)
не-tailer рекурсия:
fact(0,1):-!.
fact(X,Y):- Z=X-1,
fact(Z,NZ),Y=NZ*X.
рекурсия tailer:
fact(X,F):- X>=0,fact_aux(X,F,1).
fact_aux(0,F,F):-!.
fact_aux(X,F,Acc):-
NAcc=Acc*X, NX=X-1,
fact_aux(NX,F,NAcc).