Solving a puzzle in Prolog about time constraints

家住魔仙堡 提交于 2020-03-03 07:35:27

问题


Stuck on a Prolog problem. I know the answer (because I did it on paper first), but I cannot figure out how to get Prolog to come up with the answer.

Problem:

Bill eats a snack every night, having a different fruit and different nuts each night. From the statements below, identify what Bill had for a snack for each weeknight last week.

a) The apple was eaten later in the week than the mango.

b) The banana was eaten later in the week than both the almonds and peanuts, but earlier in the week than the pear.

c) The cashews were eaten earlier in the week than both the banana and the apricot, but later in the week than the peanuts.

d) The pecans were not eaten the evening after the almonds.

e) Bill ate walnuts one night.

Note that the problem is about 5 weeknights (Monday through Friday), and mentions 5 kinds of fruit and 5 kinds of nuts. Your program should solve the problem and print out the solution, which will be a set of 5 triples like (Monday, apple, pecans), ... (Friday, mango, walnuts).

Clearly, these are not the correct answers, but just values to show you what the solution will look like.

Code so far:

before_in_week(X, Y, Days) :-
    nth1(Xi, Days, X),
    nth1(Yi, Days, Y),
    Xi < Yi.

print_solve([Head|Tail]) :-
    write(Head),
    nl,
    print_solve(Tail).  

solve(A) :-
  % all triples
  A = [[day1, fruit1, nut1],
       [day2, fruit2, nut2],
       [day3, fruit3, nut3],
       [day4, fruit4, nut4],
       [day5, fruit5, nut5]],

  Days = [monday, tuesday, wednesday, thursday, friday],
  Days = [day1, day2, day3, day4, day5],

  Fruits = [apple,banana,pear,mango,apricot],
  permutation(Fruits, [fruit1, fruit2, fruit3, fruit4, fruit5]),

  Nuts = [almonds,pecans,cashews,peanuts,walnuts],
  permutation(Nuts, [nut1, nut2, nut3, nut4, nut5]),

  % clue 1 - mango before apple
  fruit5 \= mango,
  member([C1,mango,_], A),
  member([C2,apple,_], A), before_in_week(C1,C2,Days),
  % clue 2 - banana after almonds and peanuts, but before pear
  fruit5 \= banana,
  member([C1,banana,_], A),
  member([C2,pear,_], A), before_in_week(C1,C2,Days),
  member([C3,_,almonds], A), before_in_week(C3,C1,Days),
  member([C4,_,peanuts], A), before_in_week(C4,C1,Days),
  % clue 3 - cashews before banana and apricot, but after peanuts
  nut5 \= peanuts,
  member([C1,_,cashews], A),
  member([C2,_,peanuts], A), before_in_week(C1,C2,Days),
  member([C3,banana,_], A), before_in_week(C3,C1,Days),
  member([C4,apricot,_], A), before_in_week(C4,C1,Days),
  % clue 4 - pecans not night after almonds
  nut5 \= almonds,
  % clue 5 - ate walnuts one night


  print_solve(A).

回答1:


First, there is really no need to print anything manually. Prolog's top level does this for you, if you enter the query solve(A). yet,

second, there is no solution. That is really what you are interested in. There is a very simple and very general method to narrow down the source of failure. Simply generalize away all the goals, one after the other. I like to do this by adding a * in front like so:

:- op(950, fy, *).
*_0.

solve(A) :-
  * A = [[day1, fruit1, nut1], [day2, fruit2, nut2], [day3, fruit3, nut3],
         [day4, fruit4, nut4], [day5, fruit5, nut5]],

  Days = [monday|_/*[tuesday, wednesday, thursday, friday]*/],
  Days = [day1|_/*[day2, day3, day4, day5]*/],

  * Fruits = [apple,banana,pear,mango,apricot],
  * permutation(Fruits, [fruit1, fruit2, fruit3, fruit4, fruit5]),

  * Nuts = [almonds,pecans,cashews,peanuts,walnuts],
  * permutation(Nuts, [nut1, nut2, nut3, nut4, nut5]),

  % clue 1 - mango before apple
  * fruit5 \= mango,
  * member([C1,mango,_], A),
  * member([C2,apple,_], A), before_in_week(C1,C2,Days),
  % clue 2 - banana after almonds and peanuts, but before pear
  * fruit5 \= banana,
  * member([C1,banana,_], A),
  * member([C2,pear,_], A), before_in_week(C1,C2,Days),
  * member([C3,_,almonds], A), before_in_week(C3,C1,Days),
  * member([C4,_,peanuts], A), before_in_week(C4,C1,Days),
  % clue 3 - cashews before banana and apricot, but after peanuts
  * nut5 \= peanuts,
  * member([C1,_,cashews], A),
  * member([C2,_,peanuts], A), before_in_week(C1,C2,Days),
  * member([C3,banana,_], A), before_in_week(C3,C1,Days),
  * member([C4,apricot,_], A), before_in_week(C4,C1,Days),
  % clue 4 - pecans not night after almonds
  * nut5 \= almonds.
  % clue 5 - ate walnuts one night

