简体   繁体   English

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

[英]Inverting a function via CLP(FD) in pure Prolog

Lets take this function:让我们来看看这个 function:

function gcd(a, b)
   while a ≠ b
     if a > b
        a := a − b
     else
        b := b − a
   return a 

How would we code gcd/3 in pure Prolog, so that it can be inverted.我们如何在纯 Prolog 中编码 gcd/3,以便它可以反转。 The Prolog predicate should for example compute gcd(2,3)=1.例如,Prolog 谓词应该计算 gcd(2,3)=1。 But if we would ask what are the a, b such that gcd(a,b)=1, we would also get by the same Prolog predicate:但是如果我们要问什么是 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 seems to be especially suited since it can enumerate solutions. Prolog 似乎特别适合,因为它可以枚举解决方案。

This solution uses an argument to keep track of the current tier of the loop:此解决方案使用参数来跟踪循环的当前层:

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

So when you want to get a tiered enumeration you my write:因此,当您想获得分层枚举时,我会写:

?- 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.

I first tried to literally translate the GCD function into a Prolog code.我首先尝试将 GCD function 字面翻译成 Prolog 代码。 The first clause is for when a ≠ b is false, which means we can terminate the function.第一个条款适用于 a ≠ b 为假的情况,这意味着我们可以终止 function。 Otherwise we recurse into the two cases:否则我们递归到这两种情况:

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

We can test, seems to work fine, except it has choice points.我们可以测试,似乎工作正常,除了它有选择点。 But the choice points are kind of the price we have to pay for using CLP(FD) and programming pure Prolog without cut:但是选择点是我们必须为使用 CLP(FD) 和编程纯 Prolog 而不削减所付出的代价:

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

But using euclid/3 for enumeration isn't very satisfactory,但是使用 euclid/3 进行枚举并不是很令人满意,
the result is only one execution branch of the GCD function:结果只是 GCD function 的一个执行分支:

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

Now we can do the following and encode a path P through the GCD function by a binary number.现在我们可以执行以下操作,并用二进制数对通过 GCD function 的路径 P 进行编码。 When we terminate the path will be P=1.当我们终止时,路径将是 P=1。 Otherwise we use the lower bit of P to encode which of the two remaining clauses of the GCD were chosen:否则,我们使用 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).

The resulting Prolog predicate is indeed bidirectional:生成的 Prolog 谓词确实是双向的:

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

We can also use it to arbitrarily enumerate, although only with我们也可以用它来任意枚举,虽然只能用
the help of between/3 and maybe not the most efficient, but it wurks: 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

Edit 04.02.2021:编辑 04.02.2021:
Oh, interesting, this works also.哦,有趣的是,这也有效。 But the result is differently ordered:但结果的顺序不同:

?- 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM