Java 用DFS解8字谜

Java 用DFS解8字谜,java,artificial-intelligence,depth-first-search,sliding-tile-puzzle,state-space,Java,Artificial Intelligence,Depth First Search,Sliding Tile Puzzle,State Space,我正在寻找java代码,这些代码通过给定的初始状态为8字谜游戏实现DFS和BFS: 1 2 3 8 0 4 7 6 5 目标状态 2 8 1 0 4 3 7 6 5 我需要打印从初始状态到目标状态的解决方案路径(尚未完成) 这是我的密码。到目前为止,我只能实现DFS。到目前为止,我的程序所做的是,一旦找到目标状态,它就会输出成功。 然而,它从未达到这一点 有人能告诉我我出了什么问题吗?你不应该把已经添加到它的开放堆栈组合中。(另外,ArrayQue更好,Stack是一个旧类,请参见javad

我正在寻找java代码,这些代码通过给定的初始状态为8字谜游戏实现DFS和BFS:

1 2 3
8 0 4
7 6 5
目标状态

2 8 1
0 4 3
7 6 5
我需要打印从初始状态到目标状态的解决方案路径(尚未完成)

这是我的密码。到目前为止,我只能实现DFS。到目前为止,我的程序所做的是,一旦找到目标状态,它就会输出成功。 然而,它从未达到这一点


有人能告诉我我出了什么问题吗?

你不应该把已经添加到它的开放堆栈组合中。(另外,ArrayQue更好,Stack是一个旧类,请参见javadoc)

更完整和一致的后进先出堆栈操作集是 由Deque接口及其实现提供,应该 优先于此类使用。例如:

Deque stack=new ArrayDeque(); )

为了避免无数次地探索相同的状态,您必须使用集合作为关闭列表,并验证您试图添加到打开列表中的状态从未添加到关闭列表中

此外,使用byte[]数组(而不是int[]来节省内存)而不是字符串来执行操作可能会更舒服

总而言之,您可以这样构造代码:

public class Taquin {
    private byte[][] state = new byte[3][3];

    public Taquin(String s) { ... }
    public List<Taquin> successors() { ... }
    public boolean isSolvable(Taquin goal) { ... }
    //Necessary to use the Set !////////////
    public int hashCode() { ... }
    public boolean equals(Object o) { ... }
    public String toString() { ...state }
    ////////////////////////////////////////

    public void solve(Taquin goal) { 
        if (isSolvable(goal)) {
            Deque<Taquin> open   = new ArrayDeque<>();
            Set<Taquin>   closed = new HashSet<>();
            closed.add(this);
            open.add(this);

            Taquin current = this;
            //if isSolvable is correct you should never encounter open.isEmpty() but for safety, test it
            while (!current.equals(goal) && !open.isEmpty()) {
                current = open.pop();
                System.out.println(current);
                for (Taquin succ : current.successors())
                    //we only add to the open list the elements which were never "seen"
                    if (closed.add(succ))
                        open.add(succ);
            }
            System.out.println("Success");
        } else
            System.out.println("No solution");
    }
}
公共类Taquin{
专用字节[][]状态=新字节[3][3];
公共Taquin(字符串s){…}
公共列表继承者(){…}
公共布尔可解(Taquin目标){…}
//必须使用该设置////////////
public int hashCode(){…}
公共布尔等于(对象o){…}
公共字符串toString(){…state}
////////////////////////////////////////
公共无效解决(Taquin目标){
如果(可解(目标)){
Deque open=新的ARRAYDEFUE();
Set closed=新的HashSet();
已关闭。添加(此);
打开。添加(此);
塔昆电流=此;
//如果isSolvable是正确的,您永远不会遇到open.isEmpty(),但为了安全起见,请测试它
而(!current.equals(goal)&&!open.isEmpty()){
current=open.pop();
系统输出打印项次(当前);
for(Taquin成功:current.successivers())
//我们只在开放列表中添加从未“看到”的元素
如果(关闭。添加(成功))
打开。添加(成功);
}
System.out.println(“成功”);
}否则
System.out.println(“无解决方案”);
}
}

