2015-09-26 2 views
-1

Я пытаюсь реализовать решение для факториала (n!) Без использования рекурсии, только используя ретроакцию пролога. Например:Факториальный расчет без использования рекурсии

factorial(0, 1). 
factorial(1, 1). 
factorial(2, 2). 

retroaction(X, Y) :- 
factorial(K, Y), 
not(K = X), 
fail. 

retroaction(K, Y) :- factorial(K, Y). 

С фиксированными фактами факторного, если я называю предикат обратного действия, чтобы открыть факториал 2, к примеру, я получить правильный ответ:

?- retroaction(2, X). 
X = 2. 

Решение, которое я пытаюсь реализовать:

factorial_ret(Number, _) :- 
    retractall(factorial(_, _)), 
    assertz(factorial(0,1)), 
    factorial(X, Y), 
    not(X = Number), 
    NewNumber is X + 1, 
    Factorial is Y * NewNumber, 
    retractall(factorial(_, _)), 
    assertz(factorial(NewNumber, Factorial)), 
    fail. 

factorial_ret(Number, Y) :- 
    factorial(Number, Y). 

Если я попытаюсь получить факториал числа больше 1, не работает. Кто-то имеет представление о том, почему?

+0

Справа, @luker. Сообщение обновлено. – rwehresmann

+2

Это немного запутанно. Какой предикат вы хотите вызвать, чтобы получить факториал? Казалось бы, 'factorial/2', но в вашем примере используется' retroaction/2', а ваша основная реализация - в 'factorial_ret/2', которая не вызывается нигде. – lurker

+0

Предикат для получения факториала является 'factorial/2'. «Retroaction/2» был всего лишь примером, чтобы показать идеализацию факториального нерекурсивного решения. – rwehresmann

ответ

1

Как отметил @lurker, вы сбиваете с толку «предикат хранения» с «предикатом определения». Вам нужно определение factorial/2, поэтому он не имеет смысла для retractall(factorial(_,_)).

Ниже я использую retract(mem(N, F)) для получения временных значений и подготовки БД для возможного обновления. Всегда должен быть только экземпляр mem/2.

% replace recursion with a failure driven loop 
:- dynamic mem/2. 
factorial(X, Y) :- 
    assert(mem(0, 1)), 
    repeat, 
    retract(mem(N, F)), 
    ( X == N % done ? 
    -> Y = F, % yes, succeed 
     !  % but be sure to avoid 'external' backtracking... 
    ; N1 is N + 1, 
     F1 is N1 * F, 
     assert(mem(N1, F1)), 
     fail % loop: 'goto repeat' 
    ). 

Обратите внимание, что все встроенные функции в ветви между repeat,...,fail не являются backtrackable (ну, за исключением отводного/1).

Обратите внимание, что такой код будет намного медленнее рекурсивного определения (и не будет работать в многопоточном SWI-Prolog).

Я думаю, что assert/retract были доступны раньше программистам Prolog, как требуется для обработки «баз знаний», а не для глобальных переменных. Несколько Прологов имеют специализированный предикат библиотеки для глобальных переменных, поскольку их законное использование: например, см. memoization.

PS: «задним числом» это термин, используемый в линейной теории устойчивости систем, я никогда не видел раньше о программировании

редактировать Может быть полезно посмотреть, как ошибка сообщает @repeat может быть решена : просто замените унификацию и разрез. Ну, нам также нужно проверить, что X - положительное целое число. С уважением, я думаю, что на самом деле это не имеет отношения к делу.

