Prolog中解决农民-山羊-狼-卷心菜问题的优化

Prolog中解决农民-山羊-狼-卷心菜问题的优化,prolog,river-crossing-puzzle,Prolog,River Crossing Puzzle,我正在为经典的“狼、山羊和卷心菜”问题,在Prolog中寻找一种更好的算法,计算效率更高。下面的算法基于BFS搜索可能的情况 问题: “从前,一个农民去市场买了一只狼、一只山羊和一棵卷心菜。在回家的路上,农民来到河岸边租了一艘船。但是坐船过河时,农民只能自己和他买的一棵菜:狼、山羊或卷心菜 如果无人看管,狼会吃掉山羊,或者山羊会吃掉卷心菜 农场主的挑战是把他自己和他买的东西带到河的对岸,让每一件东西都完好无损。他是怎么做到的?” 此问题的当前解决方案是: writelist([H|T]):-

我正在为经典的“狼、山羊和卷心菜”问题,在Prolog中寻找一种更好的算法,计算效率更高。下面的算法基于BFS搜索可能的情况

问题

“从前,一个农民去市场买了一只狼、一只山羊和一棵卷心菜。在回家的路上,农民来到河岸边租了一艘船。但是坐船过河时,农民只能自己和他买的一棵菜:狼、山羊或卷心菜

如果无人看管,狼会吃掉山羊,或者山羊会吃掉卷心菜

农场主的挑战是把他自己和他买的东西带到河的对岸,让每一件东西都完好无损。他是怎么做到的?”

此问题的当前解决方案是:

writelist([H|T]):-
       write(H), writelist(T).

empty_stack([]).
stack(Top, Stack, [Top|Stack]).
member_stack(Element, Stack):-
       member(Element, Stack).


reverse_print_stack(S):-
               empty_stack(S).
reverse_print_stack(S):-
               stack(E, Rest, S),
               reverse_print_stack(Rest),
               write(E), nl.


unsafe(state(X,Y,Y,C)):-
       opp(X, Y).
unsafe(state(X,W,Y,Y)):-
       opp(X, Y).

move(state(X,X,G,C), state(Y,Y,G,C)):-
       opp(X,Y), not(unsafe(state(Y,Y,G,C))),
       writelist(['try farmer takes wolf ',Y,Y,G,C]),nl.

move(state(X,W,X,C), state(Y,W,Y,C)):-
       opp(X,Y), not(unsafe(state(Y,W,Y,C))),
       writelist(['try farmer takes goat ',Y,W,Y,C]),nl.

move(state(X,W,G,X), state(Y,W,G,Y)):-
       opp(X,Y), not(unsafe(state(Y,W,G,Y))),
       writelist(['try farmer takes cabbage ',Y,W,G,Y]),nl.

move(state(X,W,G,C), state(Y,W,G,C)):-
       opp(X,Y), not(unsafe(state(Y,W,G,C))),
       writelist(['try farmer takes self ',Y,W,G,C]),nl.

move(state(F,W,G,C), state(F,W,G,C)):-
       writelist([' BACKTRACK from: ',F,W,G,C]),nl,fail.

path(Goal, Goal, Been_stack):-
       nl, write('Solution Path is: '), nl,
       reverse_print_stack(Been_stack).


path(State, Goal, Been_stack):-
       move(State, Next_state),
       not(member_stack(Next_state, Been_stack)),
       stack(Next_state, Been_stack, New_been_stack),
       path(Next_state, Goal, New_been_stack),!.


opp(e,w).
opp(w,e).


go(Start, Goal):-
       empty_stack(Empty_been_stack),
       stack(Start, Empty_been_stack, Been_stack),
       path(Start, Goal, Been_stack).


