Java *8拼图的运行时间太长

Java *8拼图的运行时间太长,java,a-star,heuristics,Java,A Star,Heuristics,如果这是一个很长的问题,我很抱歉,但我不确定我的a*8拼图java代码是否有效。。。我发现我的代码对于简单的输入运行得很好(很容易平均),但我不知道它是否在最坏的情况下工作 我试图修改我的代码,以使用每个节点的曼哈顿距离作为我的启发式函数,我的代码即使在最坏的情况下也能工作,但它只是需要太长时间。。。当我使用“错位瓷砖数量”作为启发式函数时,与使用曼哈顿距离相比,我的代码运行简单到平均情况所需的时间更长。即使在15分钟后,它也不能为最坏情况提供解决方案 注:在最坏的情况下,一个8字谜的解题步骤不

如果这是一个很长的问题,我很抱歉,但我不确定我的a*8拼图java代码是否有效。。。我发现我的代码对于简单的输入运行得很好(很容易平均),但我不知道它是否在最坏的情况下工作

我试图修改我的代码,以使用每个节点的曼哈顿距离作为我的启发式函数,我的代码即使在最坏的情况下也能工作,但它只是需要太长时间。。。当我使用“错位瓷砖数量”作为启发式函数时,与使用曼哈顿距离相比,我的代码运行简单到平均情况所需的时间更长。即使在15分钟后,它也不能为最坏情况提供解决方案

注:在最坏的情况下,一个8字谜的解题步骤不超过31步

。。。以下是我的代码的主要功能:

    List<Node> nodeList = new ArrayList<Node>();
    nodeList.add(startNode); //"Node startNode" contains the root node of the tree that will be produced
    Node currentNode = null;
    while (1 == 1) {            
        //THIS SECTION FINDS THE LEAF NODE WITH THE LEAST f(n)
        currentNode = null;
        for (Node pickNode : nodeList) {
            if (pickNode.isLeaf == true) {
                if (currentNode == null)
                    currentNode = pickNode;

                else if (pickNode.fn < currentNode.fn){
                    currentNode = pickNode;
                }
            }
        }
    /*-----------------------------------------------------------*/ 
        //BREAK THE LOOP WHEN THE SOLUTION IS FOUND
        if (Arrays.deepEquals(currentNode.state, goalState))
            break;
    /*-----------------------------------------------------------*/     
        else {
            int xcheck = currentNode.zeroX;
            int ycheck = currentNode.zeroY;
            int switcher;
            int approve = 1;
        /*-----------------------------------------------------------*/ 
             //THE FOLLOWING LINES DETERMINES WHICH CHILDREN CAN BE PRODUCED BY A NODE
             if ((ycheck - 1) >= 0) {
                int subState[][] = new int [3][];
                subState[0] = currentNode.state[0].clone();
                subState[1] = currentNode.state[1].clone();
                subState[2] = currentNode.state[2].clone();
                switcher = subState[ycheck-1][xcheck];
                subState[ycheck-1][xcheck] = 0;
                subState[ycheck][xcheck] = switcher;

                Node checkerNode = new Node();
                checkerNode = currentNode;
                while (checkerNode != null) {
                    if (Arrays.deepEquals(subState, checkerNode.state)) {
                        approve = 0;
                        break;
                    }
                    checkerNode = checkerNode.parentNode;
                }

                if (approve != 0) {
                    Node childNode = new Node();
                    childNode.state = subState;
                    childNode.totalPath = currentNode.totalPath + "*" + "up";
                    childNode.gn = currentNode.gn + 1;
                    childNode.hn = computeHn(childNode.state, goalState);
                    childNode.fn = childNode.gn + childNode.hn;
                    childNode.isLeaf = true;
                    childNode.parentNode = currentNode;
                    childNode.zeroX = xcheck;
                    childNode.zeroY = ycheck-1;
                    nodeList.add(childNode);
                }
            }
            approve = 1;
        /*-----------------------------------------------------------*/
            if ((ycheck + 1) <= 2) {
                //same logic with: if (ycheck-1 >= 0)
            }
            approve = 1;
        /*-----------------------------------------------------------*/
            if ((xcheck + 1) <= 2) {
                //same logic with: if (ycheck-1 >= 0)
            }
            approve = 1;
        /*-----------------------------------------------------------*/
            if ((xcheck - 1) >= 0) {
                //same logic with: if (ycheck-1 >= 0)
            }
            approve = 1;
        }
        currentNode.isLeaf = false;
    }
