我正在尝试编写一个谓词,递归地找到某个数字的n次幂 [A ^ n = A * A ^(n-1)]并使用快捷键A ^(2n)= A ^ n * A ^ n。
到目前为止,这是解决方案。
p(_,0,1):-!.
p(A,N,R):-N mod 2=0,!,N1=N/2,p(A,N1,R1),R=R1*R1.
p(A,N,R):-N1=N-1,p(A,N1,R1),R=R1*A.
现在我想让这个尾递归。我可以为简单的情况做尾部,例如没有快捷方式的阶乘和幂(通过添加累加器),但这个很难。
非常感谢任何帮助!
答案 0 :(得分:3)
看起来它毕竟是可能的,只是从另一端开始:
pow(A,N,R) :-
pow(A,N,A,1,R).
pow(_,N,R,N,R) :- !.
pow(A,N,Acc,M,R) :-
M =< N div 2, !,
M1 is M*2,
NewAcc is Acc * Acc,
pow(A,N,NewAcc,M1,R).
pow(A,N,Acc,M,R) :-
M < N,
M1 is M+1,
NewAcc is A * Acc,
pow(A,N,NewAcc,M1,R).
它将快捷方式应用于小于N的2的最高功率,这无疑与您的算法正在做的不同。
答案 1 :(得分:2)
鲍里斯是正确的,他的算法所做的与原始算法不同。但是如果你真的想要,你可以在这里重现它:
注意您可以从数字的二进制表示中确定操作的顺序。设N=7
,然后二进制N=111
,表示为N=7~111
。
现在您可以在原始算法中看到该方案:
N Op N'
7~111 Mul 6~110 (= zero last bit)
6~110 Squ 3~011 (= shift right)
3~011 Mul 2~010
2~010 Squ 1~001
1~001 Base
考虑到由于算法的递归性质,这些步骤是从上到下执行的,您得到Base - Squ - Mul - Squ - Mul = ((A*A)*A)*((A*A)*A))*A = A**7
将此与鲍里斯算法对比:
N Op N'
1~001 Squ 2~010 (=shift left)
2~010 Squ 4~100 (=shift left)
4~100 Mul 5~101 (=add one)
5~101 Mul 6~110 (=add one)
6~110 Mul 7~111 (=add one)
所以这个首先完成所有的移位,而原来考虑每个位除了N的第一个,从右到左依次“排队”(因为自下而上)Mul, Squ
如果位如果未设置,则设置为Squ
。
要重现此行为(效率更高,因为您永远不会进行比正方形更简单的乘法),您可以从二进制的N
开始并执行以下操作(此处通常为伪代码,对您来说很容易翻译成prolog):
Acc=A
for i in (reverse(tail(bits(N)))):
Acc*=Acc
if i==1:
Acc*=A
这是N>=1
。 N=0
是一个特例,必须单独处理。
我很确定这是正确的。如果您有疑问,那么只需考虑您的原始算法:测试mod 2 == 0
与测试最后一位是否为零相同。如果不是,那么减去一个与将最后一位归零相同,而加倍和减半只是在二进制中向左或向右移动。