Search code examples
prolog

How can I get partition?


I am new on prolog and I want to list the n-ary partitions of a number in prolog using backtracking. The result must be something like this:

?- nary(3,9,P).
P = [9] ? ;
P = [3,3,3] ? ;
P = [3,3,1,1,1] ? ;
P = [3,1,1,1,1,1,1] ? ;
P = [1,1,1,1,1,1,1,1,1] ? ;
no

Do you have any ideas of how to do it? Lots of thanks.


Solution

  • Assuming that you mean n-ary partitions as described in Characterizing the Number of m-ary Partitions Modulo m:

    ... These are partitions of an integer n wherein each part is a power of a fixed integer m >= 2. ... As an example, note that there are five 3-ary partitions of n=9: 9, 3+3+3, 3+3+1+1+1, 3+1+1+1+1+1+1, 1+1+1+1+1+1+1+1+1. ...

    Note, that in the linked Paper the m corresponds to the n in your question. Building on base_limit_powers/3 from this answer, I propose to keep using DCGs and CLP(FD). Let's start with a name for the predicate and the DCG, e.g. nary_partitions_of/3 (Note that I swapped the second and third arguments of your predicate nary/3 to more easily obtain a nice declarative name) and partitions_//4.

    The second argument is a list (Partitions), consisting of powers of the first argument (N), that add up to the number that is the third argument (Int). The first number has to be greater or equal to 2 per definition (see above), hence the sum has to be as well. Since you want the powers in the list in descending order, the predicate reverse/2 from library(lists) can be used to reorder the list of powers as described by base_limit_powers/3 and have partitions_//4 start with the largest number. Because of N and Int being larger than 1 the list of powers is not empty and its corresponding reversed list can be written in head and tail notation ([FRP|RPs], read F irst of R eversed P owers and rest of R eversed P ower s), thereby providing easy access to the first (and largest) number (FRP) that is to be used as second argument of partitions_//4. At the time partitions_//4 is called (using phrase/2), the list Partitions is yet to be described, therefore the sum of its numbers is still 0. Putting all this together the relation nary_partitions_of/3 can be defined like so:

    nary_partitions_of(N,Partitions,Int) :-
        N #> 1,
        Int #> 1,
        base_limit_powers(N,Int,Powers),
        reverse(Powers,[FRP|RPs]),
        phrase(partitions_([FRP|RPs],FRP,0,Int),Partitions).
    

    Then the n-ary partitions can be described by a DCG like so:

    partitions_(_Powers,_Last,Int,Int) --> % If the sum and the integer are equal
        [].                                % the list is finished
    partitions_(Powers,Last,Sum0,Int) -->  % otherwise
        {Sum0 #< Int},                     % the sum is smaller than the integer and
        {P #=< Last},                      % the power to be added is less or equal to the last power in the partition and
        {member(P,Powers)},                % the power is from the list of powers and
        {Sum1 #= Sum0 + P},                % adds to the sum of powers so far and
        [P],                               % the power is in the list of partitions and
        partitions_(Powers,P,Sum1,Int).    % the remainder of the partition-list is described recursively
    

    With this definition your example query produces the desired answers:

       ?- nary_partitions_of(3,P,9).
    P = [9] ? ;
    P = [3,3,3] ? ;
    P = [3,3,1,1,1] ? ;
    P = [3,1,1,1,1,1,1] ? ;
    P = [1,1,1,1,1,1,1,1,1] ? ;
    no
    

    Due to the use of CLP(FD) you can also use nary_partitions_of/3 to ask different types of questions, e.g. to check if a given list is a partition of some N and Int, however this query loops after producing the answers:

       ?- nary_partitions_of(N,[9],Int).
    Int = N = 9 ? ;
    Int = 9,
    N = 3 ? ;
    % loop here
    

    But due to the use of CLP(FD) that can be remedied by constraining the range of N like so:

       ?- N in 2..5, nary_partitions_of(N,[9],Int).
    Int = 9,
    N = 3 ? ;
    no
    
       ?- N in 2..5, nary_partitions_of(N,[3,3,3],Int).
    Int = 9,
    N = 3 ? ;
    no
    
       ?- N in 2..5, nary_partitions_of(N,[3,3,1,1,1],Int).
    Int = 9,
    N = 3 ? ;
    no
    
       ?- N in 2..5, nary_partitions_of(N,[3,3,1,1],Int).
    Int = 8,
    N = 3 ? ;
    no
    
       ?- N in 2..5, nary_partitions_of(N,[3,3,4],Int).
    no
    

    You can also look for n-ary partitions of some number, e.g. 9 without specifying N:

       ?- N in 2..6, nary_partitions_of(N,P,9).
    N = 4,
    P = [4,4,1] ? ;
    N = 6,
    P = [6,1,1,1] ? ;
    N = 5,
    P = [5,1,1,1,1] ? ;
    N = 4,
    P = [4,1,1,1,1,1] ? ;
    P = [1,1,1,1,1,1,1,1,1],
    N in 3..6,                      % residual goal
    N^2#=_A,                        % residual goal
    _A in 10..36 ? ;                % residual goal
    .
    .
    .
    

    However this leads to some residual goals in the answers (see CLP(FD) documentation for details). You can again remedy this by constraining N, this time also labeling it:

       ?- N in 2..6, nary_partitions_of(N,P,9), label([N]).
    N = 4,
    P = [4,4,1] ? ;
    N = 6,
    P = [6,1,1,1] ? ;
    N = 5,
    P = [5,1,1,1,1] ? ;
    N = 4,
    P = [4,1,1,1,1,1] ? ;
    N = 4,
    P = [1,1,1,1,1,1,1,1,1] ? ;
    N = 5,
    P = [1,1,1,1,1,1,1,1,1] ? ;
    N = 6,
    P = [1,1,1,1,1,1,1,1,1] ? ;
    N = 3,
    P = [9] ? ;
    N = 3,
    P = [3,3,3] ? ;
    N = 3,
    P = [3,3,1,1,1] ? ;
    N = 3,
    P = [3,1,1,1,1,1,1] ? ;
    N = 3,
    P = [1,1,1,1,1,1,1,1,1] ? ;
    N = 2,
    P = [8,1] ? ;
    N = 2,
    P = [4,4,1] ? ;
    N = 2,
    P = [4,2,2,1] ? ;
    N = 2,
    P = [4,2,1,1,1] ? ;
    N = 2,
    P = [4,1,1,1,1,1] ? ;
    N = 2,
    P = [2,2,2,2,1] ? ;
    N = 2,
    P = [2,2,2,1,1,1] ? ;
    N = 2,
    P = [2,2,1,1,1,1,1] ? ;
    N = 2,
    P = [2,1,1,1,1,1,1,1] ? ;
    N = 2,
    P = [1,1,1,1,1,1,1,1,1] ? ;
    no