问题
I am trying to avoid an infinite loop and am having trouble figuring out whats wrong. This is supposed to find a solution for a 3x2 puzzle board. I suspect the problem may be with my overridden equals method but I'm not sure. Running into two issues:
1) It keeps re-exploring already explored nodes.
2) The queue is empty before a solution is found, causing an error.
Driver class:
import java.util.*;
public class Driver {
public static void main(String[] args){
Node test = new Node(new int[]{1, 4, 2, 5, 3, 0}, null);
BFS(test);
System.out.println("done");
}
public static void BFS(Node initial){
Queue<Node> queue = new LinkedList<>();
ArrayList<Node> explored = new ArrayList<>();
queue.add(initial);
Node current = initial;
while (!current.isGoal()){
current = queue.remove();
for (Node child: current.getChildren()){
if (!explored.contains(child)) queue.add(child);
}
explored.add(current);
current.print();
}
System.out.println("DONEDONEDONE");
current.printTrace();
}
public static void DFS(Node initial){
}
}
Node class:
import java.lang.reflect.Array;
import java.util.*;
public class Node {
int[] state;
Node parent;
public Node(int[] initialState, Node parent){
this.parent = parent;
this.state = initialState;
}
public boolean isGoal(){
int[] goal = {0,1,2,3,4,5};
return Arrays.equals(this.state, goal);
}
public ArrayList<Node> getChildren(){
ArrayList<Node> children = new ArrayList<>();
Integer[] newInt = new Integer[getState().length];
for (int i = 0; i < getState().length; i++) {
newInt[i] = Integer.valueOf(getState()[i]);
}
int position = Arrays.asList(newInt).indexOf(0);
switch(position){
case 0:
children.add(new Node(switchPos(0,3), this));
children.add(new Node(switchPos(0,1), this));
break;
case 1:
children.add(new Node(switchPos(1,0), this));
children.add(new Node(switchPos(1,4), this));
children.add(new Node(switchPos(1,2), this));
break;
case 2:
children.add(new Node(switchPos(2,1), this));
children.add(new Node(switchPos(2,5), this));
break;
case 3:
children.add(new Node(switchPos(3,0), this));
children.add(new Node(switchPos(3,4), this));
break;
case 4:
children.add(new Node(switchPos(4,3), this));
children.add(new Node(switchPos(4,5), this));
children.add(new Node(switchPos(4,1), this));
break;
case 5:
children.add(new Node(switchPos(5,2), this));
children.add(new Node(switchPos(5,4), this));
break;
}
return children;
}
public int[] getState(){
return this.state;
}
public int[] switchPos(int index1, int index2){
int[] newer = getState().clone();
int temp = newer[index1];
newer[index1] = newer[index2];
newer[index2] = temp;
return newer;
}
public void print(){
System.out.println("---------");
System.out.println(Arrays.toString(Arrays.copyOfRange(getState(), 0, 3)));
System.out.println(Arrays.toString(Arrays.copyOfRange(getState(), 3, 6)));
System.out.println("---------");
}
public void printTrace(){
Stack<Node> stack = new Stack<>();
Node current = this;
while (current.parent != null){
stack.push(current);
current = current.parent;
}
while (!stack.isEmpty()){
stack.pop().print();
}
}
@Override
public boolean equals(Object object){
Node node2 = (Node) object;
return (Arrays.equals(node2.getState(), this.getState()));
}
}
回答1:
The only real bug in your code is that you don't check if the queue is empty in the while
condition, thus assuming that all states are available from any initial state (that's just not true).
I should also mention that marking the node as "explored" after the node is processed is not the best strategy, because duplicate nodes may be enqueued (from different parents), before any of them is processed. Note that they all will be printed as current
, though all of their children are already processed -- and this may look like your algorithm is re-exploring the same nodes. In fact, it doesn't. It's just wasting cycles, that's all.
Here's a better version of the driver that doesn't allow duplicates in a queue:
Queue<Node> queue = new LinkedList<>();
ArrayList<Node> explored = new ArrayList<>();
queue.add(initial);
Node current = initial;
explored.add(initial);
while (!queue.isEmpty() && !current.isGoal()){
current = queue.remove();
for (Node child: current.getChildren()){
if (!explored.contains(child)) {
queue.add(child);
explored.add(child);
}
}
current.print();
}
Essential is that each node is marked as "explored" when it is first pushed to the queue. However, it still does not reach the "goal", because it is really unreachable from this particular initial state.
来源:https://stackoverflow.com/questions/48492428/bfs-uninformed-search-issue