简体   繁体   English

A* Path Finder (Java) 使用 1024 多维数组效率低下

[英]A* Path Finder (Java) is inefficient using a 1024 multidimentional array

I have the below code for a A* pathfinder, however it is taking upwards of 10 minutes to find a solution using a simple 1024 x 1024 array.我有下面的 A* 探路者代码,但是使用简单的 1024 x 1024 阵列需要 10 分钟以上的时间才能找到解决方案。

I had to comment out //Collections.sort(this.openList);我不得不注释掉 //Collections.sort(this.openList); as it was throwing a comparison method violates its general contract.因为它抛出比较方法违反了它的一般合同。 error when running.运行时出错。

Is the algorithm correct and any idea why the bottleneck?算法是否正确以及为什么会出现瓶颈? Some people using C++ are getting a response time of 40ms, not 10+ mins!有些人使用 C++ 的响应时间是 40 毫秒,而不是 10 分钟以上!

When using the maze array it does it in the blink of an eye, but thats using something like a 14x10 array, rather than 1024 from the collisionMap .当使用maze数组时,它会在眨眼之间完成,但那是使用类似 14x10 数组的东西,而不是 1024 中的collisionMap

Is the method flawed in some way?该方法在某些方面有缺陷吗? Or using the wrong data structures?还是使用了错误的数据结构?

import java.util.List;

import javax.imageio.ImageIO;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.GraphicsConfiguration;
import java.awt.Paint;
import java.awt.image.BufferedImage;
import java.io.Console;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;

class AStarTwo {

    // Closed list, open list and calculatedPath lists
    private final List<Node> openList;
    private final List<Node> closedList;
    private final List<Node> calcPath;

    // Collision Map to store tha map in
    private final int[][] collisionMap;

    // Current node the  program is executing
    private Node currentNode;

    // Define the start and end coords
    private final int xstart;
    private final int ystart;
    private int xEnd, yEnd;

    // Node class 
    static class Node implements Comparable {
        public Node parent;
        public int x, y;
        public double g;
        public double h;
        Node(Node parent, int xpos, int ypos, double g, double h) {
            this.parent = parent;
            this.x = xpos;
            this.y = ypos;
            this.g = g;
            this.h = h;
       }

       // Compare f value (g + h)
       @Override
       public int compareTo(Object o) {
           Node that = (Node) o;
           return (int)((this.g + this.h) - (that.g + that.h));
       }
   }

    // construct and initialise 
    public AStarTwo(int[][] collisionMap, int xstart, int ystart) {
        this.openList = new ArrayList<>();
        this.closedList = new ArrayList<>();
        this.calcPath = new ArrayList<>();
        this.collisionMap = collisionMap;
        this.currentNode = new Node(null, xstart, ystart, 0, 0);
        this.xstart = xstart;
        this.ystart = ystart;

    }

    // returns a List<> of nodes to target
    public List<Node> findPathTo(int xTo, int yTo) {

        this.xEnd = xTo;
        this.yEnd = yTo;

        // Add this to the closed list
        this.closedList.add(this.currentNode);

        // Add neighbours to openList for iteration
        addNeigborsToOpenList();

        // Whilst not at our target
        while (this.currentNode.x != this.xEnd || this.currentNode.y != this.yEnd) {

            // If nothing in the open list then return with null - handled in error message in main calling func
            if (this.openList.isEmpty()) {
                return null;
            }

            // get the lowest f value and add it to the closed list, f calculated when neighbours are sorted
            this.currentNode = this.openList.get(0);
            this.openList.remove(0); 
            this.closedList.add(this.currentNode); 

            addNeigborsToOpenList();

        }

        // add this node to the calculated path
        this.calcPath.add(0, this.currentNode);

        while (this.currentNode.x != this.xstart || this.currentNode.y != this.ystart) {

            this.currentNode = this.currentNode.parent;

            this.calcPath.add(0, this.currentNode);

        }

        return this.calcPath;
    }



    // Searches the current list for neighbouring nodes returns bool
    private static boolean checkNeighbourHasBeenSearched(List<Node> array, Node node) {
        return array.stream().anyMatch((n) -> (n.x == node.x && n.y == node.y));
    }


    // Calculate distance from current node to the target
    private double distance(int dx, int dy) {
        return Math.hypot(this.currentNode.x + dx - this.xEnd, this.currentNode.y + dy - this.yEnd); // return hypothenuse
    }

