我已经在一些prolog代码上苦苦挣扎了几天,但我无法找到解决方法。我正在尝试编写扩展的欧几里德算法,并在以下位置找到值p
和s
:
a*p + b*s = gcd(a,b)
等式,这是我试过的:`
common(X,X,X,_,_,_,_,_,_).
common(0,Y,Y,_,_,_,_,_,_).
common(X,0,X,_,_,_,_,_,_).
common(X,Y,_,1,0,L1,L2,SF,TF):-
append(L1,1,[H]),
append(L2,0,[A]),
SF is H ,
TF is A,
common(X,Y,_,0,1,[H],[A],SF,TF).
common(X,Y,_,0,1,L1,L2,SF,TF):-
append(L1,0,[_,S2]),
append(L2,1,[_,T2]),
Q is truncate(X/Y),
S is 1-Q*0,T is 0-Q*1 ,
common(X,Y,_,S,T,[S2,S],
[T2,T],SF,TF).
common(X,Y,N,S,T,[S1,S2],[T1,T2],SF,TF):-
Q is truncate(X/Y),
K is X-(Y*Q),
si_finder(S1,S2,Q,SF),
ti_finder(T1,T2,Q,TF),
common(Y,K,N,S,T,[S2,S],[T2,T],SF,TF).
si_finder(PP,P,Q,C):- C is PP - Q*P.
ti_finder(P2,P1,QA,C2):- C2 is P2 - QA*P1.
经过一番搜索后,我发现s和p系数从1和0开始,它们的第二个值分别为0和1.然后它继续使用我在si_finder和ti_finder谓词中完成的模式。常见谓词是我试图递归控制模式的地方。但是,常见谓词在每次调用时都会返回false。任何人都可以帮我在Prolog中实现这个算法。
提前致谢。
答案 0 :(得分:2)
首先让我们考虑谓词的arity。显然,您希望将数字A和B以及Bézout系数P和S作为参数。由于该算法无论如何都在计算GCD,因此将其作为参数也是恰当的。这让我们留下了arity 5.当我们谈论扩展的欧几里德算法时,让我们'调用谓词eeuclid/5
。接下来,考虑一个例子:让我们使用算法来计算A = 242和B = 69的P,S和GCD:
quotient (Q) | remainder (B1) | P | S
-------------+-------------------+-------+-------
| 242 | 1 | 0
| 69 | 0 | 1
242/69 = 3 | 242 − 3*69 = 35 | 1 | -3
69/35 = 1 | 69 − 1*35 = 34 | -1 | 4
35/34 = 1 | 35 − 1*34 = 1 | 2 | -7
34/1 = 34 | 34 − 34*1 = 0 | -69 | 242
我们可以观察到以下内容:
如果余数变为0
最后一行之前的行包含剩余列中的GCD(在此示例中为1)和P和S列中的Bézout系数(在此示例中为2和-7)
< / LI>商是从前一个余数计算出来的。因此,在下一次迭代中,A变为B,B变为B1。
P和S是从它们各自的前辈计算出来的。例如:P3 = P1-3 * P2 = 1-3 * 0 = 1且S3 = S1-3 * S2 = 0-3 * 1 = -3。并且因为它具有前两个P和S的足够,所以我们也可以将它们作为成对传递,例如, P1-P2和S1-S2。
算法从对P:1-0和S:0-1
算法以较大的数字
把所有这些放在一起,调用谓词必须确保A是一个更大的数字,除了它的五个参数之外,它还必须将起始对1-0和0-1传递给描述实际的谓词关系,这里a_b_p_s_/7
:
:- use_module(library(clpfd)).
eeuclid(A,B,P,S,GCD) :-
A #>= B,
GCD #= A*P + B*S, % <- new
a_b_p_s_(A,B,P,S,1-0,0-1,GCD).
eeuclid(A,B,P,S,GCD) :-
A #< B,
GCD #= A*P + B*S, % <- new
a_b_p_s_(B,A,S,P,1-0,0-1,GCD).
a_b_p_s_/7
的第一条规则描述了基本情况,其中B = 0且算法停止。然后A是GCD,P1,S1是Bézout系数。否则,计算商Q,余数B1和P和S的新值,并使用这些新值调用a_b_p_s_/7
:
a_b_p_s_(A,0,P1,S1,P1-_P2,S1-_S2,A).
a_b_p_s_(A,B,P,S,P1-P2,S1-S2,GCD) :-
B #> 0,
A #> B, % <- new
Q #= A/B,
B1 #= A mod B,
P3 #= P1-(Q*P2),
S3 #= S1-(Q*S2),
a_b_p_s_(B,B1,P,S,P2-P3,S2-S3,GCD).
使用上面的示例查询会产生所需的结果:
?- eeuclid(242,69,P,S,GCD).
P = 2,
S = -7,
GCD = 1 ;
false.
确实:gcd(242,69)= 1 = 2 * 242 - 7 * 69
编辑:我想建议添加两个约束。首先是在a_b_p_s_/7
之前调用Bézout的身份,然后在A #> B
的第一个目标之后调用a_b_p_s_/7
。我编辑了上面的谓词并标记了新的目标。这些添加使eeuclid/5
更加通用。例如,您可以询问数字A和B具有Bézout系数2和-7和1作为gcd。此查询没有唯一的答案,Prolog将为您提供每个潜在解决方案的剩余目标。但是,您可以要求A和B的有限范围,比如介于0和50之间,然后使用label/1
来获取实际数字:
?- [A,B] ins 0..50, eeuclid(A,B,2,-7,1), label([A,B]).
A = 18,
B = 5 ;
A = 25,
B = 7 ;
A = 32,
B = 9 ;
A = 39,
B = 11 ;
A = 46,
B = 13 ;
false. % <- previously loop here
如果没有新添加的约束,查询将不会在第五个解决方案后终止。然而,随着Prolog能够确定的新约束,在0到50之间没有更多的解决方案。