Решить людоедов / миссионеров, используя поиск в ширину (BFS) в Прологе? - PullRequest
4 голосов
/ 30 марта 2012

Я работаю над решением классической проблемы миссионеров (M) и каннибалов (C), начальное состояние - 3 M и 3 C на левом берегу, а состояние цели - 3M, 3C на правом берегу.Я выполнил базовую функцию в моей программе, и мне нужно реализовать поисковую стратегию, такую ​​как BFS и DFS.

В основном мой код изучают из Интернета.Пока я могу успешно запустить программу с помощью метода DFS, но я пытаюсь запустить с BFS, он всегда возвращает false.Это моя самая первая программа SWI-Prolog, я не могу найти, в чем проблема моего кода.

Вот часть моего кода, надеюсь, вы поможете мне найти ее проблему

solve2 :-
   bfs([[[3,3,left]]],[0,0,right],[[3,3,left]],Solution),
   printSolution(Solution).

bfs([[[A,B,C]]],[A,B,C],_,[]).
bfs([[[A,B,C]|Visisted]|RestPaths],[D,E,F],Visisted,Moves) :-
   findall([[I,J,K],[A,B,C]|Visited]),
     (
       move([A,B,C],[I,J,K],Description),
       safe([I,J,K]),
       not(member([I,J,K],Visited)
     ),
     NewPaths
   ),
   append(RestPaths,NewPaths,CurrentPaths),
   bfs(CurrentPaths,[D,E,F],[[I,J,K]|Visisted],MoreMoves),
   Moves = [ [[A,B,C],[I,J,K],Description] | MoreMoves ].


move([A,B,left],[A1,B,right],'One missionary cross river') :-
   A > 0, A1 is A - 1.  
   % Go this state if left M > 0. New left M is M-1
.
.
.
.
.
safe([A,B,_]) :-
   (B =< A ; A = 0),
   A1 is 3-A, B1 is 3-B,
   (B1 =< A1; A1 =0).

Я использую findall, чтобы найти все возможные пути, прежде чем перейти на следующий уровень.Только один проход safe () будет считаться возможным следующим состоянием.Государство не будет использовать, если оно уже существует.Так как моя программа может работать с DFS, я думаю, что нет ничего плохого в предикате move () и safe ().Мой предикат BFS меняется на основе моего кода DFS, но это не работает.

Ответы [ 5 ]

5 голосов
/ 30 марта 2012

Существует очень простой способ превратить программу поиска в глубину в программу шириной в первую очередь, при условии, что поиск в глубину напрямую сопоставляется с поиском Пролога.Этот метод называется итеративное углубление .

Просто добавьте дополнительный аргумент, чтобы гарантировать, что поиск будет идти только на N шагов.Таким образом, dfs-версия:

dfs(State) :-
   final(State).
dfs(State1) :-
   state_transition(State1, State2),
   dfs(State2).

преобразуется в bfs путем добавления аргумента для глубины.Например, используя :

bfs(State, _) :-
   final(State).
bfs(State1, s(X)) :-
   state_transition(State1, State2),
   bfs(State2, X).

Цель bfs(State,s(s(s(0)))) теперь найдет все деривации, требующие 3 или менее шагов.Вы все еще можете выполнять DFS!Просто используйте bfs(State,X).

Чтобы найти все деривации, используйте natural_number(X), bfs(State,X).

Часто полезно использовать список вместо номера s (X).Этот список может содержать все промежуточные состояния или выполненные конкретные переходы.

Возможно, вы не решитесь использовать эту технику, потому что кажется, что она пересчитывает много промежуточных состояний («многократно расширенных состояний»).В конце концов, сначала он ищет все пути за один шаг, затем не более двух шагов, затем не более трех шагов ... Однако, если ваша проблема связана с поиском, коэффициент ветвления, скрытый здесь в state_transition/2, уменьшит это.накладные расходы.Чтобы увидеть это, рассмотрим фактор ветвления 2: у нас будет только накладные расходы в два раза!Часто существуют простые способы восстановить этот фактор в два раза: например, путем ускорения state_transition/2 или final/1.

Но самое большое преимущество состоит в том, что он не занимает много места - в отличие отнаивные дфс.

2 голосов
/ 31 марта 2012

В дистрибутив Logtalk входит пример «поиск», который реализует структуру для поиска в пространстве состояний:

https://github.com/LogtalkDotOrg/logtalk3/tree/master/examples/searching

Включены «классические» проблемы (фермер, миссионеры иканнибалы, головоломка 8, мост, кувшины с водой и т. д.).Некоторые алгоритмы поиска адаптированы (с разрешения) из книги Ивана Братко «Программирование пролога для искусственного интеллекта».Пример также включает в себя монитор производительности, который может дать вам некоторые базовые статистические данные о производительности метода поиска (например, коэффициенты ветвления и количество переходов состояний).Фреймворк легко расширяется как для новых задач, так и для новых методов поиска.

1 голос
/ 01 апреля 2012

Я решил сначала с глубиной, а затем с широтой, пытаясь четко отделить повторно используемую часть от алгоритма поиска состояния:

miss_cann_dfs :-
    initial(I),
    solve_dfs(I, [I], Path),
    maplist(writeln, Path), nl.

solve_dfs(S, RPath, Path) :-
    final(S),
    reverse(RPath, Path).
solve_dfs(S, SoFar, Path) :-
    move(S, T),
    \+ memberchk(T, SoFar),
    solve_dfs(T, [T|SoFar], Path).

