[英]Inverting a function via CLP(FD) in pure Prolog
让我们来看看这个 function:
function gcd(a, b)
while a ≠ b
if a > b
a := a − b
else
b := b − a
return a
我们如何在纯 Prolog 中编码 gcd/3,以便它可以反转。 例如,Prolog 谓词应该计算 gcd(2,3)=1。 但是如果我们要问什么是 a, b 使得 gcd(a,b)=1,我们也会得到相同的 Prolog 谓词:
/* one while iteration */
2, 1
1, 2
/* two while iterations */
3, 1
2, 3
3, 2
1, 3
/* Etc... */
Prolog 似乎特别适合,因为它可以枚举解决方案。
此解决方案使用参数来跟踪循环的当前层:
gcd(A, B, G):-
gcd(_, A, B, G).
gcd(Tier, A, B, G):-
Tier1 #= Tier - 1,
Tier1 #>= 0,
zcompare(Order, A, B),
gcd(Order, Tier1, A, B, G).
gcd(=, 0, G, G, G).
gcd(>, Tier, A, B, G):-
A1 #= A - B,
gcd(Tier, A1, B, G).
gcd(<, Tier, A, B, G):-
B1 #= B - A,
gcd(Tier, A, B1, G).
因此,当您想获得分层枚举时,我会写:
?- between(1,3,Tier), gcd(Tier, A,B,1), write(B/A), nl, fail; true.
1/1
1/2
2/1
1/3
2/3
3/2
3/1
true.
我首先尝试将 GCD function 字面翻译成 Prolog 代码。 第一个条款适用于 a ≠ b 为假的情况,这意味着我们可以终止 function。 否则我们递归到这两种情况:
euclid(A,A,A).
euclid(A,B,R) :- A #< B, C #= B-A, euclid(A,C,R).
euclid(A,B,R) :- A #> B, C #= A-B, euclid(C,B,R).
我们可以测试,似乎工作正常,除了它有选择点。 但是选择点是我们必须为使用 CLP(FD) 和编程纯 Prolog 而不削减所付出的代价:
?- euclid(17,13,X).
X = 1 ;
No
但是使用 euclid/3 进行枚举并不是很令人满意,
结果只是 GCD function 的一个执行分支:
?- euclid(A,B,1).
A = 1,
B = 1 ;
A = 1,
B = 2 ;
A = 1,
B = 3 ;
A = 1,
B = 4 ;
现在我们可以执行以下操作,并用二进制数对通过 GCD function 的路径 P 进行编码。 当我们终止时,路径将是 P=1。 否则,我们使用 P 的低位来编码 GCD 的剩余两个子句中的哪一个被选择:
euclid(A,A,1,A).
euclid(A,B,P,R) :- A #< B, C #= B-A, P #= 2*Q, Q #> 0, euclid(A,C,Q,R).
euclid(A,B,P,R) :- A #> B, C #= A-B, P #= 2*Q+1, Q #> 0, euclid(C,B,Q,R).
生成的 Prolog 谓词确实是双向的:
?- euclid(17,13,P,X).
P = 241,
X = 1 ;
No
?- euclid(A,B,241,1).
A = 17,
B = 13 ;
No
我们也可以用它来任意枚举,虽然只能用
between/3 的帮助可能不是最有效的,但它很有效:
?- between(1,7,P), euclid(A,B,P,1), write(B/A), nl, fail; true.
1/1
2/1
1/2
3/1
2/3
3/2
1/3
Yes
编辑 04.02.2021:
哦,有趣的是,这也有效。 但结果的顺序不同:
?- P #< 8, euclid(A,B,P,1), write(B/A), nl, fail; true.
1/1
2/1
3/1
3/2
1/2
2/3
1/3
true.
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.