【问题标题】:Inverse factorial in PrologProlog中的逆阶乘
【发布时间】:2013-09-26 10:25:31
【问题描述】:

有人可以帮我找到一种在 Prolog 中获得逆阶乘的方法吗...

例如inverse_factorial(6,X) ===> X = 3

我已经为此工作了很多时间。

我目前有阶乘,但我必须让它可逆。请帮帮我。

【问题讨论】:

  • 我已经添加了示例。

标签: prolog factorial clpfd


【解决方案1】:

Prolog 的谓词是关系,所以一旦你定义了阶乘,你也隐含地定义了逆。然而,正则运算在 Prolog 中进行了建模,也就是说,(is)/2(>)/2 中的整个表达式必须在运行时知道,如果不知道,则会发生错误。约束克服了这个缺点:

:- use_module(library(clpfd))。 n_factorial(0, 1)。 n_factorial(N, F) :- N #> 0, N1 #= N - 1, F #= N * F1, n_factorial(N1,F1)。

这个定义现在可以双向使用。

?- n_factorial(N,6)。
N = 3 ;
错误的。

?- n_factorial(3,F)。
F = 6;
错误的。

由于 SICStus 4.3.4 和 SWI 7.1.25 也终止了:

?- n_factorial(N,N).
   N = 1
;  N = 2
;  false.

请参阅the manual 了解更多信息。

【讨论】:

  • @false。使用 SWI 中的 clpfd,查询确实 终止。怎么会?
  • @false。我复制了它,但是使用?- n_factorial(N,N). 我得到N = 1 ; N = 2 ; false. 事实上,我确实 使用n_fac 的其他一些变体得到非终止我在运行查询@987654329 时在别处编码@ ... 但在这儿?没有。
  • @false。很高兴知道!当然,我并不怀疑你的观察是有效的。我根本无法重现它。感谢您为调查多个版本付出的额外努力。
  • 这个定义在两个方向都有效,并且是最自然和可读的实现方式,但是在搜索逆阶乘时非常慢,例如n_factorial(Y, 1000000000) 在我的机器上回答 false 大约需要 20 秒。
  • n_factorial(N,N). 应终止于昨天发布的 SICStus Prolog 4.3.4。
【解决方案2】:

作为参考,这是我能想到的声明式 factorial 谓词的最佳实现。

