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

Posted

技术标签:

【中文标题】在 Prolog 中使用约束和排列解决难题【英文标题】:Solving puzzle using constraints and permutations in Prolog 【发布时间】:2018-08-01 19:55:07 【问题描述】:

我被要求使用 Prolog 解决一个类似于斑马谜题的谜题。相反,我试图找出吃馅饼的人的国籍。我知道这个问题有不同的解决方案,但是我试图通过使用约束和排列来解决这个难题。但是,运行 pie_eater 谓词会返回 false,并且在跟踪代码时它不会停止运行。

约束列表如下:

1) the Irish person lives next to the yellow house

2) the person who likes donuts lives next to the person who enjoys embroidery

3) the person who enjoys trainspotting lives next to the one who likes pizza

4) the person who enjoys pachinko likes beets

5) the Czech lives in the purple house

6) the person living in the center house listens to grunge

7) the person who enjoys glassblowing listens to hip hop

8) the American likes tofu

9) the Dutch person listens to J-Pop

10) the pink house's owner listens to country

11) the owner of the green house enjoys embroidery

12) the Irish person lives in the first house

13) the pink house is on the left of the orange house

14) the Japanese person enjoys gardening

15) the person who enjoys trainspotting has a neighbor who listens to jazz

在我的代码中,我定义了一个列表 Street,其中包含 5 座房屋。然后,我为列表中每个元素的每个对应形容词定义变量,以用于我的排列猜测。我将每个约束都翻译成代码。为了生成答案,我为每所房子的每组变量创建了排列。 在底部,我定义了谓词 leftofnextto 以满足问题的一些约束。

代码如下:

%who eats pie
pie_eater(PieEater) :-
        puzzle(X),
        member(house(PieEater, _, _, _, pie), X).


puzzle(Street) :- 

        Street = [H1, H2, H3, H4, H5], %street of 5 houses

        %vars for permutation
        H1 = house(Nat1, Col1, Mus1, Hob1, Food1),
        H2 = house(Nat2, Col2, Mus2, Hob2, Food2),
        H3 = house(Nat3, Col3, Mus3, Hob3, Food3),
        H4 = house(Nat4, Col4, Mus4, Hob4, Food4),
        H5 = house(Nat5, Col5, Mus5, Hob5, Food5),

        % facts
        nextto(house(irish, _, _, _, _), house(_, yellow, _, _, _), Street), %irish next to yellow house
        nextto(house(_, _, _, _, donuts), house(_, _, _, embroidery, _), Street), %donut next to embrodier lover
        nextto(house(_, _, _, trainspotting, _), house(_, _, _, _, pizza), Street),  %trainspotting next to pizza ouse      
        member(house(_, _, _, pachinko, beets), Street), %pachinko person eats beets
        member(house(czech, purple, _, _, _), Street), %czech person lives in purple house
        [_, _, house(_, _, grunge, _, _), _, _] = Street, %middle house listens to grunge
        member(house(_, _, hiphop, glassblowing, _), Street), %hiphop lover likes glassblowing
        member(house(american, _, _, _, tofu), Street), %american likes tofu
        member(house(dutch, _, jpop, _, _), Street), %dutch person likes jpop
        member(house(_, pink, country, _, _), Street),  %pink house person likes country music
        member(house(_, green, _, embroidery, _), Street), %green house person likes embroidery
        [house(irish, _, _, _, _), _, _, _, _] = Street, %irish person in first house
        leftof(house(_, pink, _, _, _),house(_, orange, _, _, _), Street),  %pink house left of orange house
        member(house(japanese, _, _, gardening, _), Street), %japanese person likes gardening
        member(house(_, _, jazz, trainspotting, _), Street), %trainspotting person likes jazz
        member(house(_, _, _, _, pie), Street), %one person likes pie

        %permutation guesses
        permutation([irish, czech, american, dutch, japanese],[Nat1, Nat2, Nat3, Nat4, Nat5]), %nat permutations
        permutation([yellow, purple, pink, green, orange],[Col1, Col2, Col3, Col4, Col5]), %color permutations
        permutation([grunge, hiphop, jpop, country, jazz],[Mus1, Mus2, Mus3, Mus4, Mus5]), %music permutation
        permutation([trainspotting, pachinko, glassblowing, embroidery, gardening],[Hob1, Hob2, Hob3, Hob4, Hob5]), %hobby permutation
        permutation([donuts, beets, pizza, tofu, pie],[Food1, Food2, Food3, Food4, Food5]). %food permutation

 %defining other predicates to help solve puzzle
 nextto(X, Y, List) :- leftof(X, Y, List). 
 nextto(X, Y, List) :- leftof(Y, X, List).

 %check if left of
 leftof(L, R, [L,R|_]).
 leftof(L, R, [_ | Rest]) :- leftof(L, R, Rest).

