繁体   English   中英

Prolog中不同的主分区

[英]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] ,则应避免重用HIH在原始代码中)以下对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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM