Prolog 谜题面包师、肉工和木匠

Posted

技术标签:

【中文标题】Prolog 谜题面包师、肉工和木匠【英文标题】:Prolog puzzle baker, meatman and carpenter 【发布时间】:2014-07-10 00:47:46 【问题描述】:

这是我在网上找到的一个非常简单的谜题,我在网上找不到任何解决方案。规则很简单:

有 6 位工匠,M.Baker 和他的儿子,M.Carpenter 和他的儿子,M.Meatman 和他的儿子 每个工匠都可以是面包师、木匠或肉匠 儿子和父亲不能做同样的工作 姓氏不能是工作名称(M.Baker 和他的儿子不能是面包师)

我们知道: - M.Meatman 的儿子是面包师 - M.Baker 和 M.Carpenter 的儿子做同样的工作

我实现了这个谓词:

% swipl prolog
% M.Meatman's son is baker
% M. Baker do job of M. Carpenter's son

% jobs
job(baker).
job(meatman).
job(carpenter).

% fathers
artisan(fatherbaker).
artisan(fathercarpenter).
artisan(fathermeatman).

% sons
artisan(sonbaker).
artisan(soncarpenter).
artisan(sonmeatman).

% some links
father(fatherbaker, sonbaker).
father(fathermeatman, sonmeatman).
father(fathercarpenter, soncarpenter).

son(S, F) :- father(F, S).

same_name(fathercarpenter, soncarpenter,  carpenter).
same_name(fathermeatman, sonmeatman, meatman).
same_name(fatherbaker, sonbaker, baker).

% rules:
do_job(Artisan, Job) :-
    Artisan==sonmeatman,!, 
    artisan(Artisan),
    job(Job),
    Job=baker. % M.Meatman's son is baker (rule 1)

do_job(Artisan, Job) :- 
    Artisan==fatherbaker,!, 
    artisan(Artisan),
    job(Job),
    do_job(soncarpenter, Job). % M.Baker do M.Carpenter's son job

% not relevant...
%do_job(Artisan, Job) :-
%        Artisan == soncarpenter,!,
%   job(Job),
%   artisan(Artisan),
%        do_job(fatherbaker, Job). % rule 2 inverted

% checking if father job is not the same and name are not forgotten
do_job(Artisan, Job) :-
    artisan(Artisan),
    job(Job),
    father(Father, Artisan),
    do_job(Father, JobFather),
    Job \== JobFather,
    not(same_name(Artisan,_,Job)).

% checking if son job is not the same and name are not forgotten
do_job(Artisan, Job) :-
    artisan(Artisan),
    job(Job),
    son(Artisan, Son),
    do_job(Son, JobSon),
    Job \== JobSon,
    not(same_name(_, Artisan, Job)).

那我试试:

swipl
?- do_job(sonmeatman, X).
X = baker ;
false.
?- do_job(fatherbaker, X).
false.

你能告诉我哪里错了吗?

注意:我是 Prolog 的新手。我以前从未使用过这种语言(我是 Golang、Python、C 程序员......)。 注 2:请原谅我的英语,我刚刚翻译了我的法语示例,可能工作名称或动词不正确... 注 3:我已经尝试过实现斑马拼图,但我意识到这比这个更容易解决……奇怪吗?

【问题讨论】:

【参考方案1】:

您过度设计,reifying 问题太多(典型的 Prolog 初学者错误)。你需要写的是一个六元谓词

jobs(BakerSrJob, BakerJrJob,
     CarpenterSrJob, CarpenterJrJob,
     MeatmanSrJob, MeatmanJrJob) :-
    ...

在正文中,对这些变量的约束。例如

member(BakerJrJob, [carpenter, meatman])

表示 Baker jr.要么是木匠,要么是肉工,并且

BakerJrJob \= BakerSrJob

表示贝克父子有不同的工作。这些member 调用和\= 约束中的几个应该足以编码所有必要的知识。 (只需在变量名称中硬编码人员的身份,而不是将名称表示和检查为原子。如果您刚刚开始,请不要尝试编写通用的解谜程序。)

【讨论】:

好的,今晚我会检查一下(法国是 14:50 :)) - 我很确定你是对的,准确地说,我之前尝试过其他一些例子......我对层次解析、路径解析等没有任何问题……我只是尝试阅读文档、测试等等。 经过一些测试,我确实填充这种方式比CappelliC解决方案“更简单”。它易读、易于理解并且只返回一个解决方案。 CapelliC 解决方案很好,但返回许多相同但顺序不同的解决方案。【参考方案2】:

我认为你这里有错误

...
son(Artisan, Son),
...

正如您将儿子/2 关系定义为儿子(儿子,父亲)

edit 说,我会以完全不同的方式解决

solve(Puzzle) :-
    % There are 6 artisans, M.Baker and his son, M.Carpenter and his son and M.Meatman and his son
    Puzzle = [N1/F1/S1, N2/F2/S2, N3/F3/S3],

    % Each artisan can be baker, carpenter or meatman
    Symbols = [baker, carpenter, meatman],
    permutation([N1,N2,N3], Symbols),
    permutation([F1,F2,F3], Symbols),
    permutation([S1,S2,S3], Symbols),

    % Son and father cannot do the same job
    foreach(member(_/F/S, Puzzle), F\=S),

    % The lastname couldn't be the job name
    foreach(member(N/F/S, Puzzle), (N\=F, N\=S)),

    % M.Meatman's son is baker
    member(meatman/_/baker, Puzzle),

    % M.Baker do the same job than M.Carpenter's son
    member(baker/J/_, Puzzle),
    member(carpenter/_/J, Puzzle).

foreach/2 这是一个棘手的谓词,您可以尝试用自己的(递归)定义替换。 permutation/2 更简单,可以轻松替换...

最后一点:“注 3:我已经尝试过实现斑马拼图,但我意识到解决这个问题比这个更简单......奇怪?”是的,这很奇怪,我认为斑马拼图要复杂得多比这个

【讨论】:

"如果Goal 不包含任何不与Generator 共享的变量,则实现执行forall/2。"看起来 forall 可以用在那里代替。 @WillNess:你说得对,我的错 不知道 X/Y/Z 表示法,与 python 元组相媲美...真不错! @Metal3d:您可以在 Prolog 中使用任何 binop 作为对构造函数。 a+b+c 也可以,或者(A, B, C)

以上是关于Prolog 谜题面包师、肉工和木匠的主要内容,如果未能解决你的问题,请参考以下文章

Prolog 逻辑谜题和约束编程

面包屑的用法面包屑有啥作用

为啥我无法在 prolog 中得到斑马谜题的答案?

面包糠的用法大全面包糠怎么制作

给定知识库的 Prolog 谜题 - 不工作

解决Prolog中的谜题