简体   繁体   中英

Performance issue with Vue.js Dijkstra algorithm

I am writing a university project, where I take two algorithms and compare their performance, the main algorithms are Dijkstra and A*, however I am not very experienced with either Vue.js, JS in general and algorithms, and my first iteration of Dijkstra is SUPER slow. I am using a 2D grid of X by Y, with a start point node and an end point node, and running the algorithm. Any grid above 10x10 is completely too slow.

I am at a loss on what to do, I don't see any errors when running the code, it executes, but executes entirely too slow. I am not very experienced, so I don't know where to start to look and what the issue could be. Maybe there's someone with experience that could help out and tell me what I'm doing wrong?

    export default ({
        name: 'Home',
        data() {                                                   // global variables init
            return {
                grid: [],                                          // initializing grid array, rows and columns
                todis: "",                                         // to display
                output: [],                                        // shortest path array
                toCheck: [],                                       // checking neighboring nodes
                startx: Number(0),                                 // starting x position of algorithm
                starty: Number(0),                                 // starting y position of algorithm 
                endx: Number(0),                                   // end x position of algorithm   
                endy: Number(0),                                   // end y position of algorithm 
                sxy: [0, 0],                                       // start x, y array coordinates
                exy: [0, 0],                                       // end x, y array coordinates
                wallx: Number(0),                                  // wall x coordinate
                wally: Number(0)                                   // wall y coordinate
            };
        },
        methods: {                                                 // all needed methods
            addToGrid(adding) {                                    // create the grid 
                this.grid.push(adding)
            },
            setwall(x, y) {
                this.grid[Number(y)][Number(x)].id = 1
            },
            updates(x, y) {                                        // update start position on button press
                this.sxy = [Number(x), Number(y)]
            },
            updatee(x, y) {                                        // update end position on button press
                this.exy = [Number(x), Number(y)]
            },
            makeGrid(width, height) {                              // takes two values to make a grid
                function Node(x, y, id) {                          // 0 - unchecked; 1 - Wall; 2 - Start; 3 - End; 4 - Checked; 5 - Shortest Path
                    this.x = x                                     // initialization of values for class Node
                    this.y = y
                    this.id = id
                    this.visited = false
                    this.prev = []
                }
                if (this.grid.length === 0) {                      // Stops grid being made multiple times
                    for (let y = 0; y < height; y++) {
                        let row = []                               // Create a row, need new empty array every time we go through a row
                        for (let x = 0; x < width; x++) {          // Create nodes for the width of the grid
                            var newNode = new Node(x, y, 0)        // give values x, y, id to the node
                            row.push(newNode)                      // push a node to the row array
                        }
                        this.addToGrid(row)                        // we add row array to grid
                    }
                }
            },
            Dijkstra(start, end) {                          
                this.grid[start[1]][start[0]].id = 2               // node array coordinates (w, h we got from inputs from the front end) width height values set to id of 2, which is the start of the grid 
                this.grid[end[1]][end[0]].id = 3                   // node array coordinates (w, h) for end point, which has an id of 3
                function findNeighbours(grid, x, y) {              // scanning the neighboring nodes, takes the whole grid
                    let Neighbours = []                            // initializing values for neighbours
                    let PosNeighbours = []                         // position of new neighbours
                    let cnode = null                               // checked node 
                    grid[y][x].visited = true
                    if (grid[y][x].id === 0) {                     // if the node at y, x id is 0 (unchecked), set it to checked (for colouring)
                        grid[y][x].id = 4
                    }
                    try {                                          // we go to the right of of the node
                        cnode = grid[y][x + 1]                     // x + 1 and if the node:
                        if (typeof cnode !== "undefined") {        // is valid
                            if (cnode.id !== 1) {                  // is not a wall
                                PosNeighbours.push(grid[y][x + 1]) // we add set the neighbour position to that value
                            }
                        }
                    }
                    catch{
                        null
                    }
                    try {                                          // we go to the left of the node
                        cnode = grid[y][x - 1]                     // x - 1 and if the node:
                        if (typeof cnode !== "undefined") {        // is valid
                            if (cnode.id !== 1) {                  // is not a wall
                                PosNeighbours.push(grid[y][x - 1]) // we add set the neighbour position to that value
                            }
                        }
                    }
                    catch{
                        null
                    }
                    try {                                          // we go to the top of the node
                        cnode = grid[y + 1][x]                     // y + 1 and if the node:
                        if (typeof cnode !== "undefined") {        // is valid
                            if (cnode.id !== 1) {                  // is not a wall
                                PosNeighbours.push(grid[y + 1][x]) // we add set the neighbour position to that value
                            }
                        }
                    }
                    catch{
                        null
                    }
                    try {                                          // we go to the bottom of the node 
                        cnode = grid[y - 1][x]                     // y - 1 and if the node: 
                        if (typeof cnode !== "undefined") {        // is valid
                            if (cnode.id !== 1) {                  // is not a wall
                                PosNeighbours.push(grid[y - 1][x]) // we add set the neighbour position to that value    
                            }
                        }
                    }
                    catch{
                        null
                    }
                    for (let node of PosNeighbours) {              // for each neighboring node
                        if (typeof grid[node.y][node.x] === 'undefined') {
                            null                                   // if the node is not valid, do nothing
                        }
                        else {
                            if (node.visited === false) {          // if the node is not visited, we add the value to the neighbours array
                                Neighbours.push(node)              // we set the new x and new y value to our current node position
                                let nx = node.x
                                let ny = node.y
                                if (grid[ny][nx].prev !== []) {    // if the new grid position has no previous value 
                                    grid[ny][nx].prev = grid[y][x] // we set the previous value to the old x and y positions
                                }
                            }
                        }
                    }
                    return [Neighbours, grid]                      // ??? return an array of nodes visited and whole grid ???
                }
                if (start !== end) {                               // we start the algorithm if the start node is not the same as the end node
                    let startNode = this.grid[start[1]][start[0]]  // set the start node, the checking array, give it a start node and declare what to check next
                    this.toCheck = []           
                    this.toCheck.push(startNode)
                    let toChecknext = []
                    while (this.grid[end[1]][end[0]].visited === false) {
                        if (this.toCheck.length !== 0) {           // while we haven't visited the end node, or if we haven't checked all the nodes
                            let node = this.toCheck.shift()        // we set the node to the first value of our toCheck array
                            let fn = findNeighbours(this.grid, node.x, node.y)
                            let nodestoadd = fn[0]                 // we call the findNeighbours function and set the nodes to add to Neighbours 
                            this.grid = fn[1]                      // grid generated with grid values of the the function
                            for (let node of nodestoadd) {         // for each node in the nodestoadd array we push the node valeu to the next node to check
                                toChecknext.push(node)
                            }
                        } else {                                   // otherwise, set the next node to check to the node we are currently at
                            for (let node in toChecknext) {
                                this.toCheck.push(node)
                            }
                        }
                    }
                    let currentNode = this.grid[end[1]][end[0]]    // we set up our currentNode end, if our current node x, y are not the same 
                    let pathnodes = []
                    while (currentNode.x !== startNode.x || currentNode.y !== startNode.y) {
                        pathnodes.push(currentNode.prev)           // we create a path of the previous nodes, using a pathnodes array
                        currentNode = currentNode.prev
                    }
                    let pathnodexy = []                            
                    for (let node of pathnodes) {                  // for each node in the pathnodes array (all the nodes for our shortest path)
                        pathnodexy.push([node.x, node.y])          // we give the pathnodexy array x, y values of the nodes
                        if (this.grid[node.y][node.x].id == 4) {   // if that node has been visited with id 4, we set that to id 5, which is the id of the path
                            this.grid[node.y][node.x].id = 5
                        }
                    }
                    this.output = pathnodexy                       // we set the output value and start the algo
                } else {
                    return [start]
                }
            },

            displayGrid(grid) {                                    // we create a string method, that creates a grid with the ID values we defined
                this.todis = "<div id='block_container'>"
                for(const row of grid) {                           // for each row, and each node in that row we set the ID value of the node and generate html to create our grid
                    for (const node of row) {
                        this.todis += "<button @click.self='changewall' id='block-id-"+node.id+"'></button>"
                    }
                    this.todis += "</div><div id='block_container'>"
                }
                this.todis = this.todis+"</div>"
                return this.todis                                   // we return the constructed html
            },
            changeGrid(thisx, thisy) {
                this.grid[thisy][thisx].id = 1
            }
        },
        created: function () {
            this.makeGrid(7, 7)

        },
        updated: function () {
            // on update
        }
    });

