我在正在制作的编码难题中看到了一种奇怪的行为' Knights Path'。我生成一组可能的移动并将它们存储在HashSet中(Move类只有一个x,y坐标和一个标准的hashcode和equals)。当我在generateMoves()方法中使用HashSet时,程序找不到解决方案,而当我更改为LinkedHashSet时,它会这样做。
public static Collection<Move> generateMoves(int startX, int startY){
Set<Move> moves = new HashSet<Move>(); <-- doesn't work
public static Collection<Move> generateMoves(int startX, int startY){
Set<Move> moves = new LinkedHashSet<Move>(); <-- works
我知道HashSet并没有对迭代器元素的排序提供任何保证,但是移动的顺序对于使用回溯方法最终找到解决方案而言并不重要(移动的某些顺序将是比其他人更优化,但采用这种蛮力方法最终应考虑所有路径。)
显然,使用HashSet的Collection迭代器有一些时髦的东西,但我已经进行了多次测试,使用LinkedHashSet和HashSet比较每个棋盘位置的generateMoves的输出,它们是相同的。
下面的完整代码,任何指针都非常感激,因为我非常好奇地了解这里可能发生的事情!
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
public class KnightTour {
private static int countSteps = 0;
public static void main(String[] args){
int[][] board = new int[8][8];
board[0][0] = 1;
solveTour(board,0,0,1);
}
public static class Move{
@Override
public String toString() {
return "["+ x + "," + y + "]";
}
int x;
int y;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Move other = (Move) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
}
private static void printBoard(int[][] board){
System.out.println("---------");
for(int i=0;i <8;i++){
for(int j=0; j<8; j++){
if(board[i][j] != 0){
System.out.print('x');
}else{
System.out.print('0');
}
}
System.out.println('\r');
}
System.out.println("---------");
}
public static boolean solveTour(int[][] sol, int x, int y, int movei){
countSteps++;
if(countSteps%100000 == 0){
System.out.println("Count:"+countSteps);
}
Collection<Move> moves = generateMoves(x,y);
if(movei == 64){
printBoard(sol);
return true;
}
for(Move tryMove : moves){
int next_x = tryMove.x;
int next_y = tryMove.y;
if(isValidMove(sol, next_x,next_y)){
sol[next_x][next_y] = movei;
if(solveTour(sol, next_x, next_y, movei+1)){
return true;
}else{
sol[next_x][next_y] = 0;
}
}
}
return false;
}
public static boolean isValidMove(int[][] board, int destX, int destY){
if(destX < 0 || destX > 7 || destY < 0 || destY > 7){
return false;//Off the board!
}
return board[destX][destY] == 0;
}
public static Collection<Move> generateMoves(int startX, int startY){
Set<Move> moves = new HashSet<Move>();//Doesn't terminate
// Set<Move> moves = new LinkedHashSet<Move>();//Works with Linked
for(int i=-2; i<=2; i++){
for(int j=-2; j<=2; j++){
if(Math.abs(i) == Math.abs(j) || i == 0 || j==0 ){
//no op
}else{
Move m = new Move();
m.x = startX + i;
m.y = startY + j;
moves.add(m);
}
}
}
return moves;
}
}
答案 0 :(得分:0)
在我看来,您可能会看到的是LinkedHashMap
迭代的副作用,它保证按插入顺序迭代。 HashSet
没有相同的保证,可以按任何顺序返回结果。
我认为移动的顺序可能对您的算法很重要,也许是您通过遍历该顺序应用的偶然启发式。移动的随机排序可能会增加复杂性并引入许多新的和次优的路径以递归地进行探索。
HashSet
解决方案可能会完成,尝试使用小型电路板并查看是否从中获得结果可能会很有趣。