... 
    ( X == N % done ? 
    -> !,  % yes, be sure to avoid further backtracking 
     Y = F % unify with the computed factorial 
    ; N1 is N + 1, 
... 
+0

Я понимаю представленные решения, но деталь все еще немного запутывает для меня. Прежде чем я приведу эту проблему в SO, я понимаю, что «fail/0' заставляет обратный отсчет, как я показал с предикатом« ретроакция/2 »в своем сообщении. Но используя assert и retract, этого не произошло (в вашем и @Boris-решении для этого было использовано 'repeat/0'). Вы понимаете, почему? – rwehresmann

+0

fail/0 принудительно сбой. Откат является следствием отказа, * если * есть остатки выбора, оставшиеся на стеке доказательств. Но вы убрали факториал/2, удалив их. Все остальные предикаты детерминированы. – CapelliC

+0

Но я также вставил с 'assertz/1' новый' factorial/2' перед копьем 'fail/0'. Я думал, что в стек доказательств должен появиться новый выбор. – rwehresmann

-1

У вас есть нижний регистр k в вашем предложении not.

+1

Правильно, @Scott Hunter. Теперь я изменился. Остается функциональным. – rwehresmann

+0

Вы написали комментарий, а не ответ. Программа, представленная ОП, логически ошибочна, 'k' или no' k'. – repeat

0

Предполагая, что «обратная реакция» вы на самом деле означает "memoization", один путь об этом будет:

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

Я пишу этот ответ, потому что он имеет немного менее ненужный код, чем в противном случае правильный answer by @CapelliC.

Вам нужен только один начальный факт для этого, факториал 0:

Затем вы можете использовать в repeat цикл, чтобы сделать «пока». Затем, если вы всегда добавлять новые факториалов в верхней части стопки предварительно рассчитанных факториалов и вы всегда смотреть только один раз, вы можете быть уверены, что вы найдете самый большой один до сих пор:

:- dynamic factorial/2. 
factorial(0, 1). 

factorial_memoize(N, F) :- 
    repeat, 
     ( factorial(N, F0) 
     -> !, F = F0 
     ; once(factorial(N0, F0)), 
      succ(N0, N1), 
      F1 is N1 * F0, 
      asserta(factorial(N1, F1)), 
      fail 
     ). 

Вот как программные работы:

?- listing(factorial/2). 
:- dynamic factorial/2. 

factorial(0, 1). 

true. 

?- factorial_memoize(0, F). 
F = 1. 

?- listing(factorial/2). 
:- dynamic factorial/2. 

factorial(0, 1). 

true. 

?- factorial_memoize(5, F). 
F = 120. 

?- listing(factorial/2). 
:- dynamic factorial/2. 

factorial(5, 120). 
factorial(4, 24). 
factorial(3, 6). 
factorial(2, 2). 
factorial(1, 1). 
factorial(0, 1). 

true. 
+0

- хороший ответ, но не выделяет более неприятную проблему, которая не очевидна: retract и логические обновления (правда, ни моя не делает ...) – CapelliC

+1

@CapelliC Я предполагаю, что Само собой разумеется. –

+0

@CapelliC И еще одно замечание. Кажется, что вы просто сохраняете «текущее значение», в то время как это реализует правильную «меморандум», что оно сохраняет все значения, которые он вычисляет по пути. Конечно, вопрос OP никогда не говорил, что они хотят воспоминания. –

2

無! Моя совесть заставляет меня un-ask your question.

За то, что вы искать является no-no -Даже в -И более в Прологе.

, конечно, может быть очень полезным в некоторых обстоятельствах; Обычно я рассматриваю его как «оружие последней инстанции», а не как «первое оружие выбора». ИМО вероятные последствия его использования включают:

Дело в точке? Возьмите ответы by @CapelliC и by @Boris. кавычки @Boris:

Я пишу этот ответ, потому что он имеет немного меньше ненужного кода, чем иначе правильный ответ по @CapelliC.

Давайте запустим несколько запросов и положим эту оценку на тест!

?- factorialBoris(0,0). 
**LOOPS** 

?- factorialBoris(0,2). 
**LOOPS** 

?- factorialBoris(1,2). 
**LOOPS** 

?- factorialCapelliC(0,0). 
**LOOPS** 

?- factorialCapelliC(0,2). 
**LOOPS** 

нет!

Пожалуйста, не поймите меня неправильно! Я не хочу подавать работу и усилие by @CapelliC и by @Boris, два Prolog topusers on SO, в любом случае, форма или форма.


Итог:

Использование стиля при кодировании в Прологе может легко иметь неприятные последствия!

Тщательное тестирование кода с использованием гораздо сложнее, чем тестирование logically-pure кода и даже что может быть довольно много, поверьте!

В случае неопределенности выберите мудрый путь: просто делать то, что правильно :)

Preserve по возможности-не торговать в ничего взамен.

+2

Я не вижу никаких усилий, чтобы ответить на вопрос OP. – CapelliC

+1

Воспоминание не «нет-нет», оно имеет действительные цели. В любом случае, этот вопрос, очевидно, служит цели обсуждения техники программирования, нам не нужно немедленно проявлять религиозность над этим. И если кто-то действительно _thinks_, что это хороший способ вычислить факториал, они также могут это сделать. –

+0

Спасибо, что указали на ошибку в моем решении, это помогло мне исправить это. –