Prolog River Crossing

时间:2015-11-02 01:45:12

标签: prolog river-crossing-puzzle

所以我被赋予了一个试图在Prolog中解决这个问题的任务,尽管老师只涵盖了基础知识,这实际上是Prolog中唯一的项目。我觉得我已经过度思考了,并且他只是期待着第一次Prolog项目。

问题列在下面,我应该如何解决这个问题?

编写一个Prolog程序,解决下面的单词问题。作为解决方案的一部分,它应该打印所有交叉点,首先列出桨手。

汤姆,杰克,比尔和吉姆不得不乘坐只有两个人的独木舟过河 在从河的左岸到右岸的三个交叉口中的每一个中,独木舟有两个人,并且在从右岸到左岸的两个交叉口的每一个中,独木舟有一个人。当别人和他在独木舟上时,汤姆无法划桨 除了比尔和他在独木舟上的时候,杰克无法划桨。每个人划船至少一次穿越。

这是我到目前为止所做的,虽然它“有效”但并不能确保每个人都至少划过一次。

state(tom(Side),jim(Side),jack(Side),bill(Side),c(Side)).
initial(state(tom(l),jim(l),jack(l),bill(l),c(l))).
final(state(tom(r),jim(r),jack(r),bill(r),c(r))).

canoe(P):-P=p.
canoe(P,C):-P=p,C=c.

bad(state(tom(W),jim(X),jack(Y),bill(Z),c(C))):-
    C=l,(W=c;X=c;Y=c;Z=c).

move(state(tom(W),jim(X),jack(Y),bill(Z),c(C)),
     state(tom(W1),jim(X),jack(Y),bill(Z),c(C1))):-
    ((canoe(W1),W=r,W=C,C1=m);(canoe(W),W1=l,W1=C1)).
move(state(tom(W),jim(X),jack(Y),bill(Z),c(C)),
     state(tom(W),jim(X1),jack(Y),bill(Z),c(C1))):-
    ((canoe(X1),X=r,X=C,C1=m);(canoe(X),X1=l,X1=C1)).
move(state(tom(W),jim(X),jack(Y),bill(Z),c(C)),
     state(tom(W),jim(X),jack(Y1),bill(Z),c(C1))):-
    ((canoe(Y1),Y=r,Y=C,C1=m);(canoe(Y),Y1=l,Y1=C1)).
move(state(tom(W),jim(X),jack(Y),bill(Z),c(C)),
     state(tom(W),jim(X),jack(Y),bill(Z1),c(C1))):-
    ((canoe(Z1),Z=r,Z=C,C1=m);(canoe(Z),Z1=l,Z1=C1)).
move(state(tom(W),jim(X),jack(Y),bill(Z),c(C)),
     state(tom(W1),jim(X1),jack(Y),bill(Z),c(C1))):-
    ((canoe(X1,W1),W=l,W=X,W=C,C1=m);
    (canoe(X,W),W1=r,W1=X1,W1=C1)).
move(state(tom(W),jim(X),jack(Y),bill(Z),c(C)),
     state(tom(W1),jim(X),jack(Y),bill(Z1),c(C1))):-
    ((canoe(Z1,W1),W=l,W=Z,W=C,C1=m);
    (canoe(Z,W),W1=r,W1=Z1,W1=C1)).
move(state(tom(W),jim(X),jack(Y),bill(Z),c(C)),
     state(tom(W),jim(X1),jack(Y1),bill(Z),c(C1))):-
    ((canoe(X1,Y1),Y=l,Y=X,Y=C,C1=m);
    (canoe(X,Y),Y1=r,Y1=X1,Y1=C1)).
move(state(tom(W),jim(X),jack(Y),bill(Z),c(C)),
     state(tom(W),jim(X1),jack(Y),bill(Z1),c(C1))):-
    ((canoe(Z1,X1);canoe(X1,Z1)),
     Z=l,Z=X,Z=C,C1=m);
    ((canoe(Z,X);canoe(X,Z)),Z1=r,Z1=X1,Z1=C1).
move(state(tom(W),jim(X),jack(Y),bill(Z),c(C)),
     state(tom(W),jim(X),jack(Y1),bill(Z1),c(C1))):-
    ((canoe(Y1,Z1);canoe(Z1,Y1)),
     Y=l,Y=Z,Y=C,C1=m);
    ((canoe(Y,Z);canoe(Z,Y)),Y1=r,Y1=Z1,Y1=C1).