盯着我的代码几个小时,我觉得它应该可以工作,但是我仍然无法弄清楚为什么我不能生成正确的排列,因为我已经定义了所有的约束。在这一点上我被卡住了。

自己做题,我相信日本人就是答案。

【问题讨论】:

【参考方案1】:

约束 15 未正确表示。改成

nextto(house(_, _, _, trainspotting, _), house(_, _, jazz, _, _), Street)

允许代码工作。

【讨论】:

【参考方案2】:

这是我过去使用的代码:

?- puzzle(Who), write(Who), nl, fail.

first(H,[H|_]).

on_the_left(X,Y,[X,Y|_]).
on_the_left(X,Y,[_|Hs]) :- on_the_left(X,Y,Hs).

next_to(X,Y,[X,Y|_]).
next_to(X,Y,[Y,X|_]).
next_to(X,Y,[_|Hs]) :- next_to(X,Y,Hs).

middle(X,[_,_,X,_,_]).

puzzle(Who) :-
    Houses = [
            house(Nationality, Colour, Music, Hobby, Food),
            house(_, _, _, _, _),
            house(_, _, _, _, _),
            house(_, _, _, _, _),
            house(_, _, _, _, _)],
    next_to(house(irish, _, _, _, _), house( _, yellow, _, _, _), Houses), % 01) the Irish person lives next to the yellow house
    next_to(house(_, _, _, _, donuts), house( _, _, _, embroidery, _), Houses), % 02) the person who likes donuts lives next to the person who enjoys embroidery
    next_to(house(_, _, _, trainspotting, _), house( _, _, _, _, pizza), Houses), % 03) the person who enjoys trainspotting lives next to the one who likes pizza
    member(house(_, _, _, pachinko, beets), Houses), % 04) the person who enjoys pachinko likes beets
    member(house(czech, purple, _, _, _), Houses), % 05) the Czech lives in the purple house
    middle(house(_, _, grunge, _, _), Houses), % 06) the person living in the center house listens to grunge
    member(house(_, _, hiphop, glassblowing, _), Houses), % 07) the person who enjoys glassblowing listens to hip hop
    member(house(american, _, _, _, tofu), Houses), % 08) the American likes tofu
    member(house(dutch, _, jpop, _, _), Houses), % 09) the Dutch person listens to J-Pop
    member(house(_, pink, country, _, _), Houses), % 10) the pink house's owner listens to country
    member(house(_, green, _, embroidery, _), Houses), % 11) the owner of the green house enjoys embroidery
    first(house(irish, _, _, _, _), Houses), % 12) the Irish person lives in the first house
    on_the_left(house(_, pink, _, _, _), house(_, orange, _, _, _), Houses), % 13) the pink house is on the left of the orange house
    member(house(japanese, _, _, gardening, _), Houses), % 14) the Japanese person enjoys gardening
    next_to(house(_, _, _, trainspotting, _), house( _, _, jazz, _, _), Houses), % 15) the person who enjoys trainspotting has a neighbor who listens to jazz
    member(house(Who, _, _, _, pie), Houses),
        write(Houses), nl.

输出:

[房子(爱尔兰,绿色,爵士,刺绣,比萨),房子(荷兰,黄色,jpop,trainspotting,甜甜圈),房子(捷克,紫色,垃圾,弹球,甜菜),房子(日本,粉红色,乡村,园艺,馅饼),房子(美国,橙子,嘻哈,吹玻璃,豆腐)] 日本人

【讨论】:

【参考方案3】:

用调试器运行你的代码,看起来这部分从来没有成功过。



    member(house(_, _, jazz, trainspotting, _), Street), 

调用时,变量的内容为:

 

     H1 = house(irish, green, Mus1, embroidery, pizza)
     H2 = house(dutch, yellow, jpop, trainspotting, donuts)
     H3 = house(czech, purple, grunge, pachinko, beets)
     H4 = house(japanese, pink, country, gardening, Food4)
     H5 = house(american, orange, hiphop, glassblowing, tofu)

这些信息有帮助吗? derpnallday 的建议可能是真的(我不知道为什么他的回答被低估了。)

顺便说一句,我曾经使用 clpfd 解决了另一个斑马问题。

