A * реализация всегда возвращает одно и то же значение - PullRequest
0 голосов
/ 18 сентября 2010

Я, кажется, схожу с ума или неправильно реализую алгоритм A *:

Ниже мой код, кажется, что независимо от того, какие значения я ввожу, он всегда будет возвращать 360. Я пропускаю здесь некоторую важную информацию? Также, прежде чем кто-либо спросит, да, это связано с заданием на машинное обучение, которое я получил.

публичный класс A_Star {

private ArrayList<SearchNode> closedNodes = new ArrayList<SearchNode>();
private ArrayList<SearchNode> openNodes = new ArrayList<SearchNode>();
private ArrayList<SearchNode> siblingNodes = new ArrayList<SearchNode>();
private ArrayList<SearchNode> obstacleNodes;
final SearchNode START_NODE = new SearchNode(0,115,655);
final SearchNode END_NODE = new SearchNode(0,380,560);
final int HEURISTIC = 1 * Math.abs((END_NODE.getxCoordinate() - START_NODE.getxCoordinate()) + (START_NODE.getyCoordinate() - END_NODE.getyCoordinate())) ;

private int cost = 0;
//Start: (115, 655) End: (380, 560)

public  int starSearch(SearchNode currentNode, Set<SearchNode> obstacleNodes) throws Exception  {
    //h(n) = D * (abs(n.x-goal.x) + abs(n.y-goal.y))

    boolean isMaxY = false;
    boolean isMaxX = false;
    int currentY = currentNode.getyCoordinate();
    int currentX = currentNode.getxCoordinate();
    System.out.println(currentNode.getxCoordinate() + " " + currentNode.getyCoordinate() + " Current Coordinates");

    int currentGScore = Math.abs(currentNode.getxCoordinate() - END_NODE.getxCoordinate()) + (currentNode.getyCoordinate() - END_NODE.getyCoordinate());

    currentNode.setgScore(currentGScore);
    System.out.println("Current node G score at entry: " + currentNode.getgScore());

    if(!closedNodes.contains(currentNode)){
        closedNodes.add(currentNode);

    }

    if(currentNode.getyCoordinate() == END_NODE.getyCoordinate()){
                isMaxY=true;
            }
         if(currentNode.getxCoordinate() == END_NODE.getxCoordinate())   {
                isMaxX =true;
            }

    SearchNode bottomCenter = new SearchNode(0,currentNode.getxCoordinate(), currentNode.getyCoordinate() -1);
   SearchNode middleRight = new SearchNode(0,currentNode.getxCoordinate() +1, currentNode.getyCoordinate() );
   SearchNode middleLeft = new SearchNode(0,currentNode.getxCoordinate() -1, currentNode.getyCoordinate());
    SearchNode topCenter = new SearchNode(0,currentNode.getxCoordinate(), currentNode.getyCoordinate()+1);
     int middleRightGScore = Math.abs(middleRight.getxCoordinate() - END_NODE.getxCoordinate()) + (middleRight.getyCoordinate() - END_NODE.getyCoordinate());
     int bottomCenterGScore = Math.abs(bottomCenter.getxCoordinate() - END_NODE.getxCoordinate()) + (bottomCenter.getyCoordinate() - END_NODE.getyCoordinate());
     int middleLeftGScore = Math.abs(middleLeft.getxCoordinate() - END_NODE.getxCoordinate()) + (middleLeft.getyCoordinate() - END_NODE.getyCoordinate());
     int topCenterGScore = Math.abs(topCenter.getxCoordinate() - END_NODE.getxCoordinate()) + (topCenter.getyCoordinate() - END_NODE.getyCoordinate());
    middleRight.setgScore(middleRightGScore);
     middleLeft.setgScore(middleLeftGScore);
     bottomCenter.setgScore(bottomCenterGScore);
     topCenter.setgScore(topCenterGScore);
     for(SearchNode obstacleNode:obstacleNodes){
         int obstacleX = obstacleNode.getxCoordinate();
         int obstacleY = obstacleNode.getyCoordinate();

          if((middleRight != null) && (middleRight.getxCoordinate() == obstacleX)){
        if(middleRight.getyCoordinate() == obstacleY){

           // throw new Exception();
            System.out.println("REMOVING AND NULLING: " + middleRight.toString());
            siblingNodes.remove(middleRight);
                  middleRight = null;

        }
    }



      if((middleLeft!=null)&&(middleLeft.getxCoordinate() == obstacleX)){
        if(middleLeft.getyCoordinate() == obstacleY){

            System.out.println("REMOVING AND NULLING: " + middleLeft.toString());
            siblingNodes.remove(middleLeft);
                  middleLeft=null;
        }
    } if((bottomCenter!=null) &&(bottomCenter.getxCoordinate() == obstacleX)){
        if(bottomCenter.getyCoordinate() == obstacleY){

            System.out.println("REMOVING AND NULLING: " + bottomCenter.toString());
            siblingNodes.remove(bottomCenter);
                  bottomCenter = null;
        }
    } if((topCenter!=null) && (topCenter.getxCoordinate() == obstacleX)){
        if(topCenter.getyCoordinate() == obstacleY){
                  System.out.println("REMOVING AND NULLING: " + topCenter.toString());
            siblingNodes.remove(topCenter);
                  topCenter=null;
        }
    }

    if((middleRight != null) && (middleRight.getxCoordinate() != obstacleX)){
        if(middleRight.getyCoordinate() != obstacleY){
                   System.out.println("ADDING THE FOLLOWING:  " + middleRight.toString());
                  siblingNodes.add(middleRight);
        }
    }



      if((middleLeft != null) && (middleLeft.getxCoordinate() != obstacleX)){
        if(middleLeft.getyCoordinate() != obstacleY){

                  siblingNodes.add(middleLeft);
        }
    } if((bottomCenter != null) && (bottomCenter.getxCoordinate() != obstacleX)){
        if(bottomCenter.getyCoordinate() != obstacleY){

                  siblingNodes.add(bottomCenter);
        }
    } if((topCenter !=null) && (topCenter.getxCoordinate() != obstacleX)){
        if(topCenter.getyCoordinate() != obstacleY){

                  siblingNodes.add(topCenter);
        }
    }
    }

    int highestScore = currentNode.getgScore();
    for(SearchNode node: siblingNodes){
          if(node == null){
             continue;
          }
        if(node.getxCoordinate() == END_NODE.getxCoordinate() && node.getyCoordinate() == END_NODE.getyCoordinate()){
            System.out.println("Returning cost: " + ++cost + " of: " + node.toString());
            return cost;
        }
       // System.out.println("Current node size: " + siblingNodes.size());
         if(node.getgScore() < highestScore){

             highestScore = node.getgScore();
             currentNode=node;
             cost++;
             System.out.println("Changed highest score: " + highestScore);
             System.out.println("Removing node: " + node.toString());
             siblingNodes.remove(node);
             System.out.println("Incrementing cost the Current Cost: " + cost);
             starSearch(currentNode,obstacleNodes);
             break;
         }

    if(isMaxY && isMaxX){
                  return cost;
    }
    }
    return cost;
    //Always move diagonal right downwards
}

public static void main(String[] args) throws Exception{
    System.out.println("Hello");
     A_Star star = new A_Star();
    HashSet<SearchNode> obstacles = new HashSet<SearchNode>();
    obstacles.add(new SearchNode(0,311,530));
    obstacles.add(new SearchNode(0,311,559));
    obstacles.add(new SearchNode(0,339,578));
    obstacles.add(new SearchNode(0,361,560));
    obstacles.add(new SearchNode(0,361,528));
    obstacles.add(new SearchNode(0,116, 655));
   int cost = star.starSearch(star.START_NODE, obstacles);
    System.out.println(cost);

    //((311, 530), (311, 559), (339, 578), (361, 560), (361, 528), (336, 516))
}

}