In this program slice, which is a generalization of your original program, it boils down to the inability to succeed for

Days = [monday|_], Days = [day1|_]

You have to change there something. day1 is a constant, it rather should be a variable.

Later, replace all X \= const by dif(X, const).




回答2:


Your biggest issue is that you are using atoms (fruit4) but you should use variables (Fruit4). Note the capitalization at the start.

Also, you're doing a permutation that you don't need. Prolog does all of the permutations you need via backtracking. That's what make Prolog such an interesting language.

Try this code:

?- solve(A),print_solve(A).

solve(A) :-
    A = [[monday,_,_],[tuesday,_,_],[wednesday,_,_],[thursday,_,_],[friday,_,_]],
%clue 1 - mango before apple
    before([_,mango,_],[_,apple,_],A),
% clue 2 - banana after almonds and peanuts, but before pear
    before([_,_,almonds],[_,banana,_],A),
    before([_,_,peanuts],[_,banana,_],A),
    before([_,banana,_],[_,pear,_],A),
% clue 3 - cashews before banana and apricot, but after peanuts
    before([_,_,cashews],[_,banana,_],A),
    before([_,_,cashews],[_,apricot,_],A),
    before([_,_,peanuts],[_,_,cashews],A),
% clue 4 - pecans not night after almonds
    append(H,[[_,_,almonds],[_,_,_]|T],A),
    (member([_,_,pecans],H);member([_,_,pecans],T)),
% clue 5 - ate walnuts one night
    member([_,_,walnuts],A),
    true.

print_solve([]).
print_solve([Head|Tail]) :-
    write(Head),
    nl,
    print_solve(Tail). 

before(X,Y,Days) :-
    append(A,B,Days),
    member(X,A),
    member(Y,B).

That gives me:

[monday, mango, peanuts]
[tuesday, apple, cashews]
[wednesday, apricot, almonds]
[thursday, banana, walnuts]
[friday, pear, pecans]
Yes.



回答3:


The puzzle can be easily solved by means of one of workhorses of Prolog: generate-and-test. The key is modelling expressions over domain variables (constraints) making easy to check if they are satisfied.

snacks(Week) :-

    % model the problem with domain variables,
    % make the symbolic associations explicit

    % this is the 'generation phase'

    Nuts = [
        almonds:Almonds,
        cashews:Cashews,
        pecans:Pecans,
        peanuts:Peanuts,
        walnuts:_Walnuts
    ],
    Fruits = [
        apple:Apple,
        banana:Banana,
        pear:Pear,
        mango:Mango,
        apricot:Apricot
    ],

    % since we are going to use plain arithmetic, assign numbers before attempt to evaluate constraints
    assign_days(Nuts),
    assign_days(Fruits),

    % now the 'application symbols' are bound to integers, then we can
    % code actual constraint expressions in a simple way...

    % this is the 'test phase'

    % a) The apple was eaten later in the week than the mango.
    Apple>Mango,

    % b) The banana was eaten later in the week than both the almonds and peanuts,
    %    but earlier in the week than the pear.
    Banana>Almonds,Banana>Peanuts,Banana<Pear,

    % c) The cashews were eaten earlier in the week than both the banana and the apricot,
    %    but later in the week than the peanuts.
    Cashews<Banana,Cashews<Apricot,Cashews>Peanuts,

    % d) The pecans were not eaten the evening after the almonds.
    Pecans=\=Almonds+1,

    % e) Bill ate walnuts one night.
    % no constraints, just existance

    % when we get here, domain variables satisfy the constraints
    % just format the workspace in easy to read list
    findall((Day,Fruit,Nut),(
                nth1(NDay,['Monday','Tuesday','Wednesday','Thursday','Friday'],Day),
                memberchk(Fruit:NDay,Fruits),
                memberchk(Nut:NDay,Nuts)
            ),Week).

assign_days(Snacks) :-
    numlist(1,5,Nums),
    permutation(Nums,Perm),
    maplist([Day,_:Day]>>true,Perm,Snacks).


来源:https://stackoverflow.com/questions/60387784/solving-a-puzzle-in-prolog-about-time-constraints

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!