以下版本的谜题出现在 1962 年的 Life International:

有五间房子。 英国人住在红房子里。 西班牙人拥有这只狗。 咖啡是在温室里喝的。 乌克兰人喝茶。 温室就在象牙屋的右侧。 老黄金吸烟者拥有蜗牛。 库尔斯在黄色的房子里抽烟。 牛奶是在中间屋喝的。 挪威人住在第一间房子里。 抽切斯特菲尔德烟的那个人住在养狐狸的人旁边的房子里。 在养马的房子旁边的房子里抽烟。 幸运罢工吸烟者喝橙汁。 日本人抽议会。 挪威人住在蓝屋旁边。 现在,谁喝水?谁拥有斑马?

为了清楚起见,必须补充的是,五座房屋中的每一座都涂有不同的颜色,其居民具有不同的民族血统,拥有不同的宠物,喝不同的饮料并抽不同品牌的美国香烟[原文如此]。另一件事:在陈述 6 中,权利意味着您的权利。



:-use_module(library(clpfd)).

zebra_prob:-

% left ------> right
House=[Color1,Color2,Color3,Color4,Color5],
Pet=[Pet1,Pet2,Pet3,Pet4,Pet5],
Race=[Race1,Race2,Race3,Race4,Race5],
Drink=[Bev1,Bev2,Bev3,Bev4,Bev5],
Smoke=[Cig1,Cig2,Cig3,Cig4,Cig5],

% Race 1:English 2:Spaniard 3:Ukrainian 4:Norwegian 5:Japanese
% HouseColor 1:red 2:green 3:ivory 4:yellow 5:blue
% Pet 1:dog 2:snails 3:fox 4:horse 5:zebra
% Drink 1:coffee 2:tea 3:milk 4:orange juice 5:water
% Smoke 1:Old Gold 2:Kool 3:Chesterfields 4:Lucky Strike 5:Parliaments

all_different(House), %house
all_different(Pet), %pet
all_different(Race), %country
all_different(Drink), %drink
all_different(Smoke), %smoke

House ins 1..5,
Pet ins 1..5,
Race ins 1..5,
Drink ins 1..5,
Smoke ins 1..5,

% The Englishman(1) lives in the red house(1).
Race1 #= 1 #<==> Color1 #= 1,
Race2 #= 1 #<==> Color2 #= 1,
Race3 #= 1 #<==> Color3 #= 1,
Race4 #= 1 #<==> Color4 #= 1,
Race5 #= 1 #<==> Color5 #= 1,

% The Spaniard(2) owns the dog(1).
Race1 #= 2 #<==> Pet1 #= 1,
Race2 #= 2 #<==> Pet2 #= 1,
Race3 #= 2 #<==> Pet3 #= 1,
Race4 #= 2 #<==> Pet4 #= 1,
Race5 #= 2 #<==> Pet5 #= 1,

% Coffee(1) is drunk in the green house(2).
Bev1 #= 1 #<==> Color1 #= 2,
Bev2 #= 1 #<==> Color2 #= 2,
Bev3 #= 1 #<==> Color3 #= 2,
Bev4 #= 1 #<==> Color4 #= 2,
Bev5 #= 1 #<==> Color5 #= 2,

% The Ukrainian(3) drinks tea(2).
Race1 #= 3 #<==> Bev1 #= 2,
Race2 #= 3 #<==> Bev2 #= 2,
Race3 #= 3 #<==> Bev3 #= 2,
Race4 #= 3 #<==> Bev4 #= 2,
Race5 #= 3 #<==> Bev5 #= 2,

% The green house(2) is immediately to the right of the ivory house(3).
Color1 #= 3 #<==> Color2 #= 2 ,
Color2 #= 3 #<==> Color3 #= 2 ,
Color3 #= 3 #<==> Color4 #= 2 ,
Color4 #= 3 #<==> Color5 #= 2 ,

% So, green house(2) is not leftmost.
Color1 #\= 2,

% The Old Gold(1) smoker owns snails(2).
Cig1 #= 1 #<==> Pet1 #= 2,
Cig2 #= 1 #<==> Pet2 #= 2,
Cig3 #= 1 #<==> Pet3 #= 2,
Cig4 #= 1 #<==> Pet4 #= 2,
Cig5 #= 1 #<==> Pet5 #= 2,

% Kools(2) are smoked in the yellow(4) house.
Cig1 #= 2 #<==> Color1 #= 4,
Cig2 #= 2 #<==> Color2 #= 4,
Cig3 #= 2 #<==> Color3 #= 4,
Cig4 #= 2 #<==> Color4 #= 4,
Cig5 #= 2 #<==> Color5 #= 4,