и

открытый класс SearchNode {

    private int xCoordinate;
    private int yCoordinate;
    private int cost;

public int getfScore() {
    return fScore;
}

public void setfScore(int fScore) {
    this.fScore = fScore;
}

private int fScore;

public int getgScore() {
    return gScore;
}

public void setgScore(int gScore) {
    this.gScore = gScore;
}

private int gScore;  

public SearchNode (int cost, int xCoordinate, int yCoordinate) {
this.cost = стоимость; * * один тысяча двадцать-одна this.xCoordinate = xCoordinate;
this.yCoordinate = yCoordinate;
}
public int getCost () { возврат стоимости; }

   public void setCost(int cost) {
       this.cost = cost;
   }



   public int getxCoordinate() {
       return xCoordinate;
   }

   public void setxCoordinate(int xCoordinate) {
       this.xCoordinate = xCoordinate;
   }

   public int getyCoordinate() {
       return yCoordinate;
   }

   public void setyCoordinate(int yCoordinate) {
       this.yCoordinate = yCoordinate;
   }

public String toString(){
    return "Y Coordinate: " + this.yCoordinate + " X Coordinate: " + this.xCoordinate + " G Score: " + this.gScore;
}

}

Ответы [ 2 ]

1 голос
/ 18 сентября 2010