Although you seem to suggest that your algorithm works, but too slow, it actually has at least one blocking error:

  • for (let node in toChecknext) : this will iterate the indexes of the array, not the values. Yet you treat those values as nodes, which will lead to undesired behaviour. Change to:

     for (let node of toChecknext)

There are other, non-breaking issues as well:

  • if (grid[ny][nx].prev !== []) will always be true, because a newly created array can never be the same object as an already existing object. I gues you first tried with === [] , then found it was always false , and then just toggled it. Instead initialise prev as null and just check whether prev is a truthy value. Also just reference node instead of grid[ny][nx] , which really is the same object (see more about that in another bullet point further down this list):

     this.prev = null; // in Node constructor /*... */ if (node.prev)
  • toChecknext is never cleared. It should, otherwise the algorithm will be doing unnecessary work. Whenever you have copied the contents of toChecknext to this.toCheck , you should empty toChecknext so that you will not be verifying nodes that are already visited. So after the copy, add:

     toChecknext.length = 0;
  • There is no provision for the case where there is no path. The algorithm will get into an infinite loop. On the condition you did the previous correction, also add this block before the else :

     } else if (toChecknext.length == 0) { this.output = []; return []; // No solution!
  • The path pathnodes lacks the end node. So initialise it with:

     let pathnodes = [currentNode];
  • The path pathnodexy actually is reversed, as it lists coordinates from the end to the beginning. So do this:

     this.output = pathnodexy.reverse();
  • In the case the start node is the end node, you return the path, but in all other cases you do not return it, but set a property. This is inconsistent. So in the else case do this:

     this.output = [start];

    and outside of the if..else block do:

     return this.output;
  • It is overkill to do things like this.grid[node.y][node.x].id , which really is node.id . You have this happening at several places in your code. Another example is currentNode.x.== startNode.x || currentNode.y !== startNode.y currentNode.x.== startNode.x || currentNode.y !== startNode.y , which can be just currentNode != startNode . There are other cases, which I will not list all...

  • It is of no use to pass this.grid to findNeighbours and then have findNeighbours return it. Any mutation you did on that grid argument will be directly on this.grid . grid is a reference to the same as this.grid . So just remove that parameter, and have the function work with this.grid instead of grid , and just return the neighbour list.

  • Don't use try {... } catch { null } , as you risk to miss out on errors you did not expect.

There is much more to improve in this code, but with these adjustments it should run in a reasonable time.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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