这是给定的矩阵,或“开始状态”矩阵(在最坏的情况下,至少可以用31个移动来回答):

  • 806
  • 5 4 7
  • 2 3 1
。。。它应该达到这一最终状态:

  • 0112
  • 3 4 5
  • 678
。。。同样,当我使用“曼哈顿距离”作为我的启发式函数时,我的代码可以工作,但需要30秒才能为这种输入生成答案。。。但是,当我使用“错位瓷砖数量”作为启发式函数时,即使在15分钟后,它也不会产生解决方案,而是在我使用此矩阵时给出答案:

  • 5 8 6
  • 271
  • 3 0 4//此矩阵可从前面提到的开始状态矩阵中访问

。。。谢谢那些愿意帮忙的人!。。。如果有点长,我很抱歉,但我认为我应该发布代码,而不是仅仅说明代码的逻辑,因为我可能在实现逻辑时出错…

曼哈顿距离会更快,因为这是一种更好的启发式方法。30秒是等待一段时间的解决方案,尤其是C++,但这并不完全荒谬。然而,即使对于一个不太好的启发来说,15分钟也是如此

如果我正确地解释了您的代码,
checkerNode
循环将通过遍历整个路径来检查当前正在探索的路径中是否已经存在此状态。这是相当低效的(O(log(n)ish),我认为)。如果你维护一个你已经扩展的状态字典,你可以把它降到常数时间

可能还有其他微妙的低效,但我怀疑这会大大加快代码的速度

编辑以解释词典:

字典是一种数据结构,允许您快速查找元素是否存在

对于大多数数据结构,如果要查找具有给定值的元素,则必须将该值与已存储在结构中的每个元素进行比较(如将checkerNode与所有前置节点进行比较的方式)。问题是,当您在数据结构中存储越来越多的内容时,这个过程将花费越来越长的时间。字典的情况并非如此,因为字典使用一种称为哈希表的东西来立即转到给定元素(如果存在)的存储位置。然后,如果元素在那里,您就知道它在数据结构中,如果不是,您就知道它不是

字典通常用于将给定的键映射到关联的值,但这里我们并不真正关心该功能。我们只想知道给定的键是否在字典中,因此我们可以将值设置为我们想要的任何值(通常我只存储布尔值“True”,或者如果需要再次找到它,则存储指向节点的指针)。在C++中,内置字典类是.< /p> 要在代码中使用它,您可以大致执行以下操作:

首先,初始化映射对象

std::map<char,int> already_explored;
继续执行程序,直到找到下一个状态,你想知道它是否已经被看到。现在我们可以在字典中查找:

if (already_explored.count(subState) > 0){
    approve = 0;
}

如果按此顺序操作,您甚至不必担心检查具有相同状态的其他节点的f(n)值。A*到达的第一个肯定是到达该状态的最快方式。在查看代码之后,我可以指出两件可能会大大提高代码效率的事情:

  • A*存储带有要展开的节点的有序列表(开放列表),该列表按升序
    f(n)
    排序。在您的代码中,我知道您正在选择第一个循环中具有最低
    f(n)
    的节点,遍历所有生成状态的树。这是非常低效的,因为搜索树随着算法的每次迭代而增长,而您只需要迭代该树的叶子(这些叶子是作为其他状态的继承者生成的,但没有扩展)。您最好将要以*扩展的节点存储在
    PriorityQueue
    中。队列的第一个元素是
    f(n)
    最低的元素,因此您不必在每次迭代中遍历所有搜索树

  • 正如@seaotternerd在他的答案中指出的,您需要以某种方式维护一个“字典”,其中包含算法已经扩展的状态。A*将作为其他节点的后续节点(但尚未展开)生成的节点存储在打开列表中,并将已展开的节点存储在关闭列表中。这需要检查,在达到之前迭代中生成的状态的情况下,您是否改进了它的
    f(n)
    。 例如,如果在库中执行此操作,则以下字段将用于存储状态的“字典”:

    private Map<int[][], Node> open;
    private Map<int[][], Node> closed;
    private Queue<Node> queue;
    
  • 编辑: 这将避免您在循环开始时沿着搜索树进行迭代,从而改进对最有希望的节点的选择
    already_explored[currentNode.state] = True;
    
    if (already_explored.count(subState) > 0){
        approve = 0;
    }
    
    private Map<int[][], Node> open;
    private Map<int[][], Node> closed;
    private Queue<Node> queue;
    
    private Node takePromising() {
        // Poll until a valid state is found
        Node node = queue.poll();
        while (!open.containsKey(node.state())) {
            node = queue.poll();
        }
        return node;
    }