% Milk(3) is drunk in the middle house.
Bev3 #= 3,

% The Norwegian(4) lives in the first house. (I assume that "first" means leftmost)
Race1 #= 4,

% The man who smokes Chesterfields(3) lives in the house next to the man with the fox(3).
(Cig1 #= 3 #/\ Pet2 #= 3) #\/
(Cig2 #= 3 #/\ Pet3 #= 3) #\/
(Cig3 #= 3 #/\ Pet4 #= 3) #\/
(Cig4 #= 3 #/\ Pet5 #= 3) #\/
(Pet1 #= 3 #/\ Cig2 #= 3) #\/
(Pet2 #= 3 #/\ Cig3 #= 3) #\/
(Pet3 #= 3 #/\ Cig4 #= 3) #\/
(Pet4 #= 3 #/\ Cig5 #= 3) ,

% Kools(2) are smoked in the house next to the house where the horse(4) is kept.
(Cig1 #= 2 #/\ Pet2 #= 4) #\/
(Cig2 #= 2 #/\ Pet3 #= 4) #\/
(Cig3 #= 2 #/\ Pet4 #= 4) #\/
(Cig4 #= 2 #/\ Pet5 #= 4) #\/
(Pet1 #= 4 #/\ Cig2 #= 2) #\/
(Pet2 #= 4 #/\ Cig3 #= 2) #\/
(Pet3 #= 4 #/\ Cig4 #= 2) #\/
(Pet4 #= 4 #/\ Cig5 #= 2) ,

% The Lucky Strike(4) smoker drinks orange juice(4).
Cig1 #= 4 #<==> Bev1 #= 4,
Cig2 #= 4 #<==> Bev2 #= 4,
Cig3 #= 4 #<==> Bev3 #= 4,
Cig4 #= 4 #<==> Bev4 #= 4,
Cig5 #= 4 #<==> Bev5 #= 4,

% The Japanese(5) smokes Parliaments(5).
Race1 #= 5 #<==> Cig1 #= 5,
Race2 #= 5 #<==> Cig2 #= 5,
Race3 #= 5 #<==> Cig3 #= 5,
Race4 #= 5 #<==> Cig4 #= 5,
Race5 #= 5 #<==> Cig5 #= 5,

% The Norwegian(4) lives next to the blue house(5).
(Race1 #= 4 #/\ Color2 #= 5) #\/
(Race2 #= 4 #/\ Color3 #= 5) #\/
(Race3 #= 4 #/\ Color4 #= 5) #\/
(Race4 #= 4 #/\ Color5 #= 5) #\/
(Color1 #= 5 #/\ Race2 #= 4) #\/
(Color2 #= 5 #/\ Race3 #= 4) #\/
(Color3 #= 5 #/\ Race4 #= 4) #\/
(Color4 #= 5 #/\ Race5 #= 4) ,

label(House),
label(Pet),
label(Race),
label(Smoke),
label(Drink),

write('house:'),write(House),nl,
write('pet:'),write(Pet),nl,
write('race:'),write(Race),nl,
write('drink:'),write(Drink),nl,
write('smoke:'),write(Smoke),nl,nl.
 

[3] 23 ?- zebra_prob.
house:[4,5,1,3,2]
pet:[3,4,2,1,5]
race:[4,3,1,2,5]
drink:[5,2,3,4,1]
smoke:[2,3,1,4,5]

true ;
false.



% Race  1:English 2:Spaniard 3:Ukrainian 4:Norwegian 5:Japanese
% HouseColor 1:red 2:green 3:ivory 4:yellow 5:blue
% Pet 1:dog 2:snails 3:fox 4:horse 5:zebra
% Drink 1:coffee 2:tea 3:milk 4:orange juice 5:water
% Smoke 1:Old Gold 2:Kool 3:Chesterfields 4:Lucky Strike 5:Parliaments

Now, who drinks water? Who owns the zebra?
5:water is drunk by 4:Norwegian
5:zebra is tamed by 5:Japanese

【讨论】:

以上是关于在 Prolog 中使用约束和排列解决难题的主要内容,如果未能解决你的问题,请参考以下文章

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

Prolog 逻辑谜题和约束编程

使用 clpfd Prolog 库解决斑马谜题(又名爱因斯坦谜题)

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

在 Prolog 中解决逻辑难题 [关闭]

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