test:-go(state(w,w,w,w), state(e,e,e,e)). ```

这是一个经过清理/简化的版本(IMHO)

总的来说,没有太多需要优化的地方

这是通过状态空间对路径进行的标准深度优先搜索

对于广度优先搜索,需要另一种方法

可以尝试比线性扫描(即某种哈希表)更快地查找访问的状态,但对于长度为8的历史来说,这真的不值得

% The state indicates the position of: Farmer, Wolf, Goat, Cabbage
% which is always one of east ('e') or west ('w').

unsafe(state(e,w,w,_)).  % Wolf and Goat are on the same border and the farmer ain't there
unsafe(state(w,e,e,_)).  % Wolf and Goat are on the same border and the farmer ain't there

unsafe(state(e,_,w,w)).  % Goat and Cabbage are on the same border and the farmer ain't there
unsafe(state(w,_,e,e)).  % Goat and Cabbage are on the same border and the farmer ain't there

opp(e,w).
opp(w,e).
       
% Valid next state generation

move(state(X,X,G,C), state(Y,Y,G,C)):-
   opp(X,Y), 
   \+ unsafe(state(Y,Y,G,C)),
   format("try farmer takes wolf ~q -> ~q\n",[state(X,X,G,C), state(Y,Y,G,C)]).

move(state(X,W,X,C), state(Y,W,Y,C)):-
   opp(X,Y),
   \+ unsafe(state(Y,W,Y,C)),
   format("try farmer takes goat ~q -> ~q\n",[state(X,W,X,C), state(Y,W,Y,C)]).

move(state(X,W,G,X), state(Y,W,G,Y)):-
   opp(X,Y),
   \+ unsafe(state(Y,W,G,Y)),   
   format("try farmer takes cabbage ~q -> ~q\n",[state(X,W,G,X), state(Y,W,G,Y)]).
   
move(state(X,W,G,C), state(Y,W,G,C)):-
   opp(X,Y),
   \+ unsafe(state(Y,W,G,C)),
   format("try farmer takes self ~q -> ~q\n",[state(X,W,G,C), state(Y,W,G,C)]).

not_yet_seen(State,History) :-                       % will fail if  member(State, History) 
   member(State, History),
   !,
   format("State ~q already seen\n",[State]),
   fail.

not_yet_seen(_,_).                                   % the replacement: success if \+ member(State, History)
   
path(State, State, History) :-                       % found a solution! (but maybe not the BEST solution)
   !,                                                % don't continue the search down this history
   reverse(History,RevHistory),
   maplist(term_to_atom,RevHistory,RevHistoryAtoms),
   atomic_list_concat(RevHistoryAtoms, '->', OutputAtom),
   length(History,L),
   format("A solution of length ~d: ~q\n",[L,OutputAtom]).

path(CurState, FinalState, History) :-
   move(CurState, NextState),                        % generate a safe next state
   not_yet_seen(NextState, History),                 % that hasn't been seen yet
   (true;(format("Backtracking from using ~q\n",[NextState]),fail)),
   path(NextState, FinalState, [NextState|History]). % add it to the visited states and recurse

go(StartState, FinalState) :- 
   path(StartState, FinalState, [StartState]). % 3rd arg is "history"

test :- 
   go(state(w,w,w,w),  % first everything is west 
      state(e,e,e,e)). % then everything is east
它找到了两个历史长度为8的解决方案:

?- test.
try farmer takes goat state(w,w,w,w) -> state(e,w,e,w)
try farmer takes goat state(e,w,e,w) -> state(w,w,w,w)
State state(w,w,w,w) already seen
try farmer takes self state(e,w,e,w) -> state(w,w,e,w)
try farmer takes wolf state(w,w,e,w) -> state(e,e,e,w)
try farmer takes wolf state(e,e,e,w) -> state(w,w,e,w)
State state(w,w,e,w) already seen
try farmer takes goat state(e,e,e,w) -> state(w,e,w,w)
try farmer takes goat state(w,e,w,w) -> state(e,e,e,w)
State state(e,e,e,w) already seen
try farmer takes cabbage state(w,e,w,w) -> state(e,e,w,e)
try farmer takes wolf state(e,e,w,e) -> state(w,w,w,e)
try farmer takes wolf state(w,w,w,e) -> state(e,e,w,e)
State state(e,e,w,e) already seen
try farmer takes goat state(w,w,w,e) -> state(e,w,e,e)
try farmer takes goat state(e,w,e,e) -> state(w,w,w,e)
State state(w,w,w,e) already seen
try farmer takes cabbage state(e,w,e,e) -> state(w,w,e,w)
State state(w,w,e,w) already seen
Backtracking from using state(e,w,e,e)
Backtracking from using state(w,w,w,e)
try farmer takes cabbage state(e,e,w,e) -> state(w,e,w,w)
State state(w,e,w,w) already seen
try farmer takes self state(e,e,w,e) -> state(w,e,w,e)
try farmer takes goat state(w,e,w,e) -> state(e,e,e,e)
A solution of length 8: 'state(w,w,w,w)->state(e,w,e,w)->state(w,w,e,w)->state(e,e,e,w)->state(w,e,w,w)->state(e,e,w,e)->state(w,e,w,e)->state(e,e,e,e)'
true ;


Backtracking from using state(e,e,e,e)
try farmer takes self state(w,e,w,e) -> state(e,e,w,e)
State state(e,e,w,e) already seen
Backtracking from using state(w,e,w,e)
Backtracking from using state(e,e,w,e)
Backtracking from using state(w,e,w,w)
Backtracking from using state(e,e,e,w)
try farmer takes cabbage state(w,w,e,w) -> state(e,w,e,e)
try farmer takes goat state(e,w,e,e) -> state(w,w,w,e)
try farmer takes wolf state(w,w,w,e) -> state(e,e,w,e)
try farmer takes wolf state(e,e,w,e) -> state(w,w,w,e)
State state(w,w,w,e) already seen
try farmer takes cabbage state(e,e,w,e) -> state(w,e,w,w)
try farmer takes goat state(w,e,w,w) -> state(e,e,e,w)
try farmer takes wolf state(e,e,e,w) -> state(w,w,e,w)
State state(w,w,e,w) already seen
try farmer takes goat state(e,e,e,w) -> state(w,e,w,w)
State state(w,e,w,w) already seen
Backtracking from using state(e,e,e,w)
try farmer takes cabbage state(w,e,w,w) -> state(e,e,w,e)
State state(e,e,w,e) already seen
Backtracking from using state(w,e,w,w)
try farmer takes self state(e,e,w,e) -> state(w,e,w,e)
try farmer takes goat state(w,e,w,e) -> state(e,e,e,e)
A solution of length 8: 'state(w,w,w,w)->state(e,w,e,w)->state(w,w,e,w)->state(e,w,e,e)->state(w,w,w,e)->state(e,e,w,e)->state(w,e,w,e)->state(e,e,e,e)'
true ;


Backtracking from using state(e,e,e,e)
try farmer takes self state(w,e,w,e) -> state(e,e,w,e)
State state(e,e,w,e) already seen
Backtracking from using state(w,e,w,e)
Backtracking from using state(e,e,w,e)
try farmer takes goat state(w,w,w,e) -> state(e,w,e,e)
State state(e,w,e,e) already seen
Backtracking from using state(w,w,w,e)
try farmer takes cabbage state(e,w,e,e) -> state(w,w,e,w)
State state(w,w,e,w) already seen
Backtracking from using state(e,w,e,e)
try farmer takes self state(w,w,e,w) -> state(e,w,e,w)
State state(e,w,e,w) already seen
Backtracking from using state(w,w,e,w)
Backtracking from using state(e,w,e,w)
false.

这是一个经过清理/简化的版本(IMHO)

总的来说,没有太多需要优化的地方

这是通过状态空间对路径进行的标准深度优先搜索

对于广度优先搜索,需要另一种方法

可以尝试比线性扫描(即某种哈希表)更快地查找访问的状态,但对于长度为8的历史来说,这真的不值得

% The state indicates the position of: Farmer, Wolf, Goat, Cabbage
% which is always one of east ('e') or west ('w').

unsafe(state(e,w,w,_)).  % Wolf and Goat are on the same border and the farmer ain't there
unsafe(state(w,e,e,_)).  % Wolf and Goat are on the same border and the farmer ain't there

unsafe(state(e,_,w,w)).  % Goat and Cabbage are on the same border and the farmer ain't there
unsafe(state(w,_,e,e)).  % Goat and Cabbage are on the same border and the farmer ain't there

opp(e,w).
opp(w,e).
       
% Valid next state generation

move(state(X,X,G,C), state(Y,Y,G,C)):-
   opp(X,Y), 
   \+ unsafe(state(Y,Y,G,C)),
   format("try farmer takes wolf ~q -> ~q\n",[state(X,X,G,C), state(Y,Y,G,C)]).

move(state(X,W,X,C), state(Y,W,Y,C)):-
   opp(X,Y),
   \+ unsafe(state(Y,W,Y,C)),
   format("try farmer takes goat ~q -> ~q\n",[state(X,W,X,C), state(Y,W,Y,C)]).

move(state(X,W,G,X), state(Y,W,G,Y)):-
   opp(X,Y),
   \+ unsafe(state(Y,W,G,Y)),   
   format("try farmer takes cabbage ~q -> ~q\n",[state(X,W,G,X), state(Y,W,G,Y)]).
   
move(state(X,W,G,C), state(Y,W,G,C)):-
   opp(X,Y),
   \+ unsafe(state(Y,W,G,C)),
   format("try farmer takes self ~q -> ~q\n",[state(X,W,G,C), state(Y,W,G,C)]).

not_yet_seen(State,History) :-                       % will fail if  member(State, History) 
   member(State, History),
   !,
   format("State ~q already seen\n",[State]),
   fail.

not_yet_seen(_,_).                                   % the replacement: success if \+ member(State, History)
   
path(State, State, History) :-                       % found a solution! (but maybe not the BEST solution)
   !,                                                % don't continue the search down this history
   reverse(History,RevHistory),
   maplist(term_to_atom,RevHistory,RevHistoryAtoms),
   atomic_list_concat(RevHistoryAtoms, '->', OutputAtom),
   length(History,L),
   format("A solution of length ~d: ~q\n",[L,OutputAtom]).

path(CurState, FinalState, History) :-
   move(CurState, NextState),                        % generate a safe next state
   not_yet_seen(NextState, History),                 % that hasn't been seen yet
   (true;(format("Backtracking from using ~q\n",[NextState]),fail)),
   path(NextState, FinalState, [NextState|History]). % add it to the visited states and recurse

go(StartState, FinalState) :- 
   path(StartState, FinalState, [StartState]). % 3rd arg is "history"

test :- 
   go(state(w,w,w,w),  % first everything is west 
      state(e,e,e,e)). % then everything is east
它找到了两个历史长度为8的解决方案:

?- test.
try farmer takes goat state(w,w,w,w) -> state(e,w,e,w)
try farmer takes goat state(e,w,e,w) -> state(w,w,w,w)
State state(w,w,w,w) already seen
try farmer takes self state(e,w,e,w) -> state(w,w,e,w)
try farmer takes wolf state(w,w,e,w) -> state(e,e,e,w)
try farmer takes wolf state(e,e,e,w) -> state(w,w,e,w)
State state(w,w,e,w) already seen
try farmer takes goat state(e,e,e,w) -> state(w,e,w,w)
try farmer takes goat state(w,e,w,w) -> state(e,e,e,w)
State state(e,e,e,w) already seen
try farmer takes cabbage state(w,e,w,w) -> state(e,e,w,e)
try farmer takes wolf state(e,e,w,e) -> state(w,w,w,e)
try farmer takes wolf state(w,w,w,e) -> state(e,e,w,e)
State state(e,e,w,e) already seen
try farmer takes goat state(w,w,w,e) -> state(e,w,e,e)
try farmer takes goat state(e,w,e,e) -> state(w,w,w,e)
State state(w,w,w,e) already seen
try farmer takes cabbage state(e,w,e,e) -> state(w,w,e,w)
State state(w,w,e,w) already seen
Backtracking from using state(e,w,e,e)
Backtracking from using state(w,w,w,e)
try farmer takes cabbage state(e,e,w,e) -> state(w,e,w,w)
State state(w,e,w,w) already seen
try farmer takes self state(e,e,w,e) -> state(w,e,w,e)
try farmer takes goat state(w,e,w,e) -> state(e,e,e,e)
A solution of length 8: 'state(w,w,w,w)->state(e,w,e,w)->state(w,w,e,w)->state(e,e,e,w)->state(w,e,w,w)->state(e,e,w,e)->state(w,e,w,e)->state(e,e,e,e)'
true ;


Backtracking from using state(e,e,e,e)
try farmer takes self state(w,e,w,e) -> state(e,e,w,e)
State state(e,e,w,e) already seen
Backtracking from using state(w,e,w,e)
Backtracking from using state(e,e,w,e)
Backtracking from using state(w,e,w,w)
Backtracking from using state(e,e,e,w)
try farmer takes cabbage state(w,w,e,w) -> state(e,w,e,e)
try farmer takes goat state(e,w,e,e) -> state(w,w,w,e)
try farmer takes wolf state(w,w,w,e) -> state(e,e,w,e)
try farmer takes wolf state(e,e,w,e) -> state(w,w,w,e)
State state(w,w,w,e) already seen
try farmer takes cabbage state(e,e,w,e) -> state(w,e,w,w)
try farmer takes goat state(w,e,w,w) -> state(e,e,e,w)
try farmer takes wolf state(e,e,e,w) -> state(w,w,e,w)
State state(w,w,e,w) already seen
try farmer takes goat state(e,e,e,w) -> state(w,e,w,w)
State state(w,e,w,w) already seen
Backtracking from using state(e,e,e,w)
try farmer takes cabbage state(w,e,w,w) -> state(e,e,w,e)
State state(e,e,w,e) already seen
Backtracking from using state(w,e,w,w)
try farmer takes self state(e,e,w,e) -> state(w,e,w,e)
try farmer takes goat state(w,e,w,e) -> state(e,e,e,e)
A solution of length 8: 'state(w,w,w,w)->state(e,w,e,w)->state(w,w,e,w)->state(e,w,e,e)->state(w,w,w,e)->state(e,e,w,e)->state(w,e,w,e)->state(e,e,e,e)'
true ;


Backtracking from using state(e,e,e,e)
try farmer takes self state(w,e,w,e) -> state(e,e,w,e)
State state(e,e,w,e) already seen
Backtracking from using state(w,e,w,e)
Backtracking from using state(e,e,w,e)
try farmer takes goat state(w,w,w,e) -> state(e,w,e,e)
State state(e,w,e,e) already seen
Backtracking from using state(w,w,w,e)
try farmer takes cabbage state(e,w,e,e) -> state(w,w,e,w)
State state(w,w,e,w) already seen
Backtracking from using state(e,w,e,e)
try farmer takes self state(w,w,e,w) -> state(e,w,e,w)
State state(e,w,e,w) already seen
Backtracking from using state(w,w,e,w)
Backtracking from using state(e,w,e,w)
false.

目前正在进行上述操作(可以大大简化…不需要使用helper谓词来扩展列表,因为Prolog程序员认识到列表扩展的含义),但这里有一个问题:“路径(下一个状态、目标、新的堆栈)!”。。。为什么要割伤?如果你想搜索,那么“提交”到当前选择的路径是没有意义的,毕竟,这只是一个猜测,一个暗中的尝试。不要在这里剪!另外,上面所说的绝对是深度优先搜索,而不是广度优先搜索(在这种情况下,建议使用迭代深化以避免内存使用的焦油坑)。移动生成器生成相同的状态,然后打印“回溯自”也没有什么意义。(好的,这意味着打印出安全移动生成失败的事实,作为一个catchall)“回溯”发生在路径/3中,尽管目前正在进行上述操作(它可以被大大简化…不需要使用帮助器谓词来扩展列表,因为Prolog程序员认识到列表扩展的本质)但这里有一个问题:“路径(下一个状态、目标、新的堆栈),!”。。。为什么要割伤?如果你想搜索,那么“提交”到当前选择的路径是没有意义的,毕竟,这只是一个猜测,一个暗中的尝试。不要在这里剪!另外,上面所说的绝对是深度优先搜索,而不是广度优先搜索(在这种情况下,建议使用迭代深化以避免内存使用的焦油坑)。移动生成器生成相同的状态,然后打印“回溯自”也没有什么意义。(好的,这意味着打印出安全移动生成失败的事实,作为一个总括)“回溯”发生在路径/3中。我正在搜索BFS优化和更大的历史记录,并且最佳答案将是最小的。路径的最小长度并最小化计算成本。我正在搜索BFS优化和更大的历史记录,并且最佳答案将是最小的。路径的最小长度和最小化计算成本。