    // Add neighbouring nodes to the open list to iterate through next
    @SuppressWarnings("unchecked")
    private void addNeigborsToOpenList() {

        Node node;

        for (int x = -1; x <= 1; x++) {

            for (int y = -1; y <= 1; y++) {

                node = new Node(this.currentNode, this.currentNode.x + x, this.currentNode.y + y, this.currentNode.g, this.distance(x, y));

                // if we are not on the current node
                if ((x != 0 || y != 0) 
                    && this.currentNode.x + x >= 0 && this.currentNode.x + x < this.collisionMap[0].length // check collision map boundaries
                    && this.currentNode.y + y >= 0 && this.currentNode.y + y < this.collisionMap.length
                    && this.collisionMap[this.currentNode.y + y][this.currentNode.x + x] != -1) { // check if tile is walkable (-1)

                    // and finally check we haven't already searched the nodes
                    if(!checkNeighbourHasBeenSearched(this.openList, node) && !checkNeighbourHasBeenSearched(this.closedList, node)){
                        node.g = node.parent.g + 1.; // Horizontal/vertical cost = 1.0
                        node.g += collisionMap[this.currentNode.y + y][this.currentNode.x + x]; // add movement cost for this square


                        // Add diagonal movement cost sqrt(hor_cost² + vert_cost²) + 0.4
                        if (x != 0 && y != 0) {
                            node.g += .4;   
                        }

                        // Add the node to the List<>
                        this.openList.add(node);
                    }

                }
            }
        }

        // sort in ascending order
        //Collections.sort(this.openList);
    }


    public static void main(String[] args) {

        int [][] maze = 
            { {1,1,1,1,1,1,1,1,1,1,1,1,1},
              {1,0,-1,0,-1,0,1,0,0,0,0,0,1},
              {1,0,-1,0,0,0,1,0,1,1,1,0,1},
              {1,0,0,0,-1,-1,-1,0,0,0,0,0,1},
              {1,0,1,0,0,0,0,0,1,1,1,0,1},
              {1,0,1,0,-1,-1,-1,0,1,0,0,0,-1},
              {1,0,-1,0,-1,0,0,0,-1,-1,-1,0,-1},
              {1,0,1,0,-1,-1,-1,0,1,0,-1,0,-1},
              {1,0,0,0,0,0,0,0,0,0,1,0,1},
              {1,1,1,1,1,1,1,1,1,1,1,1,1}

            };

        // Define the size of the grid
        final int sizeOf = 20;

        int[][] collisionMap = new int[sizeOf][];

        for(int i=0;i < sizeOf; i++) {
            // -1 = blocked
            // 0+ = cost
            collisionMap[i] = new int[sizeOf];
        }

        // set the value of the nodes
        for (int k = 0; k < sizeOf; k++) {
            for (int j = 0; j < sizeOf; j++) {
                if(j == 30 && k < 100) {
                    collisionMap[k][j] = -1;
                } else if (j == 50 && k > 230) {
                    collisionMap[k][j] = -1;
                }else {
                    collisionMap[k][j] = 0;
                }

            }
        }        

        AStarTwo as = new AStarTwo(maze, 9, 9);
        List<Node> path = as.findPathTo(0,0);

        if(path == null) {
            System.out.println("Unable to reach target");
        }


        // create image buffer to write output to
        BufferedImage img = new BufferedImage(sizeOf, sizeOf, BufferedImage.TYPE_INT_RGB);

        // Set colours
        int r = 255;
        int g = 0;
        int b = 0; 

        int colRed = (r << 16) | (g << 8) | b;

        r = 0;
        g = 255;
        b = 0; 

        int colGreen = (r << 16) | (g << 8) | b;

        r = 0;
        g = 0;
        b = 255; 

        int colBlue = (r << 16) | (g << 8) | b;

        r = 255;
        g = 255;
        b = 255; 

        int colWhite = (r << 16) | (g << 8) | b;

        int i = 0;
        int j = 0;

        if (path != null) {

            path.forEach((n) -> {
                System.out.print("[" + n.x + ", " + n.y + "] ");
                maze[n.y][n.x] = 2;
            });

            for (int[] maze_row : maze) {
                for (int maze_entry : maze_row) {
                    switch (maze_entry) {

                        // normal tile
                        case 0:
                            img.setRGB(j, i, colWhite);
                            break;

                        // final path
                        case 2:
                            img.setRGB(j, i, colBlue);
                            break;

                        // Object to avoid
                        case -1:
                            img.setRGB(j, i, colRed);
                            break;

                        // Any other value
                        default:
                            img.setRGB(j, i, colWhite);
                    }

                    j++;
                }
                // count j - reset as if it were a for loop
                if(i != 12) {
                    j=0;
                }
                i++;
                System.out.println();
            }
        }

        // output file
        File f = new File("aStarPath.png");

        try {
            ImageIO.write(img, "PNG", f);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("i: " + i + ", j: " + j);
    }
}

I suspect your problem is this line:我怀疑你的问题是这一行:

return array.stream().anyMatch((n) -> (n.x == node.x && n.y == node.y));

which is called around O(n^2) times and will take time proportional to the size of the array (which will also be O(n^2) in the worst case for an by n maze).这被称为大约 O(n^2) 次,并且所花费的时间与数组的大小成正比(在最坏的情况下,对于 n 个迷宫,这也将是 O(n^2))。

You want a faster method of performing this test.您需要一种更快的方法来执行此测试。

For example:例如:

  1. Use a set to hold the open and closed lists instead of list使用集合来保存打开和关闭列表而不是列表
  2. Or use an extra field in the node structure to indicate if it is in the open or closed list或者在节点结构中使用一个额外的字段来指示它是在打开列表还是关闭列表中

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM