简体   繁体   中英

Java: Trouble when using BFS to find a shortest path

Class State :

import java.util.Arrays;

public class State {
private int data[][];

public int[][] getData() {
    return data;
}

public void setData(int[][] data) {
    this.data = data;
}
public void swap(int row1, int col1, int row2, int col2){
    int temp = this.data[row1][col1];
    this.data[row1][col1] = this.data[row2][col2];
    this.data[row2][col2] = temp;

}
public State copyState() {
    int height = this.data.length;
    int width = this.data[0].length;
    int[][] temp = new int[height][width];
    for (int i = 0; i < height; i++) {
        for(int j=0; j< width; j++){
            temp[i][j] = this.data[i][j];
        }
    }
    State target = new State(temp);
    return target;      
}

public State(int[][] data) {
    super();
    this.data = data;
}
}

Class Node :

public class Node {
private State state;
private Node parent;
private ArrayList<Node> children;

public Node(State state){
    this.state = state;
    parent = null;
    children = new ArrayList<>();
}

public State getState() {
    return state;
}
public void setState(State state) {
    this.state = state;
}
public Node getParent() {
    return parent;
}
public void setParent(Node parent) {
    this.parent = parent;
}
public ArrayList<Node> getChildren() {
    return children;
}
public void addChild(Node node){
    node.setParent(this);
    this.children.add(node);
}
public ArrayList<Node> returnSuccessor(){ // generate all possible moves(has been tested - work well)
    ArrayList<Node> result = new ArrayList<>();
    int[][] matrix = this.getState().getData();
    int row = matrix.length;
    int col = matrix[0].length;
    int rowX = 0;
    int colX = 0;

    for(int i=0; i<row; i++){
        for(int j=0; j<col; j++){
            if ( matrix[i][j] == 0) {
                rowX = i;
                colX = j;
            }
        }
    }

    if( (colX-1) >= 0 ){
        State temp = this.getState().copyState();
        temp.swap(rowX, colX, rowX, colX-1);
        Node left = new Node(temp);
        result.add(left);
    }
    if ( (colX+1) < col ){
        State temp = this.getState().copyState();
        temp.swap(rowX, colX, rowX, colX+1);
        Node right = new Node(temp);
        result.add(right);
    }
    if ( (rowX-1) >= 0 ){
        State temp = this.getState().copyState();
        temp.swap(rowX, colX, rowX-1, colX);
        Node top = new Node(temp);
        result.add(top);
    }
    if ( (rowX+1) < row ){
        State temp = this.getState().copyState();
        temp.swap(rowX, colX, rowX+1, colX);
        Node down = new Node(temp);
        result.add(down);
    }

    return result;
}


public void printState(){
    System.out.println(Arrays.deepToString(this.getState().getData()));
}

public boolean equal(Node node){ // check whether 2 nodes are the same
    return Arrays.deepEquals(this.getState().getData(), node.getState().getData());
}
public boolean checkTree(Node node){ // check whether a node has been added into the tree or not
    if (this.equal(node)) { 
        return true;
    }
    ArrayList<Node> children = this.getChildren();
    boolean result = false;
    if (children.size() > 0){
        for(int i=0; result == false && i< children.size(); i++){
            result = children.get(i).checkTree(node);
        }
    }
    return result;
}
}

Class main :

