深度优先搜索错误

时间:2011-12-04 21:40:14

标签: java depth-first-search

我正在编写一个编程任务(在java中)来解决a fifteen-puzzle sort of thing。第一部分是使用深度优先搜索来找到解决方案。我希望它能够解决任意大的谜题,因此整个状态空间图可能不适合内存。相反,该算法将保留已探查状态列表以检查未来状态。算法是这样的:

如果我们处于目标状态,那么我们就完成了 如果没有,那么对于不在探索列表中的每个邻居,将其添加到探索列表中并递归深度优先搜索它。

然而,它无法正常工作。我做了一些测试,我相信问题是当我在探索列表中插入一个状态时,它实际上插入了相同的对象,这样当我稍后改变状态时,探索的列表也会发生变化。因此,程序似乎从未对某个州进行过探索。

我尝试编写一种方法来复制状态,并将副本传递给已探索的列表;但同样的问题仍然存在。但是,当我独立测试复制方法时,它似乎成功复制状态。我完全不知道问题是什么。

涉及很多代码,我不确定问题出在哪里,但这里是深度优先搜索的代码:

this class we're in is the puzzle class, and this is the depth first search
'state' keeps track of the current state we're in
'explored' is a linked list of explored states

public String depthFirst(){
    state.clearPath();            // state keeps track of the path taken
    explored = new LLNode(state); // create explored list with starting state
    this.dfs();                   // depth first search
    return state.path();
}

private boolean dfs(){
    if(state.equals(goal))        // if we're in the goal state, then done
        return true;
    char[] directions = {'n', 'e', 's', 'w'};
    for(char dir : directions){   // otherwise, for each direction
        if(state.move(dir)){      // if we can move in that direction, then do
            if(!explored.contains(state)){  // if the new state is unexplored
                explored = explored.insert(state.copy());
                if(this.dfs())              // add it to explored and recurse
                    return true;
            }
            switch(dir){                    // if search failed, move back and
                                            // search the next direction
                case 'n': state.move('s');
                case 'e': state.move('w');
                case 's': state.move('n');
                case 'w': state.move('e');
            }
        }
    }
    return false;  // indicates failure
}

以下是explored的插入和包含方法:

public LLNode insert(Node gameState){
    if(state == null){        // if this list node doesn't have a state
        state = gameState;    // then give it this one
        return this;
    }                         // otherwise tack it on the front of the list
    return new LLNode(gameState, this);  // and return the new head
}

public boolean contains(Node gameState){
    // I had a test here that printed a message if (state == gameState)
    // which was triggered when I ran the program, suggesting the problem
    // I explained above
    if(state.equals(gameState))
        return true;
    else if(next == null)
        return false;
    else
        return next.contains(gameState);
}

这是Node类的复制功能

public Node copy(){
    int[][] newState = new int[state.length][state[0].length];
    for(int i = 0; i < state.length; ++i)
        for(int j = 0; j < state[0].length; ++j)
            newState[i][j] = state[i][j];
    return new Node(newState);
}

感谢您的任何想法!

2 个答案:

答案 0 :(得分:1)

我会检查state.move()根本没有引用explored - 查看提供的代码,这是我能想象的唯一可以引用相同数组的变量。

滚动您自己的数据结构(即链接列表)是否需要分配?如果没有,那么我会使用标准的Java HashSet(或任何最适合您的要求),因为我看不出需要记住搜索的顺序,只有你已经探索过一个特定的事实。状态。

此外,是否有必要为每个探索状态存储整个数组?只记住每个探索状态的哈希值(可以存储在HashSet中)可能更好(消耗更少的内存并且通常更有效)。

注意:你的copy()方法假设一个NxN数组(这对你的例子来说很好,但如果拼图是矩形的话就行不了。)

答案 1 :(得分:0)

你确实知道那16个!是20,922,789,888,000 - 除非您正在做其他事情来限制搜索范围,否则您的探索状态表无法正常工作。