当fib(N,F)
是F
斐波纳契数时,N
一词为真。
以下Prolog代码通常适用于我:
:-use_module(library(clpfd)).
fib(0,0).
fib(1,1).
fib(N,F) :-
N #> 1,
N #=< F + 1,
F #>= N - 1,
F #> 0,
N1 #= N - 1,
N2 #= N - 2,
F1 #=< F,
F2 #=< F,
F #= F1 + F2,
fib(N1,F1),
fib(N2,F2).
执行此查询时(在SICStus Prolog中),找到N的第一个(和正确的)匹配(而不是立即):
| ?- fib(X,377).
X = 14 ?
当继续(通过输入“;”)查看是否还有其他匹配(根据定义是不可能的)时,需要花费大量时间(与第一次匹配相比),只是总是回答否:< / p>
| ?- fib(X,377).
X = 14 ? ;
no
作为Prolog的新手,我尝试以各种方式使用Cut-Operator(!
),但是在第一场比赛后我找不到阻止搜索的方法。根据上述规则,它是否可能?如果是,请告诉我如何:)
答案 0 :(得分:8)
有两个部分可以得到你想要的东西。
首先是使用
call_semidet/1
这确保只有一个答案。查看链接
SICStus的实现也是如此。万一有更多
一个答案,call_semidet/1
会产生安全错误。注意
仅once/1
和!/0
就可以简单地删除所有内容。
但是,仅凭call_semidet/1
你就不会很满意。它
基本上执行两次目标。一旦看看是否没有更多
不止一个答案,只有这样才能获得第一个答案。所以
你会在以后得到答案。
另一部分是加快你的定义,以上不会 太让你不安CapelliC建议的解决方案发生了变化 完全由你的具体功能决定的算法 但不扩展到任何其他功能。但它也描述了一个 不同的关系。
基本上,你已经找到了典型的部分,你只需要 组装它们有点不同,使它们工作。但是,让我们开始吧 基础知识。
你在这里做的事情对许多Prolog来说仍然不常见
程序员。您将有限域约束用于一般整数
运算。也就是说,您正在使用CLPFD作为纯粹的替代品
(is)/2
,(>)/2
和false
中的模式表达式评估
喜欢。所以你想扩展假设的有限域范式
我们在有限的给定间隔内表达一切。事实上,它
将此扩展称为CLP(Z)更合适。
这个扩展在每个Prolog提供有限的版本中都不起作用 域。事实上,只有正确的SICStus,SWI和YAP 处理无限间隔的情况。其他系统可能会失败或 当他们宁愿成功或失败时成功 - 主要是整数时 变得太大了。
第一个问题是了解原始的原因
程序没有终止。为此,我将使用failure
slice。那
是的,我在您的计划中添加了 F
目标。关键是:如果
结果程序不会终止然后也是原始程序
不会终止。所以你的最小失败片(推测)
原始程序是:
fiborig(0,0) :- false.fiborig(1,1) :- false. fiborig(N,F) :- N #> 1, N1 #= N-1, N2 #= N-2, F #= F1+F2, fiborig(N1,F1), false,fiborig(N2,F2).
这里有两个非终止来源:一个是给定的
F1
F2
和F1 #> 0, F2 #>= 0
有无限多的值。那可以
通过观察F2 #= 0
来轻松处理。
另一个与Prolog的执行机制更相关。至
说明一下,我将添加0
。再次,因为结果
程序不会终止,原始程序也会循环。
fiborig(0,0) :- false.fiborig(1,1) :- false. fiborig(N,F) :- N #> 1, N1 #= N-1, N2 #= N-2, F #= F1+F2, F1 #> 0, F2 #>= 0, F2 #= 0, fiborig(N1,F1), false,fiborig(N2,F2).
因此,实际问题是可能导致F2 #=< F1
的目标
执行得太晚了。只需交换两个递归目标。并添加
效率的冗余约束fib(N, 377)
。
fibmin(0,0). fibmin(1,1). fibmin(N,F) :- N #> 1, N1 #= N-1, N2 #= N-2, F1 #> 0, F2 #>= 0, F1 #>= F2, F #= F1+F2, fibmin(N2,F2), fibmin(N1,F1).
在我的蹩脚笔记本电脑上,我获得了call_semidet/1
的以下运行时间:
SICStus SWI answer/total fiborig: 0.090s/27.220s 1.332s/1071.380s fibmin: 0.080s/ 0.110s 1.201s/ 1.506s
取两者的总和来获得N
的运行时。
请注意,SWI的实现仅使用Prolog编写,而 SICStus部分在C中,部分在Prolog中。因此,当将SWI(实际上是@mat的)clpfd移植到 SICStus,速度可能相当。
还有很多事情需要优化。想想索引,以及
处理“计数器”,N1
,N2
,F #>= N-1
。
此外,您的原始程序可以改进很多。例如, 您不必要地发布约束{{1}}三次!
答案 1 :(得分:5)
如果您只对第一个解决方案感兴趣或知道最多只有一个解决方案,您可以使用once/1
提交该解决方案:
?- once(fib(X, 377)).
+1使用CLP(FD)作为低级算术的声明性替代。您的版本可以在所有方向上使用,而基于原始整数算术的版本则不能。
答案 2 :(得分:4)
我用另一个定义玩了一下,我用标准算法写了并且故意翻译成CLP(FD)这个问题。
我的普通Prolog定义是
fibo(1, 1,0).
fibo(2, 2,1).
fibo(N, F,A) :- N > 2, M is N -1, fibo(M, A,B), F is A+B.
一旦翻译,因为它在反向模式下花费的时间太长(或者没有终止,不知道), 我试图添加更多约束(并移动它们)以查看“向后”计算终止的位置:
fibo(1, 1,0).
fibo(2, 2,1).
fibo(N, F,A) :-
N #> 2,
M #= N -1,
M #>= 0, % added
A #>= 0, % added
B #< A, % added - this is key
F #= A+B,
fibo(M, A,B). % moved - this is key
添加B #< A
并在最后一次调用时移动递归后,现在可以正常工作。
?- time(fibo(U,377,Y)).
% 77,005 inferences, 0.032 CPU in 0.033 seconds (99% CPU, 2371149 Lips)
U = 13,
Y = 233 ;
% 37,389 inferences, 0.023 CPU in 0.023 seconds (100% CPU, 1651757 Lips)
false.
编辑要考虑基于0的序列,请添加一个事实
fibo(0,0,_).
也许这解释了最后一个论点的作用:它是一个累加器。