find(Path):-initial(S),rez(S,Path).
bkt(State,Path,[Path|State]):-final(State).
bkt(State,Path,Sol):-move(State,Next),not(bad(Next)),
    not(member(Next,Path)),bkt(Next,[Path|Next],Sol).
rez(State,Sol):-bkt(State,[State],Sol).
start:-find(D),writef('%w\n',D).

1 个答案:

答案 0 :(得分:0)

(这个答案可能为时已晚,但由于这是一个非常经典的问题/谜题,有许多变种,我认为尝试将问题稍微解决可能仍然有用。)

至于上面的答案,我认为做一些重构并尝试为这个问题编写一个更简单,更易于管理的模型可能是一个好主意。我的意思是,如果有人要求您举例,请快速修改'你的代码集成了让第五个人说出这个难题,重构上面的代码并不是很有趣。

你可以开始 - 这只是一种给你一个想法的方法 - 通过在列表中编码4个人的配置,我们使用' l'或者' r'指定某人是否位于河岸的左侧或右侧。这会给我们一个像这样的初始状态:

% Tom, Jack, Bill, and Jim are all on the left side
[l,l,l,l]

我们希望达到目标状态:

% Tom, Jack, Bill, and Jim are all on the right side
[r,r,r,r]

这为我们提供了一个更容易阅读/理解的模型(imo)。

然后我们可以更多地考虑如何编码河岸之间的实际运输。我们编写了列表配置来指定哪个人位于哪里,所以现在我们需要一个可以将一个配置转换为另一个配置的Prolog谓词。让我们说:

transport(StartState,[Persons],EndState)

现在,对于实现而言,不是明确地匹配所有可能的移动(就像你在当前代码中那样),尝试概括究竟发生了什么总是一个好主意(Prolog中有趣的部分) :))。

如果不立即编写过于复杂的代码,我们会将问题分解成小块:

% Facts defining a crossing of the river
cross(l,r).
cross(r,l).

% Transport example for Tom
transport([X,Jack,Bill,Jim],[tom],[Y,Jack,Bill,Jim]) :- cross(X,Y).

正如您所看到的,我们现在定义了' transport'以一种非常简单的方式:众所周知汤姆只会自己过河,所以我们用交叉事实来改变他的位置。 (请注意,杰克,比尔和吉姆只是说明了这些人所在河岸的指示。或者说,因为汤姆只是独自穿过,这些变量不会改变!)。我们可以写得更加抽象,并且没有专门针对“汤姆”,但我试图在这个例子中保持简单。

当然,我们仍需要表明哪些交叉有效且哪些交叉无效。从您的问题:"在从河的左岸到右岸的三个交叉口的每一个中,独木舟有两个人,在从右到左的两个交叉口的每一个中,独木舟有一个人&#34。 这些条件可以很容易地使用我们的'运输'谓词,因为初始状态(第一个arg)告诉我们是否从左到右交叉,反之亦然,我们的第二个参数指定了哪些人正在穿越的列表。换句话说,交叉的方向和乘客的数量已经知道,在这里写下这些条件似乎有点微不足道。

接下来:"当除了比尔之外的其他任何人与他一起划独木舟时,杰克无法划桨。"同样,这已经很容易在我们的运输中一起写出来了。 (请注意,我使用通配符忽略了我们不关心这一特定条件的信息,但当然,这可能会在最终的代码中有所不同。这只是举个例子。 ):

% Transport example for Jack (Persons length = min 1 - max 2)
transport([_,_,_,_],Persons,[_,_,_,_]) :-
    length(Persons,2),
    ( member(jack,Persons) ->
      member(bill,Persons)
    ;
      * other condition(s) *
    ).

% Alternative with pattern matching on Persons
transport([_,_,_,_],[A,B],[_,_,_,_]) :-
    * if jack is A or B, then bill is the other one *    

另一个简单的例子:"当别人和他一起乘独木舟时,汤姆无法划桨。"

% Tom cannot peddle in a team of 2
transport([_,_,_,_],Persons,[_,_,_,_]) :-
    length(Persons,2),
    \+ member(tom,Persons).

正如您可能已经注意到的那样,我们现在已经几乎完全分解了在我们的模型中可以非常容易地表达的零碎问题,并且我们距离编写实际求解器并不远。但是,有足够的代码示例可以在网上找到,我认为没有必要在这里找到更多的代码。

通过搜索经典Fox-Goose-Beans/Cabbage拼图,可以找到更多灵感。

祝大家好运!