0

私たちに示唆されたアルゴリズムを使用可能なコードに変換しようとするのに苦労しています。ここに出口とともに8つの座標(N、NE、NW、S、SE、SW、E、W)を持つDirection列挙型が与えられます。

これはヒント付きのアルゴリズムです。

getPathToExit(row, col):
  if (row, col) is outside of the map:
    return an empty list
  else if (row, col) is an obstacle:
    return an empty list
  else if (row, col) is marked as visited or as deadend:
   return an emtpy list
  else if (row, col) is the exit:
    //optional: mark exit as visited
   return a list containing Direction.HERE
  else:
   //try to find a path from current square to exit:
    mark current square as visited (that is, part of path)
      for each neighbor of current square:
        path = path from neighbor to exit
          if path is not empty:
             add (direction to neighbor) to start of path
               return path
   //after for loop: no path exists from this square to exit
   mark current square as deadend
     return empty list

これは私がしばらく取り組んできたコードです:

public java.util.ArrayList<Direction> getPathToExit(){

for (int x=0; x<map.length; x++){
    for (int y=0; y<map[x].length; y++){
        if (map[x][y]=='S'){
            this.startRow=x;
            this.startCol=y;
        }
    }
}
System.out.println("start "+startRow+", "+startCol);
return getPathToExit(this.startRow, this.startCol);
}

private java.util.ArrayList<Direction> getPathToExit(int row, int col){

  Direction [] dirs = Direction.values();
  ArrayList<Direction> path = new ArrayList<Direction>();

getPathToExit(row, col);

    if (row < 0 || col < 0 || row > map.length || col > map[row].length){
        return null;
    }
    else if (map[row][col] != ' '){
        return null;
    }
    else if (map[row][col] == 'E'){
        path.add(Direction.HERE);
        return path;
    }
    else {
        for (int x=0; x<dirs.length-1; x++){
            int nextRow = row + dirs[x].getRowModifier();
            int nextCol = col + dirs[x].getColModifier();
            path = getPathToExit(nextRow, nextCol);
        }
    }
return path;
}

これは列挙型クラスです:

public enum Direction {
   N, NE, E, SE, S, SW, W, NW, HERE;

 /**
 * Returns the X/column change on the screen that is associated with
 * this direction: -1 for W, 0 for N/S, and +1 for E.
 */
  public int getColModifier() {
    int mod;
    switch (this) {
     case NW:
     case W:
     case SW:
     mod = -1;
    break;
  case NE:
  case E:
  case SE:
    mod = +1;
    break;
  default:
    mod = 0;
    break;
  }
return mod;
}

/**
 * Returns the Y/row change on the screen that is associated with
 * this direction: -1 for N, 0 for E/W, and +1 for south.
 */
public int getRowModifier() {
 int mod;
 switch (this) {
   case N:
   case NE:
   case NW:
    mod = -1;
    break;
  case S:
  case SE:
  case SW:
    mod = +1;
    break;
  default:
    mod = 0;
    break;
  }
return mod;
}

/** As {@link #getColModifier()} */
public int getXModifier() {
 return this.getColModifier();
}

 /** As {@link #getRowModifier()} */
public int getYModifier() {
  return this.getRowModifier();
}

/**
 * Returns the direction that is the opposite of this one.
 * For example, <code>Direction.NE.reverse() == Direction.SW</code>.
 * (The opposite of HERE is still HERE though.)
 */
public Direction reverse() {
  if (this == HERE) {
    return this;
  }else {
    int reversed = (this.ordinal() + 4) % 8;
   Direction[] dirs = Direction.values();
    return dirs[reversed];
  }
}

}

前もって感謝します。

4

1 に答える 1

2

コードには 2 つの問題があります。

(1) メインの for ループ:

for (int x=0; x<dirs.length-1; x++){
      int nextRow = row + dirs[x].getRowModifier();
      int nextCol = col + dirs[x].getColModifier();
      path = getPathToExit(nextRow, nextCol);
}

getPathToExit()再帰呼び出しが null 以外のリストを返したかどうかを確認する必要があります。ある場合はbreak、ループして、関連する方向を最初にプッシュする必要があります。あなたはすでにパスを見つけました - 残りをチェックし続けることを意味しません!


(2) アルゴリズムを完成させる (存在する場合は解決策を見つける) ためには、visitedセットを維持し、既にアクセスしたノードを再訪しないようにする必要があります。
次の例を見てください。

-------
|S |x1|
-------
|x2|E |
-------

ここで、すべて有効な正方形 (障害物なし) で、S が開始点、E が終了点です。

ここで、方向の順序が であるとしright,left, ...ます。

コード (visited設定なし) は次のことを行います。

go right (to x1).
go right - out of maze, go back.
go left (to S).
go right (to x1).
go right - out of maze, go back.
go left (to S)
....

あなたは無限ループに陥っています!(DFS の既知の欠点の 1 つ)
StackOverflowError は通常、これが問題であることを示しており、呼び出しスタックがすべての再帰呼び出しでいっぱいになり、エラーがスローされます。

visitedこれを修正するには、セットを維持し、既にアクセスしたノードに再度アクセスしないようにする必要があります。このセットと上記の迷路 (方向の順序はright, left, down, ...) を使用すると、次のようになります。

go right (to x1)
go right - out of maze, go back.
go left (to S) - already visitted, go back.
go down (to E) - found target, return it.

より高度な代替手段は、反復的深化 DFSを使用することです。これは基本的に、パスの長さを に制限し、lこれを繰り返し増加させることを意味しますl。今回はこの代替手段を無視します。これはもう少し高度です。


補足として、アルゴリズムはDFSの実装であり、訪問済みのセットと有限グラフ (存在する場合は常にソリューションを見つける) を備えていますが、最適ではありません (最短パスを見つけることを保証しません)。最短パスを見つけるには、代わりにBFSを使用することをお勧めします。

また: メソッドの 3 行目の再帰呼び出しは、デバッグ用の残り物だと思います。そこにあるべきではありません。

于 2012-09-28T08:31:19.130 に答える