在 Prolog 中解决关于时间限制的难题

Posted

技术标签:

【中文标题】在 Prolog 中解决关于时间限制的难题【英文标题】:Solving a puzzle in Prolog about time constraints 【发布时间】:2020-06-08 18:48:26 【问题描述】:

卡在 Prolog 问题上。我知道答案(因为我先在纸上做),但我不知道如何让 Prolog 得出答案。

问题:

比尔每天晚上都吃点心,吃不同的水果和不同的 每晚都吃坚果。从下面的陈述中,确定比尔的用途 上周每个工作日晚上的小吃。

a) 苹果比芒果吃得晚。

b) 一周吃香蕉的时间比吃杏仁和 花生,但比梨早。

c) 腰果比香蕉和香蕉更早被吃掉。 杏,但比花生晚。

d) 杏仁后的晚上没有吃山核桃。

e) 比尔吃了一晚核桃。

请注意,问题是大约 5 个工作日晚上(周一至周五), 并提到了 5 种水果和 5 种坚果。你的程序应该 解决问题并打印出解决方案,这将是一组 5 三元组,例如(星期一,苹果,山核桃),...(星期五,芒果,核桃)。

显然,这些不是正确的答案,而只是要显示的值 解决方案是什么样的。

到目前为止的代码:

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).

【问题讨论】:

我添加了一个标签。仔细阅读它,看看它的答案是否有帮助。还有,问题是什么?代码是否有效,如图所示?您正在尝试的查询是什么? @false(抢先)。在我添加突出显示之前,无论多么有缺陷,我什至没有看到小的 cased logvar。 YMWV。 fruit5 \= mango 总是正确的。两个原子是不同的。 Prolog 的变量必须以大写字母开头,例如Fruit5。为了使 "ununifiable" 目标 (Fruit5 \= mango) 成功,logvar (Fruit5) 应该在尝试目标时已经实例化(出现在整体谓词中),否则该目标将失败,因为通过实例化一个免费的 logvar 总是可以与给定的值统一。 感兴趣的:Rosetta Code - Zebra Puzzle - Prolog versions I did it on paper first 你可能不知道作为一个有能力解决问题的人,这让你领先多少。 【参考方案1】:

首先,真的不需要手动打印任何东西。 Prolog 的顶层会为您执行此操作,如果您输入查询 solve(A).

其次,没有解决办法。那确实是您感兴趣的。有一种非常简单且非常通用的方法可以缩小失败的根源。简单地概括所有目标,一个接一个。我喜欢通过在前面添加* 来做到这一点,如下所示:

:- op(950, fy, *)。 *_0。 解决(A):- * A = [[day1,fruit1,nut1],[day2,fruit2,nut2],[day3,fruit3,nut3], [day4,fruit4,nut4],[day5,fruit5,nut5]], 天数 = [星期一|_/*[星期二、星期三、星期四、星期五]*/]、 天数 = [day1|_/*[day2, day3, day4, day5]*/], * 水果 = [苹果、香蕉、梨、芒果、杏], * permutation(Fruits, [fruit1,fruit2,fruit3,fruit4,fruit5]), * 坚果 = [杏仁、山核桃、腰果、花生、核桃], * 排列(坚果,[nut1,nut2,nut3,nut4,nut5]), % 线索 1 - 苹果之前的芒果 * fruit5 \= 芒果, * 成员([C1,mango,_], A), * member([C2,apple,_], A), before_in_week(C1,C2,Days), % 线索 2 - 香蕉在杏仁和花生之后,但在梨之前 * fruit5 \= 香蕉, * 成员([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), % 线索 3 - 腰果在香蕉和杏之前,但在花生之后 * nut5 \= 花生, * 成员([C1,_,腰果], 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), % 线索 4 - 杏仁后不晚上吃山核桃 * nut5 \= 杏仁。 % 线索 5 - 一晚吃了核桃

在这个程序切片中,它是您原始程序的概括,归结为无法成功

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

你必须在那里改变一些东西。 day1 是常量,应该是变量。

稍后,将所有X \= const 替换为dif(X, const)

【讨论】:

【参考方案2】:

您最大的问题是您正在使用原子 (fruit4),但您应该使用变量 (Fruit4)。注意开头的大小写。

另外,你正在做一个你不需要的排列。 Prolog 通过回溯完成您需要的所有排列。这就是 Prolog 如此有趣的语言的原因。

试试这个代码:

?- 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).

这给了我:

[星期一,芒果,花生] [星期二,苹果,腰果] [星期三,杏,杏仁] [星期四,香蕉,核桃] [星期五,梨,山核桃] 是的。

【讨论】:

【参考方案3】:

这个难题可以通过 Prolog 的主力之一轻松解决:生成和测试。关键是对域变量(约束)的表达式建模,以便检查它们是否满足。

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).

【讨论】:

以上是关于在 Prolog 中解决关于时间限制的难题的主要内容,如果未能解决你的问题,请参考以下文章

解决Prolog中的谜题

在 Prolog 中使用约束和排列解决难题

如何使用Prolog CLP FD进行路径限制?

在 Prolog 中解决“Feed the Golorp”难题

在 CLPQ/R (Prolog) 中解决一个简单的几何难题

Prolog - 尝试解决文本难题