我想创建一个谓词除数(X,[Y]),如果 X> 1和Y是X的所有除数的列表,从X开始,一直到1。
我现在的代码如下:
divisors(1,[1]).
divisors(X,[Y,Z|Ys]) :-
X>0,
Y is X,
Y>Z,
divides(X,[Z|Ys]).
divides(X,[Y,Z|Ys]) :-
Y>Z,
0 is X mod Y,
divides(X,[Z|Ys]).
divides(X,[1]).
但是它有几个问题:
序言将返回错误(例如?-除数(10,X)。)
?-除数(X,[Y])。其中[Y]是除数的不完整列表是真的...
由Guy Coder编辑
此答案是由OP提供的,并在下面的评论中发布。
移动到这里以便其他人可以看到。
divisors(X,R) :-
X > 1,
divisors(X,1,[],R).
divisors(X,D,R,R):-
D>X.
divisors(N,D0,R0,R) :-
divisors_0(N,D0,R0,R1),
D is D0 + 1,
divisors(N,D,R1,R).
divisors_0(N,D,R0,[D|R0]) :-
divides(N,D).
divisors_0(N,D,R0,R0).
divides(N,D) :-
0 is N mod D.
Op还注意到此版本中的一些错误:
如果我问一个错误的语句(10,[1,2,3]),它不会终止。
如果我问类似(X,[10,5,2,1])的语句,则会引发错误。 (->参数未充分初始化。)
答案 0 :(得分:3)
威廉(William)的answer很好,而这里的回答可能更接近您所写的内容。
divides(N,D) :-
0 is N mod D.
divisors_0(N,D,R0,[D|R0]) :-
divides(N,D).
divisors_0(N,D,R0,R0) :-
\+ divides(N,D).
divisors(_,0,R,R).
divisors(N,D0,R0,R) :-
divisors_0(N,D0,R0,R1),
D is D0 - 1,
divisors(N,D,R1,R).
divisors(X,R) :-
X > 1,
divisors(X,X,[],R), !.
示例:
?- between(1,15,N), divisors(N,Rs).
N = 2,
Rs = [1, 2] ;
N = 3,
Rs = [1, 3] ;
N = 4,
Rs = [1, 2, 4] ;
N = 5,
Rs = [1, 5] ;
N = 6,
Rs = [1, 2, 3, 6] ;
N = 7,
Rs = [1, 7] ;
N = 8,
Rs = [1, 2, 4, 8] ;
N = 9,
Rs = [1, 3, 9] ;
N = 10,
Rs = [1, 2, 5, 10] ;
N = 11,
Rs = [1, 11] ;
N = 12,
Rs = [1, 2, 3, 4, 6, 12] ;
N = 13,
Rs = [1, 13] ;
N = 14,
Rs = [1, 2, 7, 14] ;
N = 15,
Rs = [1, 3, 5, 15].
编辑
OP修改了他们的代码,请参阅有问题的更新并出现一些错误。
此版本解决了这些错误。
divisors(X,R) :-
(
var(X)
->
false
;
(
var(R)
->
X > 1,
divisors(X,1,[],R)
;
divisors_2(X,R), !
)
).
divisors_2(_,[]).
divisors_2(X,[H|T]) :-
divides(X,H),
divisors_2(X,T).
divisors(X,D,R,R):-
D>X.
divisors(N,D0,R0,R) :-
divisors_0(N,D0,R0,R1),
D is D0 + 1,
divisors(N,D,R1,R).
divisors_0(N,D,R0,[D|R0]) :-
divides(N,D).
divisors_0(_,_,R0,R0).
divides(N,D) :-
0 is N mod D.
第一个错误:It doesn't terminate if I ask a wrong statement like divisors(10,[1,2,3]).
通过添加除数/ 2来固定
(
var(R)
->
X > 1,
divisors(X,1,[],R)
;
divisors_2(X,R), !
)
和
divisors_2(_,[]).
divisors_2(X,[H|T]) :-
divides(X,H),
divisors_2(X,T).
仅处理分母列表而不是生成列表。
第二个错误:It throws an error if I ask a statement like divisors(X, [10,5,2,1]). (-> Arguments are not sufficiently initialized.)
divisor/2
来解决
divisors(X,R) :-
(
var(X)
->
false
;
(
var(R)
->
X > 1,
divisors(X,1,[],R)
;
divisors_2(X,R), !
)
).
检查第一个参数X
是否为变量,如果是,则仅返回false
。另一种选择是生成无限的答案列表。可能的话,没有要求。
答案 1 :(得分:2)
在Prolog中,使用回溯并为同一查询提出多种解决方案是很普遍的。因此,我们可以构造一个谓词,将第二个参数与所有除数统一起来,而不是构造一个除法器列表。例如:
divisor(N, D) :-
between(1, N, D),
0 is N mod D.
然后产生:
?- divisor(12, N).
N = 1 ;
N = 2 ;
N = 3 ;
N = 4 ;
N = 6 ;
N = 12.
上面的算法是 O(n)算法:我们扫描除数,该除数与要获得其除数的项目的值成线性关系。我们可以通过扫描到√n轻松地将其改进为 O(√n),并且每次都产生除数(当然,如果是除数),和共同除数,例如:
emitco(D, _, D).
emitco(D, C, C) :-
dif(D, C).
divisor(N, R) :-
UB is floor(sqrt(N)),
between(1, UB, D),
0 is N mod D,
C is N / D,
emitco(D, C, R).
这仍然会产生正确的答案,但是顺序就像一个收敛的交替序列:
?- divisor(12, N).
N = 1 ;
N = 12 ;
N = 2 ;
N = 6 ;
N = 3 ;
N = 4.
?- divisor(16, N).
N = 1 ;
N = 16 ;
N = 2 ;
N = 8 ;
N = 4 ;
false.
我们可以使用findall/3
[swi-doc]或setof/3
[swi-doc]获得除数的列表。 setof/3
甚至可以对除数进行排序,因此我们可以根据divisors/2
来实现divisor/2
:
divisors(N, Ds) :-
setof(D, divisor(N, D), Ds).
例如:
?- divisors(2, N).
N = [1, 2].
?- divisors(3, N).
N = [1, 3].
?- divisors(5, N).
N = [1, 5].
?- divisors(12, N).
N = [1, 2, 3, 4, 6, 12].
?- divisors(15, N).
N = [1, 3, 5, 15].
我们可以使用reverse/2
来反转结果。