prolog - 找到一个数的所有自然除数(使用 Prolog)
问题描述
我想创建一个谓词除数(X,[Y]),如果 X>1 并且 Y 是 X 的所有除数的列表,则该谓词除数(X,[Y])是从 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]).
但是它有几个问题:
如果询问列表,prolog 会返回错误(例如 ?-divisors(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]) 这样的语句,它会引发错误。(-> 参数没有充分初始化。)
解决方案
在 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
来扭转这个结果。
推荐阅读
- java - DOM 中两个不同元素的 Xpath OR 条件
- php - socket.io 可以在本地连接,但不能在真实的服务器上连接
- java - Java JSON POST 格式不正确
- swift3 - 即使在不同的位置,位置也从未更新,可能是什么问题
- java - 未使用 JPA 检索日期字段
- c# - 仅当 DTS 完成其工作时才在 C# 中继续任务
- python - Python - 将 JSON 文件中的重音字符更改为常规字符
- python - 在一行中构建 for 循环并收到错误的输出,即 Nonetype
- c# - 从 Outlook 桌面应用程序打开邮件时不显示背景图像
- c - &A[1] 应该与 A 本身的地址相同吗?