这样做的优点是,对于图形中的任何类型的搜索都是通用的。如果您想解决另一个难题,只需修改我没有实现的方法(实际上是节点接口的一部分)。如果您想更改算法(例如,经常用于8字谜的星星),您只需更改求解方法即可。我希望此代码示例将对您有所帮助。

好的,因此您的程序花费的时间比预期的要长。首先,我们想知道它是卡在一个无限循环中,还是很慢。为此,让程序通过向主循环添加以下内容来打印其进度:

    int statesVisited = 0;
    while (OPEN.empty() == false && STATE == false) {
        statesVisited++;
        System.out.println(statesVisited);
然后我们看到这个程序每秒访问了几千个州。由于我们的处理器每秒执行数十亿条指令,这意味着处理一个状态需要大约一百万条cpu指令。不应该那么高,是吗?那么是什么导致了这一切呢

一般来说,我们现在会使用一个分析器来测量这段时间花在代码的哪一部分上,但是由于程序很短,我们可以先尝试猜测。我的第一个猜测是,打印我们访问的每个州可能会非常昂贵。为了验证这一点,我们只打印第1000个州:

    while (OPEN.empty() == false && STATE == false) {
        statesVisited++;
        if (statesVisited % 1000 == 0) {
            System.out.println(statesVisited);
        }
我们注意到这一变化,我们注意到前5000个州的访问量低于第二个州,因此印刷业确实意义重大。我们还注意到一些奇怪的事情:虽然前5000个州的访问时间不到一秒钟,但出于某种原因,程序似乎越来越慢。在访问了20000个州后,访问1000个州需要大约一秒钟的时间,而且情况越来越糟。这是出乎意料的,因为处理状态不应该变得越来越昂贵。因此,我们知道我们的循环中的一些操作越来越昂贵。让我们回顾一下我们的代码,以确定它可能是哪个操作

无论集合的大小如何,推送和弹出都需要固定的时间。但您也可以使用Stack.search和LinkedList.contains。这两个操作都需要在整个堆栈或列表上迭代。那么,让我们输出这些集合的大小:

        if (statesVisited % 1000 == 0) {
            System.out.println(statesVisited);
            System.out.println(OPEN.size());
            System.out.println(CLOSED.size());
            System.out.println();
        }
等了一会儿,我们看到:

40000
25947
39999
因此,OPEN包含25000个元素,CLOSED包含近40000个元素。这解释了为什么处理状态的速度越来越慢。因此,我们希望选择具有更高效的contains操作的数据结构,例如
java.util.HashSet
java.util.LinkedHashSet
(它是散列集和链表的混合体,允许我们按添加顺序检索元素)。这样做,我们得到:

public static LinkedHashSet<String> OPEN = new LinkedHashSet<String>();
public static HashSet<String> CLOSED = new HashSet<String>();
public static boolean STATE = false;

public static void main(String args[]) {

    int statesVisited = 0;

    String start = "123804765";
    String goal = "281043765";
    String X = "";
    String temp = "";

    OPEN.add(start);

    while (OPEN.isEmpty() == false && STATE == false) {

        X = OPEN.iterator().next();
        OPEN.remove(X);

        int pos = X.indexOf('0'); // get position of ZERO or EMPTY SPACE
        if (X.equals(goal)) {
            System.out.println("SUCCESS");
            STATE = true;
        } else {
            // generate children
            CLOSED.add(X);

            temp = up(X, pos);
            if (!(temp.equals("-1")))
                OPEN.add(temp);
            temp = left(X, pos);
            if (!(temp.equals("-1")))
                OPEN.add(temp);
            temp = down(X, pos);
            if (!(temp.equals("-1")))
                OPEN.add(temp);
            temp = right(X, pos);
            if (!(temp.equals("-1")))
                OPEN.add(temp);
        }
    }

}

/*
 * MOVEMENT UP
 */
public static String up(String s, int p) {
    String str = s;
    if (!(p < 3)) {
        char a = str.charAt(p - 3);
        String newS = str.substring(0, p) + a + str.substring(p + 1);
        str = newS.substring(0, (p - 3)) + '0' + newS.substring(p - 2);
    }
    // Eliminates child of X if its on OPEN or CLOSED
    if (!OPEN.contains(str) && CLOSED.contains(str) == false)
        return str;
    else
        return "-1";
}

/*
 * MOVEMENT DOWN
 */
public static String down(String s, int p) {
    String str = s;
    if (!(p > 5)) {
        char a = str.charAt(p + 3);
        String newS = str.substring(0, p) + a + str.substring(p + 1);
        str = newS.substring(0, (p + 3)) + '0' + newS.substring(p + 4);
    }

    // Eliminates child of X if its on OPEN or CLOSED
    if (!OPEN.contains(str) && CLOSED.contains(str) == false)
        return str;
    else
        return "-1";
}

/*
 * MOVEMENT LEFT
 */
public static String left(String s, int p) {
    String str = s;
    if (p != 0 && p != 3 && p != 7) {
        char a = str.charAt(p - 1);
        String newS = str.substring(0, p) + a + str.substring(p + 1);
        str = newS.substring(0, (p - 1)) + '0' + newS.substring(p);
    }
    // Eliminates child of X if its on OPEN or CLOSED
    if (!OPEN.contains(str) && CLOSED.contains(str) == false)
        return str;
    else
        return "-1";
}

/*
 * MOVEMENT RIGHT
 */
public static String right(String s, int p) {
    String str = s;
    if (p != 2 && p != 5 && p != 8) {
        char a = str.charAt(p + 1);
        String newS = str.substring(0, p) + a + str.substring(p + 1);
        str = newS.substring(0, (p + 1)) + '0' + newS.substring(p + 2);
    }
    // Eliminates child of X if its on OPEN or CLOSED
    if (!OPEN.contains(str) && CLOSED.contains(str) == false)
        return str;
    else
        return "-1";
}

public static void print(String s) {
    System.out.println(s.substring(0, 3));
    System.out.println(s.substring(3, 6));
    System.out.println(s.substring(6, 9));
    System.out.println();
}
public static LinkedHashSet OPEN=new LinkedHashSet();
public static HashSet CLOSED=new HashSet();
公共静态布尔状态=false;
公共静态void main(字符串参数[]){
int statesVisited=0;
字符串start=“123804765”;
字符串目标=“281043765”;
字符串X=“”;
字符串temp=“”;
打开。添加(开始);
while(OPEN.isEmpty()==false&&STATE==false){
X=OPEN.iterator().next();
打开。移除(X);
int pos=X.indexOf('0');//获取零或空空间的位置
如果(X等于(目标)){
System.out.println(“成功”);
状态=真;
}否则{
//生子
已结束。添加(X);
温度=上升(X,位置);
如果(!(温度等于(“-1”))
打开。添加(临时);
SearchProblem p = 
  ProblemBuilder.create()
    .initialState(Arrays.asList(5,4,0,7,2,6,8,1,3))
    .defineProblemWithExplicitActions()
    .useActionFunction(new ActionFunction<Action, List<Integer>>() {
    @Override
    public Iterable<Action> actionsFor(List<Integer> state) {
        // Here we compute the valid movements for the state
        return validMovementsFor(state);
    }
    }).useTransitionFunction(new ActionStateTransitionFunction<Action, List<Integer>>() {
    @Override
    public List<Integer> apply(Action action, List<Integer> state) {
        // Here we compute the state that results from doing an action A to the current state
        return applyActionToState(action, state);
    }
    }).useCostFunction(new CostFunction<Action, List<Integer>, Double>() {
    @Override
    public Double evaluate(Transition<Action, List<Integer>> transition) {
        // Every movement has the same cost, 1
        return 1d;
    }
    }).build();
System.out.println(Hipster.createDijkstra(p).search(Arrays.asList(0,1,2,3,4,5,6,7,8)));