Prolog Beginner: How to make unique values for each Variable in a predicate

前端 未结 3 1396
一个人的身影
一个人的身影 2020-12-21 15:32

I have a prolog predicate:

Add( [A|B] , Answer ) :-
    ...
    ~ Add everything in the list to come up with answer
    ...

I would now lik

相关标签:
3条回答
  • 2020-12-21 15:55

    Given your description of the addUnique/2 predicate, constraint logic programming can be used to implement a solution. This is far from beginner stuff, but I'll post an explanation anyway.

    Firstly, it might be worthwhile looking up what constraint logic programming is, and an how to use an implementation (e.g., SWI-PL clpfd). Basically, constraint logic programming (particularly the finite domain solver) will allow you to specify the following constraints over your variables on the input list to addUnique/2:

    1. How the variables on your input list can bind to certain numeric values (i.e., integers from 0 up to and including a specified value)
    2. How the different variables on your input list cannot simultaneously bind to the same value (i.e., != where different)
    3. How the sum of the variables in the input list plus any numbers must add up to a specified value (i.e., the maximum value that variables can take on in 1, above).

    Together, these specifications will allow the underlying constraint solver to automatically determine what permissible values the variables can simultaneously take on given the above constraints, giving you your solutions (there may be several, one, or none).

    Here is a solution using the aforementioned constraint solver in SWI-PROLOG (the clpfd solver):

    :- use_module(library(clpfd)).  % import and use the constraint solver library
    
    addUnique([A|As], Val) :-
        unique_vars([A|As], UVs),  % determine all unique variables
        UVs ins 0..Val,            % (1) domain of all unique variables is 0 to Val
        pairwise_inequ(UVs),       % (2) all unique variables are pairwise !=
        construct_sum_constr(As, Val, A, Program),  % (3) construct the sum constraint
        Program,              % assert the sum constraint
        label(UVs).           % label domains to enumerate a solution (backtracks)
    
    % predicate to return a list of unique vars, if present
    unique_vars([], []).
    unique_vars([V|Vs], [V|Uniq]) :-
        var(V),
        \+ var_memberchk(V, Vs), !,
        unique_vars(Vs, Uniq).
    unique_vars([_|Vs], Uniq) :-
        unique_vars(Vs, Uniq).
    
    % predicate to test if a variable appears in a list (possibly including variables)
    var_memberchk(V0, [V1|_]) :- 
        V0 == V1, !.
    var_memberchk(V0, [_|V1s]) :- 
        var_memberchk(V0, V1s).
    
    % create constraints that assert each in the input list != to each other
    pairwise_inequ([]).
    pairwise_inequ([V|Vs]) :-
        map_inequ(Vs, V),
        pairwise_inequ(Vs).
    
    % predicate to pairwise assert inequality between all list members
    map_inequ([], _).
    map_inequ([V1|V1s], V0) :-
        V0 #\= V1,   % the inequality constraint
        map_inequ(V1s, V0).
    
    % predicate to construct a summation constraint, whereby all variables in the 
    % input list are constructed into a sum with +/2 and finally equated to a value
    construct_sum_constr([], Val, Sum, (Sum #= Val)).
    construct_sum_constr([V|Vs], Val, Sum, Program) :-
        construct_sum_constr(Vs, Val, (V + Sum), Program).
    

    Running this code, e.g., gives you:

    ?- addUnique([A,B,B], 6).
    A = 0,
    B = 3 ;
    A = 4,
    B = 1 ;
    A = 6,
    B = 0.
    

    ; enumerates the next solution for permissible bindings between variables. Note that A and B never take on the same value, as required, but all occurrences in the input list will always sum to 6. Another query:

     ?- addUnique([A,A,A],4).
    false.
    

    The result is failure here because no single integer could be found to bind to A that, when summed up, totaled 4, whereas:

     ?- addUnique([A,A,A,A],4).
    A = 1.
    

    ...as expected. Also, you wanted to try:

    ?- addUnique([A,B,C,D],4).
    false.
    

    Again, the result is failure here because all the variables A, B, C and D were all asserted to be different, and cannot all bind to 1.

    EDIT ps. ony also wanted to try:

    ?- addUnique([A,A,A,1],4).
    A = 1.
    

    A simple modification to the code above ensures that only variables are used when calling ins to assert domains (and not any numbers in the input list).

    0 讨论(0)
  • 2020-12-21 16:00

    Khm... You should understand that doStuff(A,B,C,D) and doStuff(A,A,B,B) means. First is going to unify values A .. D with appropriate values which makes doStuff/4 reachable goal. And second is equal to A=B, C=D, doStuff(A,B,C,D) and doStuff(A,B,C,D), A=B, C=D (but last variant probably will cause backtracking). So I hope you understand that unique/1 shouldn't be done inside doStuff/4, because it's outside restriction. So you shoulad use doStuff(A,B,C,D), unique([A,B,C,D]) and doStuff(A,A,B,B), unique([A,B]).

    I wonder how you read A is not B... Anyway you can define unique/1 as

    not_unique([H|T]):- member(H, T) ; not_unique(T).
    unique(L):- not(not_unique(L)).
    
    0 讨论(0)
  • 2020-12-21 16:08

    This is the solution that I came up with. It will only assign the input to be numbers less than ten but works great for that!

    addUnique( A, Answer ) :- 
        used(A,[0,1,2,3,4,5,6,7,8,9],_),
        add(A,Answer).
    
    add( [A|B] , Answer ) :-
        ~ Add everything in the list to come up with answer ~.
    
    
    % ================================
    % Ensures that all variables are unique.  
    % ================================
    
    % Base case: Assigned variables unique values
    used([], Nin, Nin).
    
    % Have already assigned a value to this variable
    used([A|B], Nin, Nout) :-
            integer(A),
            helper(B,Nin,Nout).
    
    % Have not assigned a value to this variable yet
    % Assign it and remove it from the list.  
    used( [A|B] , Nin, Nout) :-
            member(A,Nin),
            delete(Nin,A,Temp),
            helper(B,Temp,Nout).
    
    0 讨论(0)
提交回复
热议问题