算法找到网格上所有单元的最短路径

3

我有一个[40 x 15]的网格,上面有2到16个单位,并有未知数量的障碍物。
如何从我的位置找到到达所有单元的最短路径。

我有两种辅助方法,可认为它们的时间复杂度是O(1)

  • getMyLocation() - 返回网格上我的位置的(x, y)坐标
  • investigateCell(x, y) - 返回(x, y)坐标处单元格的信息

我实现了A*搜索算法,同时搜索所有方向。最后输出一张网格,其中每个单元格都有一个表示距离我的位置的数字,以及网格上所有单元格的集合。在我的情况下,它的时间复杂度是O(N),其中N是单元格的数量-600个。

我使用AS3实现了这个算法,但不幸的是,它需要我的机器计算30-50毫秒。

这是我的源代码。你能建议我更好的方法吗?

package com.gazman.strategy_of_battle_package.map
{
    import flash.geom.Point;

    /**
     * Implementing a path finding algorithm(Similar to A* search only there is no known target) to calculate the shortest path to each cell on the map.
     * Once calculation is complete the information will be available at cellsMap. Each cell is a number representing the
     * number of steps required to get to that location. Enemies and Allies will be represented with negative distance. Also the enemy and Allys
     * coordinations collections are provided. Blocked cells will have the value 0.<br><br>
     * Worth case and best case efficiency is O(N) where N is the number of cells.
     */
    public class MapFilter
    {
        private static const PULL:Vector.<MapFilter> = new Vector.<MapFilter>();
        public var cellsMap:Vector.<Vector.<int>>;
        public var allys:Vector.<Point>;
        public var enemies:Vector.<Point>;
        private var stack:Vector.<MapFilter>;
        private var map:Map;
        private var x:int;
        private var y:int;
        private var count:int;
        private var commander:String;
        private var hash:Object;
        private var filtered:Boolean;

        public function filter(map:Map, myLocation:Point, commander:String):void{
            filtered = true;
            this.commander = commander;
            this.map = map;
            this.x = myLocation.x;
            this.y = myLocation.y;
            init();
            cellsMap[x][y] = 1;
            excecute();
            while(stack.length > 0){
                var length:int = stack.length;
                for(var i:int = 0; i < length; i++){
                    var mapFilter:MapFilter = stack.shift();
                    mapFilter.excecute();
                    PULL.push(mapFilter);
                }
            }
        }

        public function navigateTo(location:Point):Point{
            if(!filtered){
                throw new Error("Must filter before navigating");
            }
            var position:int = Math.abs(cellsMap[location.x][location.y]);
            if(position == 0){
                throw new Error("Target unreachable");
            }
            while(position > 2){
                if(canNavigateTo(position, location.x + 1, location.y)){
                    location.x++;
                }
                else if(canNavigateTo(position, location.x - 1, location.y)){
                    location.x--;
                }
                else if(canNavigateTo(position, location.x, location.y + 1)){
                    location.y++;
                }
                else if(canNavigateTo(position, location.x, location.y - 1)){
                    location.y--;
                }
                position = cellsMap[location.x][location.y];
            }

            return location;
            throw new Error("Unexpected filtering error");
        }

        private function canNavigateTo(position:int, targetX:int, targetY:int):Boolean
        {
            return isInMapRange(targetX, targetY) && cellsMap[targetX][targetY] < position && cellsMap[targetX][targetY] > 0;
        }

        private function excecute():void
        {
            papulate(x + 1, y);
            papulate(x - 1, y);
            papulate(x, y + 1);
            papulate(x, y - 1);
        }

        private function isInMapRange(x:int, y:int):Boolean{
            return x < cellsMap.length && 
                x >= 0 &&
                y < cellsMap[0].length && 
                y >= 0;
        }

        private function papulate(x:int, y:int):void
        {
            if(!isInMapRange(x,y) ||
                cellsMap[x][y] != 0 ||
                hash[x + "," + y] != null || 
                map.isBlocked(x,y)){
                return;
            }

            // we already checked that is not block
            // checking if there units
            if(map.isEmpty(x,y)){
                cellsMap[x][y] = count;
                addTask(x,y);
            }
            else{
                cellsMap[x][y] = -count;
                if(map.isAlly(x,y, commander)){
                    hash[x + "," + y] = true;
                    allys.push(new Point(x,y));
                }
                else {
                    hash[x + "," + y] = true;
                    enemies.push(new Point(x,y));
                }
            }
        }

        private function addTask(x:int, y:int):void
        {
            var mapFilter:MapFilter = PULL.pop();
            if(mapFilter == null){
                mapFilter = new MapFilter();
            }

            mapFilter.commander = commander;
            mapFilter.hash = hash;
            mapFilter.map = map;
            mapFilter.cellsMap = cellsMap;
            mapFilter.allys = allys;
            mapFilter..enemies = enemies;
            mapFilter.stack = stack;
            mapFilter.count = count + 1;
            mapFilter.x = x;
            mapFilter.y = y;
            stack.push(mapFilter);
        }

        private function init():void
        {
            hash = new Object();
            cellsMap = new Vector.<Vector.<int>>();
            for(var i:int = 0; i < map.width;i++){
                cellsMap.push(new Vector.<int>);
                for(var j:int = 0; j < map.height;j++){
                    cellsMap[i].push(0);
                }
            }   
            allys = new Vector.<Point>();
            enemies = new Vector.<Point>();
            stack = new Vector.<MapFilter>();
            count = 2;
        }
    }
}

这里有另一个 Stack Overflow 的问题,可能提供一些可接受的替代方案:http://stackoverflow.com/questions/2748602/a-a-star-implementation-in-as3 - Gigggas
A* 算法在很大程度上取决于障碍物的数量。这里真正重要的问题是 - 你的障碍物是否是动态的?如果不是,那么有一些相当聪明的算法可以预先计算所有可能的选项并将它们缓存,因此您只需在需要时选择路径即可。尽管如此,我认为 30 毫秒并不糟糕...你的目标是什么? - Andrey Popov
@AndreyPopov 我的目标是将其减少到1000/60毫秒以下。因为我的16个单位每次轮换都需要执行此计算。而且我按照这样的方式构建它们,使它们完全独立,甚至可以由第三方在运行时上传。所以我的障碍不是动态的,但我的单位是动态的。而且我不知道它们或我的位置下一轮会在哪里。 - Ilya Gazman
安德烈的意思是,由于你的障碍物不是动态的,两个单元格之间的最短路径不会改变,因此你可以事先计算并存储它们。 - golyo
@MartonPallagi 这是动态的,因为单位也是障碍物。 - Ilya Gazman
1个回答

1
你可以使用Floyd Warshall算法来找到每对点之间的最短路径。这将是O(|V|^3),你不需要为每个单位运行它,只需在每个回合运行一次即可。这是一个非常简单的算法,我怀疑它在实践中可能比运行BFS / Bellman Ford更快。

我的算法性能更好。它是O(KV)(K个单位,V个单元格),而且Floyd Warshall不返回路径本身的详细信息。 - Ilya Gazman
1
我知道,600和16都是非常小的数字,我建议你尝试一些更简单的算法,这些算法在实践中可能更好,而不是在理论上。(而A*算法的时间复杂度是O(|V|^2)) - kyldvs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接