Я предполагаю, что график представляет собой правильную прямоугольную сетку с узлами препятствий, через которые не должно пройти ни одно из решений. Более того, я предполагаю, что путешествие от узла к соседнему узлу равно 1. Также я понял, что Манхэттенское расстояние используется в качестве эвристического.

Учитывая это, я боюсь, что твое является неправильной реализацией.

Прежде всего, вместо рекурсивного вы должны использовать итеративный подход. Учитывая размер вашего графика, вы наверняка получите Stackoverflows, если это будет правильная реализация.

Во-вторых, кажется, есть проблема с понятиями GValue, HValue, FValue и / или стоимостью. Я чувствую себя обязанным дать неофициальное описание этих терминов.

Простая формула, которую использует A *: F = G + H, где

G - это текущая стоимость проезда от начального узла к текущему узлу. Итак, для начального узла значение Gvalue должно быть 0, любой узел, достижимый из startNode, должен иметь значение G, равное 1 (моё предположение было таким: при переходе от узла к соседнему узлу) я хотел бы подчеркнуть термин «в настоящее время» здесь, потому что gValue узла может измениться во время выполнения алгоритма.

H - эвристическая часть стоимости, обозначающая стоимость от текущего узла до конечного узла. В отличие от части G, значение H узла не меняется вообще (у меня есть сомнения, может быть такая эвристика?, А у вас нет, давайте продолжим), его следует вычислять только один раз. Ваша реализация, похоже, использует Манхэттенское расстояние в качестве эвристики, которая, безусловно, является лучшей эвристикой для такого графа. Но будьте осторожны, мой друг, здесь тоже есть небольшая проблема: абсолютные значения разностей следует брать отдельно и затем суммировать.

А F - это сумма этих значений, обозначающая возможные затраты на решение, проходящее от текущего узла (с учетом вашего графика и эвристики любое вычисленное значение F должно быть равно или меньше фактической стоимости, что хорошо).

Имея это под рукой, я бы использовал SearchNode так:

public class SearchNode {
    private int xCoordinate;
    private int yCoordinate;
    private double gScore;
    private double hScore;

    public double getfScore() {
        return gScore + hScore;
    }

    public double getgScore() {
        return gScore;
    }

    public void setgScore(int gScore) {
        this.gScore = gScore;
    }


    public SearchNode(int xCoordinate,int yCoordinate, double gScore, SearchNode endNode) {
        this.gScore=gScore;
        this.hScore = //Manhattan distance from this node to end node
        this.xCoordinate =xCoordinate;
        this.yCoordinate = yCoordinate;
    }

   public int getxCoordinate() {
       return xCoordinate;
   }

   public int getyCoordinate() {
       return yCoordinate;
   }
}

Тогда алгоритм может быть реализован так:

private ArrayList<SearchNode> closedNodes = new ArrayList<SearchNode>();
private ArrayList<SearchNode> openNodes = new ArrayList<SearchNode>();
//create the start and end nodes
SearchNode end = new SearchNode(380, 560, -1, null);
SearchNode start = new SearchNode(115,655, 0, end);


