【问题标题】:15 puzzle with AStar Algorithm15 谜题 AStar 算法
【发布时间】:2018-11-07 18:48:13
【问题描述】:

我使用 A-star 算法和曼哈顿距离制作了一个简单的 15puzzle 游戏。 对于简单的问题,它可以工作,但解决方案不是最佳解决方案。

例如,如果一个动作是:

右->上

我的解决方案是:

右->上->左->下->右->上

如果我有一个很难解决的游戏,它会花费无限的时间并且无法解决问题,我认为是因为这个问题。

为了实现我的游戏,我遵循了 A* 算法的维基百科伪代码。 这是我的 AStar 函数:

public ArrayList<String> solution(Vector<Integer> start){

    ArrayList<String> movePath = new ArrayList<String>(); //Path to solution
    PriorityQueue<Node> closedQueue = new PriorityQueue<Node>(500,new Comparator<Node>() {
        @Override public int compare(Node a,Node b) {
            return a.get_fScore() - b.get_fScore();
        }
    });

    Node node = new Node(start,movePath,heuristic);
    int cnt =0;
    openQueue.add(node);

    while(!openQueue.isEmpty() ) {

        //Alt if it takes too much time (ToRemove)
        if(cnt == (150)*1000) {
            ArrayList<String> noResult = new ArrayList<String>();
            noResult.add("Timeout");
            return noResult;
        }

        Node bestNode = openQueue.remove(); //Remove best node from openQueue
        closedQueue.add(bestNode); //Insert its to closedQueue

        cnt++;
        if( cnt % 10000 == 0) {
            System.out.printf("Analizzo %,d posizioni. Queue Size = %,d\n", cnt, openQueue.size());
        }
        //Get first step from bestNode and add to movePath
        if(!bestNode.isEmptyMoves()) {
            String step = bestNode.get_moves().get(0);
            movePath.add(step);
        }
        //Exit Condition
        if(bestNode.get_hScore() == 0) {
            return bestNode.get_moves();
        }
        //Looking for childs
        Vector<Node> childs = get_nextMoves(bestNode);
        for(int i=0; i<childs.size(); i++) {

            if(closedQueue.contains(childs.elementAt(i))) 
                continue;

            childs.elementAt(i).set_gScore(bestNode.get_gScore()+1); //Increment level in tree

            if(!openQueue.contains(childs.elementAt(i)))
                openQueue.add(childs.elementAt(i));

            else {
                //!Never reached this level!
                System.out.println("Here!");
                //TODO Copy child from openQueue to closedQueue
            }

        }   
    }
    return null;

这是我寻找邻居的功能:

public Vector<Node> get_nextMoves(Node act){

    Vector<Node> steps = new Vector<Node>();
    int position = act.get_valuePos(0);
    String lastMove = act.get_lastMove();
    //System.out.println(lastMove);


    //Right Child
    if(position + 1 < 16 && position + 1!=3 && position + 1!=7 && position+1 !=11 && lastMove !="Left") {

        int temp_pos[] = copyToArray(act.get_posVect());//Copy array of positions of ACT to a temp_pos array
        temp_pos[position] = temp_pos[position+1]; //Switch 0 position with Right position
        temp_pos[position+1] = 0;

        ArrayList<String> temp_moves = new ArrayList<String>();
        for(int i=0; i<act.get_moves().size(); i++) {
            temp_moves.add(act.get_moves().get(i)); //Save old steps
        }
        temp_moves.add("Right");//And add new one

        Node child = new Node(temp_pos,temp_moves,act.get_heuristic()); //New Node
        steps.addElement(child);//Added to vector
    }
    //Left Child
    if(position - 1 >= 0 && position - 1 != 4 && position - 1 != 8 && position - 1 != 12 && lastMove !="Right") {
        int temp_pos[] = copyToArray(act.get_posVect());
        temp_pos[position] = temp_pos[position-1];
        temp_pos[position-1] = 0;

        ArrayList<String> temp_moves = new ArrayList<String>();
        for(int i=0; i<act.get_moves().size(); i++) {
            temp_moves.add(act.get_moves().get(i));
        }
        temp_moves.add("Left");

        Node child = new Node(temp_pos,temp_moves,act.get_heuristic());
        steps.addElement(child);
    }
    //Up Child
    if(position - 4 >= 0 && lastMove !="Down") {
        int temp_pos[] = copyToArray(act.get_posVect());
        temp_pos[position] = temp_pos[position-4];
        temp_pos[position-4] = 0;

        ArrayList<String> temp_moves = new ArrayList<String>();
        for(int i=0; i<act.get_moves().size(); i++) {
            temp_moves.add(act.get_moves().get(i));
        }
        temp_moves.add("Up");

        Node child = new Node(temp_pos,temp_moves,act.get_heuristic());
        steps.addElement(child);
    }
    //Down Child
    if(position + 4 < 16 && lastMove !="Up") {
        int temp_pos[] = copyToArray(act.get_posVect());
        temp_pos[position] = temp_pos[position+4];
        temp_pos[position+4] = 0;

        ArrayList<String> temp_moves = new ArrayList<String>();
        for(int i=0; i<act.get_moves().size(); i++) {
            temp_moves.add(act.get_moves().get(i));
        }
        temp_moves.add("Down");

        Node child = new Node(temp_pos,temp_moves,act.get_heuristic());
        steps.addElement(child);
    }
    return steps;

这就是我的曼哈顿距离函数:

public int calcolaDist(Vector<Integer> A) {
    int result = 0;
    Vector<Integer> goal_Mat = initialize_Mat();

    for(int i=0; i<16; i++) {
        int x_goal = (goal_Mat.indexOf(i))/4;
        int y_goal =  (goal_Mat.indexOf(i))%4;

        int x_def = (A.indexOf(i))/4;
        int y_def = (A.indexOf(i))%4;

        if(A.elementAt(i) > 0) {

            result += Math.abs(x_def - x_goal);
            result += Math.abs(y_def - y_goal);
        }
    }

    return result;

如果我的困惑是:

开始 = {1,3,0,4,5,2,7,8,9,6,10,11,13,14,15,12}

我的解决方案是:

[左、下、下、右、下、右、上、左、下、右、上、左、下、右]

我知道使用向量不是一个好的选择,而且我的代码“有点”脏,但我会在解决这个问题后立即清理它!

谢谢大家!

【问题讨论】:

  • 我稍后会仔细看看(还没有阅读你的代码)但我记得当我做这个完全相同的难题时:导致它无限循环的一个问题是我正在添加动作将在可用操作中返回父节点,也许就是这样。
  • 这是 A* 和 IDDFS 上的 helpful resource,用于解决 15 个谜题。 A* 使用大量内存并且通常会在 15 个谜题上崩溃,因此 IDDFS 或模式数据库是一个很好的解决方案。我建议对这篇文章进行一轮代码清理,以生成带有标记的MCVE,以识别您的问题区域。如果您想消除您提到的循环,您可能需要跟踪所有访问过的位置,而不是只检查长度为 1 的循环。
  • 谢谢你!这是大学作业,所以(不幸)我不能使用模式数据库。是的,我会尽快清理我的代码!!

标签: java algorithm a-star


【解决方案1】:

首先,我发现您的代码与 OPEN 和 CLOSED 队列有些混淆。 OPEN 队列应该是管理节点优先级的队列 (PriorityQueue)。 CLOSED 不需要这样做,它只存储访问过的节点及其成本(也许你的算法会更有效地通过 HashSetHashMap 更改 CLOSED 以避免对 CLOSED 中的节点进行排序)。我在您的代码中看不到您如何初始化 OPEN 队列,但这可能是您的 A* 实现的一个问题。

我在您的代码中看到的另一个问题是,对于基于 A* 的算法,您需要管理到达已经处于 OPEN/CLOSED 但成本不同的节点的情况。如果您访问来自不同父节点的节点,或者您进入循环,则可能会发生这种情况。如果您不考虑这一点,算法将无法正常工作。

  1. 如果您访问的节点已经在 OPEN 队列中,并且新节点的 f-score 较低,则应从 OPEN 中移除旧节点并插入成本较低的节点。
  2. 如果节点的成本较高(在 OPEN 或 CLOSED 中),那么您应该简单地丢弃该节点以避免循环。

问题是,但状态空间是有限的,算法应该在某个时候完成。我看到您的实现是用 Java 编写的。如果您查看库Hipster4j,它可能会对您有所帮助,其中包含an implementation of A*an example solving the 8-puzzle

希望我的回答有帮助。祝你好运!

【讨论】:

  • 您好阿德里安,感谢您的帮助!首先,是的,我不需要在 CLOSED 上使用 PriorityQueue。我正在看图书馆,但我不能在我的项目中使用它,因为它是大学作业。我的 OPEN 初始化是:openQueue= new PriorityQueue&lt;Node&gt;(1000, new Comparator&lt;Node&gt;() { @Override public int compare(Node a,Node b) { return a.get_fScore() - b.get_fScore(); }
  • 也许我对“如何找到”队列中的节点有点困惑。我使用compare的方法在CLOSED中搜索它(如果找到,跳转到下一步,不要在OPEN中添加neighb.),然后再次使用compare在OPEN中搜索,如果没有找到,添加到OPEN。否则,我还没有实现要做什么,我只是添加了一个打印,但它从未达到这一点。也许我应该以另一种方式搜索队列中的节点,这样我可以先搜索节点是否具有相同的元素数组,然后我可以比较它们的分数。那能行吗?再次感谢您!
  • 是的,那么OPEN的初始化是正确的!我们在库中查找节点是否在队列中的做法是存储两个分开的Map&lt;State, Node&gt;,一个用于打开,一个用于关闭。在这种情况下,State 将是您的数组,其中包含拼图的数字。对于 OPEN,我们也像您一样使用PriorityQueue。当我们想在 OPEN 中查找 Node 时,我们检查它是否在 Map 中,给定 State。因此,我们可以比较旧节点和新节点的成本。如果必须重新插入 OPEN 中的节点,我们更新Map,删除队列中的旧节点,然后插入新节点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-26
  • 1970-01-01
相关资源
最近更新 更多