在纯 Prolog 中通过 CLP(FD) 反转函数

问题描述

让我们使用这个函数

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

gcd(Tier,G):-
  Tier1 #= Tier - 1,Tier1 #>= 0,zcompare(Order,B),gcd(Order,Tier1,G).
  
gcd(=,G,G).
gcd(>,Tier,G):-
  A1 #= A - B,gcd(Tier,A1,G).
gcd(<,G):-
  B1 #= B - A,B1,G).

因此,当您想获得分层枚举时,我会写:

?- between(1,3,Tier),1),write(B/A),nl,fail; true.
1/1
1/2
2/1
1/3
2/3
3/2
3/1
true.
,

我首先尝试从字面上翻译 GCD 函数 成 Prolog 代码。第一个子句是当 a ≠ b 为假时, 这意味着我们可以终止该函数。否则我们 递归到两种情况:

euclid(A,A).
euclid(A,R) :- A #< B,C #= B-A,euclid(A,C,R).
euclid(A,R) :- A #> B,C #= A-B,euclid(C,R).

我们可以测试,似乎工作正常,除了它有选择点。 但选择点是我们必须付出的代价 使用 CLP(FD) 和编程纯 Prolog,无需剪切:

?- euclid(17,13,X).
X = 1 ;
No

但是使用 euclid/3 进行枚举不是很令人满意,
结果只是GCD函数的一个执行分支:

?- euclid(A,1).
A = 1,B = 1 ;
A = 1,B = 2 ;
A = 1,B = 3 ;
A = 1,B = 4 ;

现在我们可以执行以下操作并通过 GCD 对路径 P 进行编码 一个二进制数的函数。当我们终止路径时,路径将是 P=1。 否则我们使用 P 的低位来编码剩下的两个中的哪一个 选择了 GCD 的条款:

euclid(A,1,P,P #= 2*Q,Q #> 0,Q,P #= 2*Q+1,R).

生成的 Prolog 谓词确实是双向的:

?- euclid(17,X).
P = 241,X = 1 ;
No
?- euclid(A,241,1).
A = 17,B = 13 ;
No

我们也可以用它来任意枚举,虽然只能用
between/3 的帮助也许不是最有效的,但它确实有效:

?- between(1,7,P),fail; true.
1/1
2/1
1/2
3/1
2/3
3/2
1/3
Yes

编辑 04.02.2021:
哦,有趣的是,这也有效。但结果的顺序不同:

?- P #< 8,fail; true.
1/1
2/1
3/1
3/2
1/2
2/3
1/3
true.

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...