[英]Distinct Prime Partitions in Prolog
我需要列举在Prolog中将给定数字n划分为一个或多个不同素数之和的所有方法,即a + b + ... + m
例如:
给定整数n,程序应将适当的总和写入标准输出。 例如,如果n = 20,则程序可能会打印
2 + 5 + 13
2 + 7 + 11
3 + 17
7 + 13
n可以是任何数字,并且对运行时间没有限制。
这是我所拥有的:
partition(N, _, []) :- N = 0.
partition(N, [], _) :- fail.
partition(N, [IH|IT], [OH|OT]) :-
N =< 0, fail;
N >= IH, M is N-IH, OH = IH,
partition(M, [IH|IT], OT).
partition(N, [_|IT], Output) :-
N =< 0, fail;
partition(N, IT, Output).
partition(N, Output) :-
generatePrime(N,L),
partition(N, L, Output).
generatePrime(1, []) :- !.
generatePrime(N, X) :-
not(isPrime(N)), !,
Z is N-1,
generatePrime(Z,X).
generatePrime(N, [N | X]) :-
Z is N-1,
generatePrime(Z,X).
isPrime(2).
isPrime(P) :-
P > 2,
isDivisible(P, P-1).
isDivisible(P,X) :-
X > 1,
P mod X =\= 0,
isDivisible(P, X-1).
isDivisible(_, X) :-
1 is X.
目前,我尝试运行以下内容:
[?-partition(5,X)。
并且我得到重复的提示[5]和[3,2]。 当我使用较大的数字(例如n = 20)时,还有另一种类型的问题,因为我得到带有重复质数的提示,例如[2,2,2,2,2,2,2,2,2,2,2]。
我对Prolog还是很陌生,我敢肯定甚至有一种更简单的方法来解决此问题,但是我不确定代码中的错误之处。
你不远...
更大的问题是您调用partition/3
partition(5, generatePrime(5,Y), X)
partition/3
需要第二项的列表,而不是generatePrime(5, Y)
。
我建议您添加如下所示的partition/2
partition(N, Output) :-
generatePrime(N, L),
partition(N, L, Output).
并调用此版本的partition
partition(5, X)
还有其他问题,因为此调用以相同的响应返回更多时间(在X
,返回[5]
两次,返回[3,2]
两次)。
我看一下看是否有问题
-编辑-
(很抱歉,但我有understandign割伤Prolog的大问题!
), fail
和或( ;
)。
我想这是我的问题。
我已经通过以下方式修改了您的partition/3
partition(0, _, []).
partition(N, [H | It], [H | Ot]) :-
N >= H,
M is N-H,
partition(M, [H | It], Ot).
partition(N, [_ | It], O) :-
N > 0,
partition(N, It, O).
这应该避免重复列表
如果要避免在同一列表中出现重复的质数(如果因为重复质数而不接受8的[3, 3, 2]
或[2, 2, 2, 2]
,则应避免重用H
( IH
在原始代码中)以下对partition/3
调用中。
我的意思是以下条款
partition(N, [H | It], [H | Ot]) :-
N >= H,
M is N-H,
partition(M, [H | It], Ot).
应该成为
partition(N, [H | It], [H | Ot]) :-
N >= H,
M is N-H,
partition(M, It, Ot).
这是一个相对有效的答案,使用SWI-Prolog中的library(clpfd)
。
您将需要:- use_module(library(clpfd)).
在程序开始时。
partition/2
可以很简单的方式描述该谓词:
partition(N,Z) :-
findall(P, (P in 2..N, prime(P)), L),
findall(S, ordered_subset_sum(L, S, N), Z).
简而言之:找出[2,N]
所有数字P
,使它们为质数。 我们将这些素数存储在列表L
。
然后,我们使用谓词ordered_subset_sum/3
检查对L
的有序子集S
求和是否得出N
我们将有效子集存储在Z
,这是我们的输出。
ordered_subset_sum/3
ordered_subset_sum([],[],0).
ordered_subset_sum([H|T],[H|T2],N) :-
M #= N - H,
M #>= 0,
ordered_subset_sum(T,T2,M).
ordered_subset_sum([_|T],T2,N) :-
ordered_subset_sum(T,T2,N).
基本情况:空列表只能生成一个空子集; 空子集的元素之和为0
。
第二个子句:我们将H
(列表的头)保留在子集中; 因此,我们想要达到的N
的总和减少H
,得出M
如果M
为负,则我们已经知道该子集不能求和为N
; 它太大了。
第三条款:我们忽略列表的开头,允许生成子集。
prime/1
如果愿意,可以重用谓词isPrime
,例如:
prime(N) :-
indomain(N),
isPrime(N).
但我建议使用更有效的素数检查算法。 我提出以下方案,它远非最佳方案,但比您的方案效率更高。 它检查数字的素数分解是否只有一个元素(只有素数才有)。 您还可以查看Julio Di Egidio的Prime-Prolog库 。
prime(N) :-
prime_decomposition(N,[_P]).
prime_decomposition(N, Z) :-
N #> 0,
indomain(N),
prime_decomposition_ceiled_square_root(N,SN),
prime_decomposition_1(N, SN, 2, [], Z).
prime_decomposition_1(1, _, _, L, L) :- !.
prime_decomposition_1(N, SN, D, L, LF) :-
(
0 #= N mod D ->
Q #= N // D,
prime_decomposition_ceiled_square_root(Q,SQ),
prime_decomposition_1(Q, SQ, D, [D |L], LF)
;
D1 #= D+1,
(
D1 #> SN ->
LF = [N |L]
;
prime_decomposition_2(N, SN, D1, L, LF)
)
).
prime_decomposition_2(1, _, _, L, L) :- !.
prime_decomposition_2(N, SN, D, L, LF) :-
(
0 #= N mod D ->
Q #= N // D,
prime_decomposition_ceiled_square_root(Q,SQ),
prime_decomposition_2(Q, SQ, D, [D |L], LF);
D1 #= D+2,
(
D1 #> SN ->
LF = [N |L]
;
prime_decomposition_2(N, SN, D1, L, LF)
)
).
prime_decomposition_ceiled_square_root(0, 0).
prime_decomposition_ceiled_square_root(N0, Root) :-
N1 #= N0 - 1,
Max in 0..N1,
R0^2 #= Max,
Root #= Root0 + 1,
fd_sup(R0, Root0).
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.