与@false 的回答有两点不同:

  • 它使用累加器参数,递归调用增加我们乘以阶乘的因子,而不是基本情况为0 的标准递归实现。当阶乘已知且初始数未知时,这使得谓词更快。

  • 它广泛使用模块reif 中的if_/3(=)/3,以尽可能消除不必要的选择点。它还使用(#>)/3 和具体化的(===)/6,这是(=)/3 的变体,用于我们有两对可用于if_if -> then 部分的情况。

factorial/2

factorial(N, F) :-
    factorial(N, 0, 1, F).

factorial(N, I, N0, F) :-
    F #> 0,
    N #>= 0,
    I #>= 0,
    I #=< N,
    N0 #> 0,
    N0 #=< F,
    if_(I #> 2,
        (   F #> N,
            if_(===(N, I, N0, F, T1),
                if_(T1 = true,
                    N0 = F,
                    N = I
                ),
                (   J #= I + 1,
                    N1 #= N0*J,
                    factorial(N, J, N1, F)
                )
            )
        ),
        if_(N = I,
            N0 = F,
            (   J #= I + 1,
                N1 #= N0*J,
                factorial(N, J, N1, F)
            )
        )
    ).

(#&gt;)/3

#>(X, Y, T) :-
    zcompare(C, X, Y),
    greater_true(C, T).

greater_true(>, true).
greater_true(<, false).
greater_true(=, false).

(===)/6

===(X1, Y1, X2, Y2, T1, T) :-
    (   T1 == true -> =(X1, Y1, T)
    ;   T1 == false -> =(X2, Y2, T)    
    ;   X1 == Y1 -> T1 = true, T = true
    ;   X1 \= Y1 -> T1 = true, T = false
    ;   X2 == Y2 -> T1 = false, T = true
    ;   X2 \= Y2 -> T1 = false, T = false
    ;   T1 = true, T = true, X1 = Y1
    ;   T1 = true, T = false, dif(X1, Y1)
    ).

一些查询

?- factorial(N, N).
N = 1 ;
N = 2 ;
false.          % One could probably get rid of the choice point at the cost of readability


?- factorial(N, 1).
N = 0 ;
N = 1 ;
false.          % Same


?- factorial(10, N).
N = 3628800.    % No choice point


?- time(factorial(N, 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000)).
% 79,283 inferences, 0.031 CPU in 0.027 seconds (116% CPU, 2541106 Lips)
N = 100.        % No choice point


?- time(factorial(N, 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518284253697920827223758251185210916864000000000000000000000000)).
% 78,907 inferences, 0.031 CPU in 0.025 seconds (125% CPU, 2529054 Lips)
false.


?- F #> 10^100, factorial(N, F).
F = 11978571669969891796072783721689098736458938142546425857555362864628009582789845319680000000000000000,
N = 70 ;
F = 850478588567862317521167644239926010288584608120796235886430763388588680378079017697280000000000000000,
N = 71 ;
F = 61234458376886086861524070385274672740778091784697328983823014963978384987221689274204160000000000000000,
N = 72 ;
...

【讨论】:

  • 到目前为止,您发现 SO 上的“简单迭代变量”(缺少更好的名称)的优化仅适用于 list_length/2 等更简单的情况。我想知道这是否可以自动化!
  • @false “算术和句法相等的混合”是什么意思?不,我不知道(#)/1
  • X= 1+0, X#=1 成功,X#=1, X= 1+0 失败。看来您已经设法消除了所有这些相关问题,这确实令人惊讶。至少我给出的定义有这个缺陷:N = 1+1, n_factorial(N,2). 成功,但n_factorial(N,2),N = 1+1. 失败。有一个仿函数 # /1 来克服这个问题:#(X)#=1, X=0+1 失败,而 X=0+1, #(X)#=1 是一个错误。参见 clpfd/clpz 的手册。这仍然是试探性的,理想情况下 # 将是一个优先级很低的前缀运算符,然后可以这样写:#X + #Y #= #Z
  • @false 当N 接地且F 空闲时,或者两者都空闲时,则可以使用(=)/3 并在then 部分执行N0 = F。但是当F 被接地而N 不是时,那么在N 上使用(=)/3 将留下一个不必要的选择点((=)/3 的最后一部分与dif)我们不会获取 F = N0 是否是 if 部分。所以(===)/6 允许融合这两种情况,同时如果NF 都是免费的,则仍然具有正确的行为。不需要为X2, Y2 添加=dif 部分,实际上会产生冗余结果。
  • 您需要生成声明性构建块。否则,您绝不会比纯粹的程序解决方案更好。
【解决方案3】:

一种简单的“低技术”方式:枚举整数直到

  • 您找到所寻找的阶乘,然后“取回”该数字
  • 正在构建的阶乘大于目标。那么你可能会失败......

实际上,您只需将 2 个参数添加到现有的阶乘实现中,即目标和找到的逆。

【讨论】:

  • 您对我的解决方案有何看法?
  • @SergeiLodyagin: 如果 XFact 不是 阶乘,将循环。在递归调用之前添加一个测试。
【解决方案4】:

只需实现 factorial(X, XFact) 然后交换参数

factorial(X, XFact) :- f(X, 1, 1, XFact).

f(N, N, F, F) :- !.
f(N, N0, F0, F) :- succ(N0, N1), F1 is F0 * N1, f(N, N1, F1, F).

【讨论】:

  • 这不适用于 X=0,它不会计算数字 0 的阶乘。这是因为没有 1 的后继为 0,所以内存不足。有什么方法可以纠正它是可逆的?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
相关资源
最近更新 更多