不使用递归的因子计算

时间:2015-09-26 01:37:43

标签: prolog factorial prolog-assert

我试图在不使用递归的情况下实现因子计算(n!)的解决方案,仅使用prolog的retroaction。例如:

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).   

使用固定事实 factorial ,如果我调用谓词 retroaction 来发现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的阶乘,则不起作用。有人知道为什么吗?

4 个答案:

答案 0 :(得分:2)

无!我的良心迫使我un-ask your question

寻求的内容是no-no - 即使在 - 而且在Prolog中也是如此。

在某些情况下,

肯定非常有用;我通常将其视为“万不得已的武器”,而不是“首选武器”。 IMO使用它的可能后果包括:

一个例子吗?得到答案by @CapelliCby @Boris。 引用@Boris:

  

我正在写这个答案,因为它比@CapelliC的正确答案少了一些不必要的代码。

让我们运行一些查询并将评估纳入考试!

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

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

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

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

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

<强>нет!

请不要误会我的意思!我不想以任何方式,形式或形式放下工作和努力by @CapelliCby @Boris两个Prolog topusers on SO

底线:

在Prolog中编码时使用样式很容易适得其反!

使用严格测试代码比测试logically-pure代码要困难得多 - 甚至

如果存在不确定性,请选择明智的路径:只需做正确的事情:)

尽可能保留 - 不要交换任何东西作为回报。

答案 1 :(得分:1)

正如@lurker指出的那样,你混淆了存储谓词&#39;使用&#39;定义谓词&#39;。你需要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之间分支中的所有内置函数都不可回溯(除了retract / 1之外)。

请注意,此类代码的递归定义要慢得多(并且在多线程SWI-Prolog中不起作用)。

我认为断言/撤回是早期提供给Prolog程序员的,以处理知识库,而不是作为全局变量。有几个Prolog对全局变量有专门的库谓词,因为它们有合法用途:例如,参见memoization

PS:&#39;追溯&#39;是线性系统稳定性理论中使用的术语,我从未见过它用于编程

编辑可以了解@repeat报告的错误如何解决:只需交换统一和剪切即可。 好吧,我们还需要测试X是一个正整数。真诚地,我认为这对于手头的问题实际上是无关的

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

答案 2 :(得分: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.

答案 3 :(得分:-1)

您的k条款中有一个小写的not