我正在使用Java创建一个解决n-puzzle的程序,而不使用启发式算法,只需使用深度优先和广度优先搜索状态空间。我正在努力实现深度优先搜索。有时它会解决给定的谜题,但有时它似乎会早早放弃。
这是我的DFS课程。 DepthFirstSearch()传递一个PuzzleBoard,它最初是通过改组已解决的板来生成的(以确保该板处于可解决的状态)。
public class DepthFirst {
static HashSet<PuzzleBoard> usedStates = new HashSet<PuzzleBoard>();
public static void DepthFirstSearch(PuzzleBoard currentBoard)
{
// If the current state is the goal, stop.
if (PuzzleSolver.isGoal(currentBoard)) {
System.out.println("Solved!");
System.exit(0);
}
// If we haven't encountered the state before,
// attempt to find a solution from that point.
if (!usedStates.contains(currentBoard)) {
usedStates.add(currentBoard);
PuzzleSolver.print(currentBoard);
if (PuzzleSolver.blankCoordinates(currentBoard)[1] != 0) {
System.out.println("Moving left");
DepthFirstSearch(PuzzleSolver.moveLeft(currentBoard));
}
if (PuzzleSolver.blankCoordinates(currentBoard)[0] != PuzzleSolver.n-1) {
System.out.println("Moving down");
DepthFirstSearch(PuzzleSolver.moveDown(currentBoard));
}
if (PuzzleSolver.blankCoordinates(currentBoard)[1] != PuzzleSolver.n-1) {
System.out.println("Moving right");
DepthFirstSearch(PuzzleSolver.moveRight(currentBoard));
}
if (PuzzleSolver.blankCoordinates(currentBoard)[0] != 0) {
System.out.println("Moving up");
DepthFirstSearch(PuzzleSolver.moveUp(currentBoard));
}
return;
} else {
// Move up a level in the recursive calls
return;
}
}
}
我可以断言我的moveUp(),moveLeft(),moveRight()和moveDown()方法和逻辑正常工作,所以问题必须在其他地方。
这是我的PuzzleBoard对象类,带有hashCode和equals方法:
static class PuzzleBoard {
short[][] state;
/**
* Default constructor for a board of size n
* @param n Size of the board
*/
public PuzzleBoard(short n) {
state = PuzzleSolver.getGoalState(n);
}
public PuzzleBoard(short n, short[][] initialState) {
state = initialState;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.deepHashCode(state);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
PuzzleBoard other = (PuzzleBoard) obj;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (state[i][j] != other.state[i][j])
return false;
}
}
return true;
}
}
如前所述,有时搜索工作正常并找到解决方案的路径,但有时它会在找到解决方案之前和内存耗尽之前停止。
以下是输出的片段,在搜索停止搜索之前开始几步。
...
Moving down
6 1 3
5 8 2
0 7 4
Moving right
6 1 3
5 8 2
7 0 4
Moving left
Moving right
Moving up
6 1 3
5 0 2
7 8 4
Moving left
Moving down
Moving right
Moving up
Moving up
Moving right
Moving down
Moving up
Moving down
Moving up
Moving down
Moving up
Moving down
Moving up
Moving down
...
为了简洁起见,我提前将其截断了,但它最终只是上下移动数十次而且从未达到解决状态。
有人能说清楚我做错了吗?
修改:这是MoveUp()。其余的移动方法以相同的方式实现。
/**
* Move the blank space up
* @return The new state of the board after the move
*/
static PuzzleBoard moveUp(PuzzleBoard currentState) {
short[][] newState = currentState.state;
short col = blankCoordinates(currentState)[0];
short row = blankCoordinates(currentState)[1];
short targetCol = col;
short targetRow = row;
newState[targetCol][targetRow] = currentState.state[col - 1][row];
newState[targetCol - 1][targetRow] = 0;
return new PuzzleBoard(n, newState);
}
答案 0 :(得分:0)
我在hashset中遇到过很多问题,最好的办法是不要将对象存储在hashset中,而是尝试将对象编码为字符串。
这是一种方法: -
StringBuffer encode(PuzzleBoard b) {
StringBuffer buff = new StringBuffer();
for(int i=0;i<b.n;i++) {
for(int j=0;j<b.n;j++) {
// "," is used as separator
buff.append(","+b.state[i][j]);
}
}
return buff;
}
Make two changes in the code:-
if(!usedStates.contains(encode(currentBoard))) {
usedStates.add(encode(currentBoard));
......
}
注意: - 这里不需要编写自己的哈希码函数&amp;也不需要实现equals函数,因为java已经在StringBuffer中为你完成了。
答案 1 :(得分:0)
我在实施中遇到了一个问题: -
在以下代码中: -
static PuzzleBoard moveUp(PuzzleBoard currentState) {
short[][] newState = currentState.state;
short col = blankCoordinates(currentState)[0];
short row = blankCoordinates(currentState)[1];
short targetCol = col;
short targetRow = row;
newState[targetCol][targetRow] = currentState.state[col - 1][row];
newState[targetCol - 1][targetRow] = 0;
return new PuzzleBoard(n, newState);
}
这里使用的是来自currentState.state的相同数组的引用,因此当你对newState进行更改时,你的currentState.state也会改变,这会在调用返回时影响DFS。为了防止你应该初始化一个新数组。继承人该怎么做: -
static PuzzleBoard moveUp(PuzzleBoard currentState) {
short[][] newState = new short[n][n];
short col = blankCoordinates(currentState)[0];
short row = blankCoordinates(currentState)[1];
short targetCol = col;
short targetRow = row;
for(int i=0;i<n;i++) {
for(int j=0;j<n;j++) {
newState[i][j] = currentState.state[i][j];
}
}
newState[targetCol][targetRow] = currentState.state[col - 1][row];
newState[targetCol - 1][targetRow] = 0;
return new PuzzleBoard(n, newState);
}
对所有提升,移动....进行此更改。
此外,我不认为你的hashset工作正常,因为如果是,那么你总是会在hashset中找到你的新状态,你的程序就会停止。在equals中,您将状态数组与相同的引用进行比较,因此将始终为true。请尝试使用我的编码函数作为哈希。