关于搜索所有可能路径的Prolog问题

huangapple go评论56阅读模式
英文:

Prolog issue about searching all possible paths

问题

I am new in Prolog. I am trying to develop a Program which goal is, given the X,Y coordinate of a trophy (specified in the fact trofeo(4,3)), and the coordinates of some obstacles (specified in the fact ostacolo(,)), the Program has to find the shortest path between the initial fixed coordinates(0,0) and the trophy, avoiding obstacles and without returning in visited coordinates.

For each rule you will find a comment explaining it. The main problem is that now, I am trying to generate all the possible paths.

The fact is that if I use only the move up and move right actions, (using only the rules X1 is X+1, Y1 is Y and X1 is X, Y1 is Y+1), and without using the rule about the visited position, so without the line: ( \+(visitato(X1,Y1))-> assert(visitato(X1, Y1))), the program gives as output all the possible paths. (For example, in the simple case here, it will return Paths = [[(0, 0), (0, 1), (1, 1)], [(0, 0), (1, 0), (1, 1)]].)

While if I add all the possible actions, and the rule: (\+(visitato(X1,Y1))-> assert(visitato(X1, Y1))), the program finds only one of the paths, for example, gives as an output only: Paths = [[(0, 0), (1, 0), (0, 0), (0, 1), (1, 1)]]. (it shows the right coordinates, but (0,0) twice, because it is not asserted as visited and all in one path).

Thanks in advance to everyone.

英文:

I am new in Prolog. I am trying to develop a Program which goal is, given the X,Y coordinate of a trophy (specified in the fact trofeo(4,3)), and the coordinates of some obstacles (specified in the fact ostacolo(,)), the Program has to find the shorthest path between the initial fixed coordinates(0,0) and the trophy, avoiding obstacles and without returning in visited coordinates.

For each rule you will find a comment explaining it. The main problem is that now, I am trying to generate all the possible paths.
The fact is that if I use only the move up and move right actions, (using only the rules X1 is X+1, Y1 is Y and X1 is X, Y1 is Y+1), and without using the rule about the visited position, so without the line: ( \+(visitato(X1,Y1))-> assert(visitato(X1, Y1))),
the program gives as output all the possible paths.(For example,in the simple case here, it will return Paths = [[(0, 0), (0, 1), (1, 1)], [(0, 0), (1, 0), (1, 1)]].)

While if I add all the possible actions, and the rule:
(\+(visitato(X1,Y1))-> assert(visitato(X1, Y1))),
the program find only one of the paths, for example gives as an output only: Paths = [[(0, 0), (1, 0), (0, 0), (0, 1), (1, 1)]]. (it shows the right coordinates, but (0,0) twice, because is not assterted as visited and all in one path).

Thanks in advance to everyone.

% to calculate the max
max([X],X):-!.
max([X|T],X):- max(T,N),X>=N,!.
max([X|T],N):- max(T,N).

:- dynamic visitato/2.

ostacolo(2,0).
trofeo(1,1).

% with this I am finding the maximum X and Y value between obstacles and trophy, to set a limit in the "grid".

max_coordinate(MaxX, MaxY) :-
    findall(X, ostacolo(X, _), XOstacoli),
    findall(Y, ostacolo(_, Y), YOstacoli),
    findall(X, trofeo(X, _), XTesoro),
    findall(Y, trofeo(_, Y), YTesoro),
    append(XTesoro, XOstacoli, ListaX),
    append(YTesoro, YOstacoli, ListaY),
    max(ListaX,MaxX),
    max(ListaY,MaxY).

%every X,Y respects the limit if they are lower or equal to the maximum X and Y calculated before

limite(X, Y) :-
  max_coordinate(MaxX, MaxY),
  X =< MaxX,
  Y =< MaxY.

% base case for recursion
path((X,Y), (X,Y), [(X,Y)], _ ).
% Limit is a constant to not exceed the Stack limit, adiacente is the rule written below
path((X,Y), Trofeo, [(X,Y)|Path], Limit) :- 
  Limit > 0,
  adiacente((X,Y), (X1,Y1)),
  NewLimit is Limit - 1,
  path((X1,Y1), Trofeo, Path,NewLimit).
  
% I recall in the console ?- trofeo(X,Y), find_all_paths((0,0),(X,Y), Paths), to search for the paths

find_all_paths(Start, Trofeo, Paths) :-
   setof(Path, path(Start, Trofeo, Path, 20), Paths).

