positive_integer / 1谓词适用于大数字

时间:2016-08-31 15:01:49

标签: prolog integer clpfd

在我的Prolog灵感语言Brachylog中,有可能标记具有潜在无限域的CLP(FD)等效变量。可以找到执行此标注的代码here(感谢Markus Triska @mat)。

此谓词需要存在谓词positive_integer/1,该行必须具有以下行为:

?- positive_integer(X).
X = 1 ;
X = 2 ;
X = 3 ;
X = 4 ;
…

这在我们当前的解决方案中实现:

positive_integer(N) :- length([_|_], N).

我可以看到两个问题:

  • 这会很快变慢:

    ?- time(positive_integer(100000)).
    % 5 inferences, 0.000 CPU in 0.001 seconds (0% CPU, Infinite Lips)
    
    ?- time(positive_integer(1000000)).
    % 5 inferences, 0.000 CPU in 0.008 seconds (0% CPU, Infinite Lips)
    
    ?- time(positive_integer(10000000)).
    % 5 inferences, 0.062 CPU in 0.075 seconds (83% CPU, 80 Lips)
    
  • 对于有点过大的数字,这最终会返回Out of global stack错误:

    ?- positive_integer(100000000).
    ERROR: Out of global stack
    

这显然是因为Prolog需要实例化列表,如果它的长度很大就很糟糕。

我们怎样才能改进这个谓词,这样即使对于非常大的数字也能有相同的行为?

4 个答案:

答案 0 :(得分:5)

已经发布了许多好的想法,并且它们在不同程度上发挥作用。

附加测试用例

@vmg有正确的直觉:between/3与约束不能很好地融合。为了看到这一点,我想使用以下查询作为额外的基准:

?- X #> 10^30, positive_integer(X).

解决方案

考虑到测试用例,我建议使用以下解决方案

positive_integer(I) :-
        I #> 0,
        (   var(I) ->
            fd_inf(I, Inf),
            (   I #= Inf
            ;   I #\= Inf,
                positive_integer(I)
            )
        ;   true
        ).

关键思想是使用CLP(FD)反射谓词 fd_inf/2来推断变量域中的最小元素。当您将解决方案移植到其他Prolog系统时,这是您需要更改的唯一谓词。例如,在SICStus Prolog中,谓词称为fd_min/2

主要功能

  • 轻松到几个Prolog系统,只需更改
  • 快速在所示情况下
  • 工作也在上面的测试用例中
  • 宣传并使用CLP(FD)约束的全部功能。

当然,非常清楚哪一点是最重要的。

示例查询

creatio ex nihilo

?- positive_integer(X).
X = 1 ;
X = 2 ;
X = 3 .

已修复整数:

?- X #= 12^42, time(positive_integer(X)).
% 4 inferences, 0.000 CPU in 0.000 seconds (68% CPU, 363636 Lips)
X = 2116471057875484488839167999221661362284396544.

约束整数:

?- X #> 10^30, time(positive_integer(X)).
% 124 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 3647059 Lips)
X = 1000000000000000000000000000001 ;
% 206 inferences, 0.000 CPU in 0.000 seconds (93% CPU, 2367816 Lips)
X = 1000000000000000000000000000002 ;
% 204 inferences, 0.000 CPU in 0.000 seconds (92% CPU, 2428571 Lips)
X = 1000000000000000000000000000003 .

其他评论

  1. 首先,请务必查看Code Golf上的Brachylog和最新Brachylog solutions。感谢Julien的努力,一种受Prolog启发的语言现在越来越多地主持一些最简洁优雅的程序。朱利安很棒的工作!

  2. 请不要使用between/3的特定于实现的异常:这些异常会破坏谓词的重要语义属性,并且无法移植到其他系统。

  3. 如果您忽略(2),请使用infinite代替inf。在CLP(FD)的上下文中,inf表示整数集的下限,这是正无穷大的相反

  4. 在CLP(FD)的上下文中,我建议使用CLP(FD)约束代替 between/3以及不采用约束的其他谓词考虑到

  5. 事实上,我建议使用CLP(FD)约束代替 所有低级谓词来推理整数。这最多可以使您的程序更通用,而不是更具体。

  6. 非常感谢您对此问题和发布的解决方案感兴趣!我希望您发现以上测试用例对您的变体有用,并找到在您的版本中考虑CLP(FD)约束的方法,以便它们运行得更快,我们都可以对它们进行投票!

答案 1 :(得分:4)

Since "Brachylog's interpreter is entirely written in Prolog" meaning SWI-Prolog, you can use between/3 with the second argument bound to inf.

Comparing your positive_integer with

positive_integer_b(X):- between(1,inf,X).

Tests on my machine:

?- time(positive_integer(10000000)).
% 5 inferences, 0.062 CPU in 0.072 seconds (87% CPU, 80 Lips)
true.
9 ?- time(positive_integer_b(10000000)).
% 2 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
true.

And showing "Out of global stack":

13 ?- time(positive_integer(100000000)).
% 5 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
ERROR: Out of global stack

14 ?- time(positive_integer_b(100000000)).
% 2 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
true.

I don't think between is pure prolog though.

答案 2 :(得分:1)

In case you want your code to be runnable also on other systems, consider:

positive_integer(N) :-
   (  nonvar(N), % strictly not needed, but clearer
      integer(N),
      N > 0
   -> true
   ;  length([_|_], N)
   ).

This version produces exactly the same errors as your first try.

You have indeed spotted a bit towards a weakness in current length/2 implementations. Ideally, a goal like length([_|_], 1000000000000000) would take some time, but at least does not consume more than constant memory. On the other hand, I am not too sure if this is worth optimizing. After all, I do not see an easy way to solve the runtime problem for such cases.

Note that the version of between/3 in SWI-Prolog is highly specific to SWI. It makes termination arguments much more complex. In other systems like SICStus, you know for sure that between/3 is terminating, regardless of the arguments. In SWI you would have to prove, that the atom inf will not be encountered which raises the burden of proof obligation.

答案 3 :(得分:1)

没有/ 3和ISO兼容(我认为)

positive_integer(1).
positive_integer(X) :-
 var(X),
 positive_integer(Y),
 X is Y + 1.
positive_integer(X) :-
 integer(X),
 X > 0.