// add start node to the openSet

openNodes.Add(start);

while(openNodes.Count > 0) // while there still is a node to test
{
    // I am afraid there is another severe problem here.
    // OpenSet should be PriorityQueue like collection, not a regular Collection.
    // I suggest you to take a look at a Minimum BinaryHeap implementation. It has a logN complexity
    // of insertion and deletion and Constant Complexity access.

   // take the Node with the smallest FValue from the openSet. (With BinHeap constant time!)
    SearchNode current = openNodes.GetSmallestFvaluedNode(); // this should both retrieve and remove the node fromt he openset.

    // if it is the endNode, then we are node. The FValue (or the Gvalue as well since h value is zero here) is equal to the cost.
    if (current.EqualTo(end)) // not reference equality, you should check the x,y values
{
    return current.getfScore();
}

   //check the neighbourNodes, they may have been created in a previous iteration and already present in the OpenNodes collection. If it is the case, their G values should be compared with the currently calculated ones.
 // dont forget to check the limit values, we probably do not need nodes with negative or greater than the grid size coordinate values, I am not writing it
 // also here is the right place to check for the blocking nodes with a simple for loop I am not writing it either

  double neighbourGValue = current.getgScore() + 1;
 if (openNodes.Contains(current.getXCoordinate(), current.getYCoordinate() + 1))
  {
     // then compare the gValue of it with the current calculated value.
     SearchNode neighbour = openNodess.getNode(current.getXCoordinate(), current.getYCoordinate() + 1);
     if(neighbour.getgScore() > neighbourGValue)
        neighbour.setgScore(neighbourGValue);
  }
  else if(!closedNodes.Contains(current.getXCoordinate(), current.getYCoordinate()))
  {
      // create and add a fresh Node
     SearchNode n = new SearchNode(current.getXCoordinate(), current.getYCoordinate() + 1, neighbourGValue, endNode);
     openNodes.Add(n);
  }
  // do the same for the other sides : [x+1,y - x-1,y - x, y-1]

  // lastly add the currentNode to the CloseNodes.
  closedNodes.Add(current);
}

// if the loop is terminated without finding a result, then there is no way from the given start node to the end node.
 return -1;

Единственный вопрос, который возникает у меня в связи с приведенной выше реализацией, - это строка

if (openNodes.Contains(current.getXCoordinate(), current.getYCoordinate() + 1))

Даже если открытый набор реализован как Min Binary Heap, нет простого способа проверить неминимальные f-значные узлы. Я не могу вспомнить детали сейчас, но я помню реализацию этого со сложностью logN. Более того, моя реализация осуществляла доступ и изменение значения g этого узла (если это необходимо) за один вызов, поэтому вы не потратили время на его восстановление. Независимо от того, было ли изменено значение g, если есть узел с заданными координатами, он возвращал значение true, поэтому новый узел не создавался.

Последнее, что я понял, когда закончил писать все это. Вы сказали, что при любом вводе данных ваша реализация вычисляла тот же результат. Хорошо, если вход, о котором вы говорите, является узлами препятствий, то в большинстве случаев он найдет, независимо от того, что это за реализация, одинаковое расстояние, так как ищет минимально возможное расстояние. На изображении ниже я пытался это объяснить.

alt text

1 голос
/ 18 сентября 2010

Ниже мой код, кажется, что нет независимо от того, какие значения я ввожу это будет всегда возвращай 360.

Мое первое предположение состоит в том, что у вас есть фиксированная эвристическая стоимость для каждого узла. Так может ли это 360 прийти?

final SearchNode START_NODE = new SearchNode(0,115,655);
final SearchNode END_NODE = new SearchNode(0,380,560);

Предполагается, что вы используете эвристику Манхэттенского расстояния, (380-115) + (655-560) = 265 + 95 = 360

Код немного сложен для чтения из-за его форматирования. Но я предполагаю, что вы вычислили значение h для начального узла, и после этого вы используете его для каждого узла. Помните, что h (x) <= d (x, y) + h (y) и вычислите его для каждого расширения узла. </p>

...