在 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).
,
其次,没有解决办法。那确实是您感兴趣的。有一种非常简单且非常通用的方法可以缩小失败的根源。简单地概括所有目标,一个接一个。我喜欢通过在前面添加*
来做到这一点,如下所示:
在这个程序切片中,它是您原始程序的概括,归结为无法成功
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 中解决“Feed the Golorp”难题