% basically with this we are saying that there are four possible actions: move up, right, left, down. The coordinates evaluated must be positive(rule positivo), respect the limit(rule limite), must be not an obstacle(rule \+obstacle), and if they are not yet visited(rule \+(visitato(X1,Y1)), then we will insert a fact, that will state that the current X1,Y1 coordinate was visited.

adjacent((X, Y), (X1, Y1)) :-
  (X1 is X, Y1 is Y - 1),
   positivo(X1,Y1),
   limite(X1,Y1),
   \+ostacolo(X1,Y1),
   (   \+(visitato(X1,Y1))-> assert(visitato(X1, Y1))).
adjacent((X, Y), (X1, Y1)) :-
    (X1 is X-1, Y1 is Y),
    positivo(X1,Y1),
    limite(X1,Y1),
   \+ostacolo(X1,Y1),
   (   \+(visitato(X1,Y1))-> assert(visitato(X1, Y1))).
adjacent((X, Y), (X1, Y1)) :- 
    (X1 is X + 1, Y1 is Y),
     positivo(X1,Y1),
     limite(X1,Y1),
     \+ostacolo(X1,Y1),
   (  \+(visitato(X1,Y1))-> assert(visitato(X1, Y1))).
adjacent((X, Y), (X1, Y1)) :-
   (X1 is X, Y1 is Y + 1),
    positivo(X1,Y1),
    limite(X1,Y1),
   \+ostacolo(X1,Y1),
    (  \+(visitato(X1,Y1))-> assert(visitato(X1, Y1))).

% establish that all the coordinates must be positive
positivo(X,Y) :-
    X >= 0, Y >= 0.

</details>


# 答案1
**得分**: 1

以下是已翻译的内容:

"assert/1"在您的代码中的主要问题可能是使用,因为在搜索期间正确同步**状态**相当困难。更容易的方法是维护一个已访问(或更好地说,**禁止**)坐标的列表,每次在棋盘上行走时都会更新。因此,我建议开始编写一个next_position/3实用谓词,例如
```prolog
next_position(CurrentPosition, ForbiddenPositions, NextPosition) :-
  member(Offset, [(0, 1), (1, 0), (0, -1), (-1, 0)]),
  CurrentPosition = (Cx, Cy),
  Offset = (Ox, Oy),
  NextPosition = (Nx, Ny),
  Nx is Cx + Ox,
  Ny is Cy + Oy,
  \+ memberchk(NextPosition, ForbiddenPositions).

这个谓词在每一步都会列举(在回溯时)可用的位置(注意它需要游乐场边界的坐标,否则它将无限扩展)。

接下来,您可以专注于单个路径,只有在获得它之后才开始担心所有路径...

访问图的更容易编码使用Prolog的搜索能力。类似于

find_path(Target, Target, _Forbidden, [Target]).
find_path(From, Target, Forbidden, [From|Rest]) :-
  From \= Target,
  next_position(From, Forbidden, Next),
  find_path(Next, Target, [From|Forbidden], Rest).

对于一个非常简单的测试,可以提供一些图形化的数据渲染(上面的代码仅适用于非常小的问题...):

test(Path) :-
    Border = [
        (-1, -1), (0, -1), (1, -1), (2, -1), (3, -1),
        (-1, 0),        /* xx */        (3, 0),
        (-1, 1),                        (3, 1),
        (-1, 2),/* yy */                (3, 2),
        (-1, 3),                /* $$ */(3, 3),
        (-1, 4), (0, 4), (1, 4), (2, 4), (3, 4)
    ],
    Start = (0, 0),
    Obstacles = [(1, 0), (0, 2)],
    Trophy = (2, 3),
    append(Border, Obstacles, Forbidden),
    find_path(Start, Trophy, Forbidden, Path).
英文:

The main problem in your code could be the use of assert/1, since it is rather difficult to correctly keep the state syncronized during the search. It's far easier to keep a list of visited (or better, forbidden) coordinates, that gets updated every time you step in your board. So, I suggest to start writing a next_position/3 utility predicate, for instance

next_position(CurrentPosition,ForbiddenPositions,NextPosition) :-
  member(Offset,[(0,1),(1,0),(0,-1),(-1,0)]),
  CurrentPosition=(Cx,Cy),
  Offset=(Ox,Oy),
  NextPosition=(Nx,Ny),
  Nx is Cx+Ox,
  Ny is Cy+Oy,
  \+ memberchk(NextPosition,ForbiddenPositions).

that enumerate (on backtracking) the positions available at every step (note it needs the coordinates of the border of the playground, or it will go to infinity).

Next, you could concentrate on a single path, and only after you got it, start to worry about all paths...

The easier coding for visiting a graph uses the search capability of Prolog. Something like

find_path(Target,Target,_Forbidden,[Target]).
find_path(From,Target,Forbidden,[From|Rest]) :-
  From \= Target,
  next_position(From,Forbidden,Next),
  find_path(Next,Target,[From|Forbidden],Rest).

It's possibile to give a somewhat graphical data rendering for a very simple test (the code above will work only on really small problems...):

test(Path) :-
    Border = [
        (-1,-1),( 0,-1),( 1,-1),( 2,-1),( 3,-1),
        (-1, 0),        /* xx */        ( 3, 0),
        (-1, 1),                        ( 3, 1),
        (-1, 2),/* yy */                ( 3, 2),
        (-1, 3),                /* $$ */( 3, 3),
        (-1, 4),( 0, 4),( 1, 4),( 2, 4),( 3, 4)
    ],
    Start = ( 0, 0),
    Obstacles = [( 1, 0), (0, 2)],
    Trophy = ( 2, 3),
    append(Border,Obstacles,Forbidden),
    find_path(Start,Trophy,Forbidden,Path).

huangapple
  • 本文由 发表于 2023年2月8日 18:45:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/75384641.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定