查找数字的所有自然因数(使用 Prolog)

Xi *_*awa 5 prolog

我想创建一个谓词 divisors(X,[Y]),如果 X>1 并且 Y 是 X 的所有除数的列表,从 X 开始一直到 1,则该谓词为 true。

我的代码现在的样子:

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]).
Run Code Online (Sandbox Code Playgroud)

但它有几个问题:

  1. 如果询问列表(例如?-divisors(10,X)),prolog 将返回错误。

  2. ?- 除数(X,[Y])。其中 [Y] 是不完整的除数列表,则为 true...


盖伊·编码器编辑

这个答案是由 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.
Run Code Online (Sandbox Code Playgroud)

Op 还指出了这个版本中的一些错误:

  1. 如果我询问像 (10,[1,2,3]) 这样的错误语句,它不会终止。

  2. 如果我询问类似 (X, [10,5,2,1]) 的语句,它会抛出错误。(-> 参数未充分初始化。)

Guy*_*der 3

虽然威廉的答案很好而且可能更快,但这里的答案更接近您所写的内容。

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), !.
Run Code Online (Sandbox Code Playgroud)

例子:

?- 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].
Run Code Online (Sandbox Code Playgroud)

编辑

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.
Run Code Online (Sandbox Code Playgroud)

第一个错误: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), !
)
Run Code Online (Sandbox Code Playgroud)

divisors_2(_,[]).

divisors_2(X,[H|T]) :-
  divides(X,H),
  divisors_2(X,T).
Run Code Online (Sandbox Code Playgroud)

它只处理分母列表而不是生成列表。

第二个错误: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), !
    )
  ).
Run Code Online (Sandbox Code Playgroud)

它检查第一个参数是否X是变量,如果是则返回false。另一种选择是生成无限的答案列表。虽然可能没有要求。