miss_cann_bfs :-
    initial(I),
    solve_bfs([[I]], Path),
    maplist(writeln, Path), nl.

solve_bfs(Paths, Path) :-
    extend(Paths, Extended),
    (   member(RPath, Extended),
        RPath = [H|_],
        final(H),
        reverse(RPath, Path)
    ;   solve_bfs(Extended, Path)
    ).

extend(Paths, Extended) :-
    findall([Q,H|R],
        (   member([H|R], Paths),
            move(H, Q),
            \+ member(Q, R)
        ), Extended),
    Extended \= [].

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% problem representation
% independent from search method
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

initial((3,3, >, 0,0)).
final((0,0, <, 3,3)).

% apply a *valid* move
move((M1i,C1i, Bi, M2i,C2i), (M1f,C1f, Bf, M2f,C2f)) :-
    direction(Bi, F1, F2, Bf),
    who_move(MM, CM),
    M1f is M1i + MM * F1, M1f >= 0,
    C1f is C1i + CM * F1, C1f >= 0,
    ( M1f >= C1f ; M1f == 0 ),
    M2f is M2i + MM * F2, M2f >= 0,
    C2f is C2i + CM * F2, C2f >= 0,
    ( M2f >= C2f ; M2f == 0 ).

direction(>, -1, +1, <).
direction(<, +1, -1, >).

% valid placements on boat
who_move(M, C) :-
    M = 2, C = 0 ;
    M = 1, C = 0 ;
    M = 1, C = 1 ;
    M = 0, C = 2 ;
    M = 0, C = 1 .

Я предлагаю вам структурировать свой код аналогичным образом, используя предикат, аналогичный extension / 2, который дает понять, когда следует прекратить поиск.

1 голос
/ 01 апреля 2012

Пожалуйста, обратитесь к этой сути, чтобы увидеть возможное решение, которое может помочь вашей проблеме.

Суть: решить Миссионеры и людоеды в Прологе

0 голосов
/ 14 января 2014

Если ваша система Prolog имеет передний цепной блок, вы также можете решить проблема, моделируя ее с помощью правил прямой цепочки. Вот пример того, как решить проблему кувшина с водой в Jekejeke Minlog. Состояние представлено предикатом состояния / 2.

Сначала вы даете правило, которое фильтрует дубликаты, следующим образом. правило говорит, что факт входящего состояния / 2 должен быть удален, если он уже находится в магазине форвардов:

% avoid duplicate state
unit &:- &- state(X,Y) && state(X,Y), !.

Затем вы даете правила, которые утверждают, что поиск не нужно продолжать когда конечное состояние достигнуто. В настоящем примере мы проверяем что в одном из сосудов содержится 1 литр воды:

% halt for final states
unit &:- state(_,1), !.
unit &:- state(1,_), !.

В качестве следующего шага моделируются переходы состояний как прямая цепочка правила. Это прямо вперед. Мы моделируем опорожнение, наполнение и розлив судов:

% emptying a vessel
state(0,X) &:- state(_,X).
state(X,0) &:- state(X,_).

% filling a vessel
state(5,X) &:- state(_,X).
state(X,7) &:- state(X,_).

% pouring water from one vessel to the other vessel
state(Z,T) &:- state(X,Y), Z is min(5,X+Y), T is max(0,X+Y-5).
state(T,Z) &:- state(X,Y), Z is min(7,X+Y), T is max(0,X+Y-7).

Теперь мы можем использовать механизм прямой цепочки, чтобы выполнить эту работу за нас. Это не будет делать итеративное углубление, и он также не будет делать ширину в первую очередь. Он просто сделает разрешение блока по стратегии, которая жадна для данный факт и процесс только завершается, так как пространство состояний конечно. Вот результат:

?- post(state(0,0)), posted.
state(0, 0).
state(5, 0).
state(5, 7).
state(0, 7).
Etc..

Подход покажет вам, есть ли решение, но не объяснит решение. Один из способов сделать это объяснимым заключается в использовании факта состояние / 4 вместо факта состояние / 2. Последние два аргумента используются для список действий и для длины списка.

Правило, которое избегает дубликатов, затем изменяется на правило, которое выбирает самое маленькое решение. Оно гласит:

% choose shorter path
unit &:- &- state(X,Y,_,N) && state(X,Y,_,M), M<N, !.
unit &:- state(X,Y,_,N) && &- state(X,Y,_,M), N<M.

Затем получаем:

?- post(state(0,0,[],0)), posted.
state(0, 0, [], 0).
state(5, 0, [fl], 1).
state(5, 7, [fr,fl], 2).
state(0, 5, [plr,fl], 2).
Etc..

С помощью небольшого предиката-помощника мы можем вызвать объяснение действия, которые ведут к пути:

?- post(state(0,0,[],0)), state(1,7,L,_), explain(L).
0-0
fill left vessel
5-0
pour left vessel into right vessel
0-5
fill left vessel
5-5
pour left vessel into right vessel
3-7
empty right vessel
3-0
pour left vessel into right vessel
0-3
fill left vessel
5-3
pour left vessel into right vessel
1-7

Bye

Исходный код: Water Jug State
http://www.xlog.ch/jekejeke/forward/jugs3.p

Исходный код: состояние и путь кувшина с водой
http://www.xlog.ch/jekejeke/forward/jugs3path.p

...