public class main {
public static void BFS(Node root, Node goal) throws InterruptedException{
Queue<Node> queue = new LinkedList<Node>();
queue.add(root);
while(queue.size()>0){
    Node temp = queue.poll();
    if (temp.equal(goal)) {
        goal.setParent(temp.getParent());
        break;
    }
    else{
        ArrayList<Node> successor = temp.returnSuccessor();
        for(int i=0; i< successor.size(); i++){
            boolean check = root.checkTree(successor.get(i));
            if (check == false){
                queue.add(successor.get(i));
                temp.addChild(successor.get(i));
            }
        }
    }
  }
}
public static void main(String[] args) throws InterruptedException {
    int[][] initialState = { {2,1}, {3,0} };
    int[][] goalState = { {0,1}, {2,3} };
    Node root = new Node(new State(initialState));
    Node goal = new Node(new State(goalState));
    BFS(root,goal);
    Node temp = goal;
    if(temp.getParent() ==  null){
        System.out.println("There is no such a way to go from the initial state to the goal state");
    }
    else{
        ArrayList<Node> listSteps = new ArrayList<>();
        while(temp != null){
            listSteps.add(temp);
            temp = temp.getParent();
    }
        for (int i=listSteps.size()-1; i>=0; i--){
            listSteps.get(i).printState();
            Thread.sleep(1000);
        }
        int numSteps = listSteps.size()-1;
        System.out.println("Number of steps: " + numSteps);
    }
}

I want to find a shortest path from the initial state to goal state ( nearly the same as n-puzzle game )

When i try running my program with 2x2 size puzzle as an input, it works well.

For example with input :

int[][] initialState = { {2,1}, {3,0} };
int[][] goalState = { {0,1}, {2,3} };

The result will be:

[[2, 1], [3, 0]]
[[2, 1], [0, 3]]
[[0, 1], [2, 3]]
Number of steps: 2

However, it has an infinite loop with nxn size(n>2)

Sample input:

int[][] initialState = { {7,2,4}, {5,0,6}, {8,3,1} };
int[][] goalState = { {0,1,2}, {3,4,5}, {6,7,8} };

It keeps adding nodes into the queue in DFS method

It makes me confused because the method checkTree in class Node has been written with the purpose to avoid loops which may happen when generate the states.

Can somebody figure out what my mistake is?

Late answer, but I hope it helps someone:
The basic issue in the code posted are these lines :

for(int i=0; i<row; i++){
    for(int j=0; j<col; j++){
        if ( matrix[i][j] == 0) {
           rowX = i;
           colX = j;
        }
    }
}

which result in checking "successor", or neighbors, only for the element (in state data) who`s value is 0. You need to check the neighbors of all elements. Note the comments in the code:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;

public class Main {

    public static void BFS(Node root, Node goal) throws InterruptedException{
        Queue<Node> queue = new LinkedList<>();
        Set<Node> visited = new HashSet<>(); //**use to flow which nodes were tested
        queue.add(root);
        while(queue.size()>0){
            Node temp = queue.poll();
            if (temp.equals(goal)) {
                goal.setParent(temp.getParent());
                break;
            }
            else{
                List<Node> successor = temp.returnSuccessor();
                for(int i=0; i< successor.size(); i++){
                    //** made redundant by using visited boolean check = root.checkTree(successor.get(i));
                    Node node = successor.get(i);
                    if (visited.add(node)){ //** successful add indicates it was not visited before
                        queue.add(node);
                        temp.addChild(node);
                    }
                }
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {

        int[][] initialState = { {0,1}, {2,3}, {4,5} };
        int[][] goalState = {{3,4}, {5,0}, {1,2}};

        Node root = new Node(new State(initialState));
        Node goal = new Node(new State(goalState));
        BFS(root,goal);

        if(goal.getParent() ==  null){
            System.out.println("There is no such a way to go from the initial state to the goal state");
        }
        else{
            ArrayList<Node> listSteps = new ArrayList<>();
            while(goal != null){
                listSteps.add(goal);
                goal = goal.getParent();
            }
            for (int i=listSteps.size()-1; i>=0; i--){
                System.out.println(listSteps.get(i));
            }
            int numSteps = listSteps.size()-1;
            System.out.println("Number of steps: " + numSteps);
        }
    }
}

class State {
    private int data[][];

    public int[][] getData() {  return data;}

    public void setData(int[][] data) { this.data = data;   }
    public void swap(int row1, int col1, int row2, int col2){
        int temp = data[row1][col1];
        data[row1][col1] = data[row2][col2];
        data[row2][col2] = temp;
    }

    public State copyState() { //**simpler version of
        int i = 0;
        int[][] temp = new int[data.length][];
        for (int[] row : data) {
            temp[i++] = Arrays.copyOf(row, row.length); //**simpler way to copy array
        }
        return new State(temp);
    }

    public State(int[][] data) {
        super();
        this.data = data;
    }
}

class Node {
    private State state;
    private Node parent;
    private ArrayList<Node> children;

    public Node(State state){
        this.state = state;
        parent = null;
        children = new ArrayList<>();
    }

    public State getState() {   return state; }
    public void setState(State state) { this.state = state; }
    public Node getParent() { return parent;}
    public void setParent(Node parent) { this.parent = parent;  }
    public ArrayList<Node> getChildren() {  return children;    }
    public void addChild(Node node){
        node.setParent(this);
        children.add(node);
    }

    public List<Node> returnSuccessor(){
        List<Node> result = new ArrayList<>();
        int[][] matrix = getState().getData();
        int row = matrix.length;
        int col = matrix[0].length;

        for(int i=0; i<row; i++){
            for(int j=0; j<col; j++){
                   /* need to check possible move for ALL nodes
                    * if ( matrix[i][j] == 0) {
                    rowX = i;
                    colX = j;
                   }*/

                    if( (j-1) >= 0 ){
                        State temp = getState().copyState();
                        temp.swap(i, j, i, j-1);
                        Node left = new Node(temp);
                        result.add(left);
                    }
                    if ( (j+1) < col ){
                        State temp = getState().copyState();
                        temp.swap(i, j, i, j+1);
                        Node right = new Node(temp);
                        result.add(right);
                    }
                    if ( (i-1) >= 0 ){
                        State temp = getState().copyState();
                        temp.swap(i, j, i-1, j);
                        Node top = new Node(temp);
                        result.add(top);
                    }
                    if ( (i+1) < row ){
                        State temp = getState().copyState();
                        temp.swap(i, j, i+1, j);
                        Node down = new Node(temp);
                        result.add(down);
                    }
            }
        }
        return result;
    }

    //override toString rather than creating
    @Override
    public String toString(){
        return Arrays.deepToString(getState().getData());
    }

    //**override equals rather than creating your own equal
    @Override
    public boolean equals(Object node){

           if (node == this) { return true; }
           if (node == null) { return false;}
           if (!(node instanceof Node)) {return false; }
        return Arrays.deepEquals(getState().getData(), ((Node)node).getState().getData());
    }

    //** override hashCode so each Node has a unique one
    @Override
    public int hashCode() {
        return toString().hashCode();
    }
}  

I also thing the terminology is a bit confusing. I think it would be cleared if every data element represented a Node, so int[][] initialState = { {2,1}, {3,0} }; represented 4 nodes. A collection of such nodes create a tree, which represents a unique State.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM