zoukankan      html  css  js  c++  java
  • JS-AStar算法

    算法的思想详解请看下面链接

    https://blog.csdn.net/windcao/article/details/1533879

    下面是转载的另一种实现方式

    https://blog.csdn.net/liebert/article/details/79672425

    个人的理解

    启发式的算法

    每次选择最短路径 F = G + H,其中,F 是从起点经过该点到终点的总路程,G 为起点到该点的“已走路程”,H 为该点到终点的“预计路程”。

    (我习惯采用曼哈顿距离)F = Math.abs(end_node.s_x - start_node.s_x) + Math.abs(end_node.s_y - start_node.s_y);

    算法的关键是维护开启列表,每次你访问开启列表,你都需要寻找F值最低的方格

    思想

    // 0. 初始化01地图

    // 1. 从起点A开始, 把它作为待处理的方格存入一个"开启列表", 开启列表就是一个等待检查方格的列表.

    // 2. 寻找起点A周围可以到达的方格, 将它们放入"开启列表", 并设置它们的"父方格"为A.

    // 3. 从"开启列表"中删除起点 A, 并将起点 A 加入"关闭列表", "关闭列表"中存放的都是不需要再次检查的方格

    // 4. 从 "开启列表" 中选择 F 值最低的方格 C (绿色起始方块 A 右边的方块),检查它所有相邻并且可以到达 (障碍物和 "关闭列表" 的方格都不考虑) 的方格.

      如果这些方格还不在 "开启列表" 里的话, 将它们加入 "开启列表", 计算这些方格的 G, H 和 F 值各是多少, 并设置它们的 "父方格" 为 C.

    // 5. 如果某个相邻方格 D 已经在 "开启列表" 里了, 检查如果用新的路径 (就是经过C 的路径) 到达它的话, G值是否会更低一些,

      如果新的G值更低, 那就把它的 "父方格" 改为目前选中的方格 C, 然后重新计算它的 F 值和 G 值 (H 值不需要重新计算, 因为对于每个方块, H 值是不变的).

      如果新的 G 值比较高, 就说明经过 C 再到达 D 不是一个明智的选择, 因为它需要更远的路, 这时我们什么也不做.

    // 就这样, 我们从 "开启列表" 找出 F 值最小的, 将它从 "开启列表" 中移掉, 添加到 "关闭列表". 再继续找出它周围可以到达的方块, 如此循环下去...

    // 那么什么时候停止呢? —— 当我们发现 "开始列表" 里出现了目标终点方块的时候, 说明路径已经被找到.

    // 最后以终点为起点通过 "父方块" 可以依次索引到最初的 "起始方块", 这样就得到了路径

    // astar算法 模仿blake老师的写法,用来熟悉算法
    
    var map_maze = []; // 场景节点顺序保存数组
    var open_table = []; // 开启列表
    var close_table = []; // 关闭列表
    var path_stack = []; // 保存路径
    
    var is_found = 0; // 是否找到路径 1 true 0 false
    var open_node_count = 0; // 开启列表元素个数
    var close_node_count = 0; // 关闭列表元素个数
    var top = -1; // path_stack从后往前变量指针
    
    var map_height = 0; //地图高度
    var map_width = 0; // 地图宽度
    var BARRIER = 1; // 阻挡标记
    
    function swap(idx1, idx2) {
        var tmp = open_table[idx1];
        open_table[idx1] = open_table[idx2];
        open_table[idx2] = tmp;
    }
    
    function adjust_heap(nIndex){
        var curr = nIndex;
        var child = curr * 2 - 1; // 得到左孩子idx( 下标从0开始,所有做孩子是curr*2+1 )
        var parent = Math.floor((curr - 1) / 2); // 得到双亲idx  
        if(nIndex < 0 || nIndex >= open_node_count){
            return;
        }
    
        // 往下调整( 要比较左右孩子和cuur parent )  
        while(child < open_node_count){
            if(child + 1 < open_node_count && 
                open_table[child].s_g + open_table[child].s_h > open_table[child + 1].s_g + open_table[child + 1].s_h){
                ++child; // 判断左右孩子大小  
            }
    
            if (open_table[curr].s_g + open_table[curr].s_h <= open_table[child].s_g + open_table[child].s_h) {
                break;
            }else{
                swap(child, curr); // 交换节点  
                curr = child;// 再判断当前孩子节点  
                child = curr * 2 + 1; // 再判断左孩子  
            }
        }
    
        if (curr != nIndex) {
            return;
        }
    
        // 往上调整( 只需要比较cuur child和parent )  
        while (curr != 0) {
            if (open_table[curr].s_g + open_table[curr].s_h >= open_table[parent].s_g + open_table[parent].s_h) {
                break;
            } else {
                swap(curr, parent);
                curr = parent;
                parent = Math.floor((curr - 1) / 2);
            }
        }
    }
    
    function insert_to_opentable(x, y, curr_node, end_node, w){ // w损耗
        var i;
        if (map_maze[x * map_width + y].s_style != BARRIER){ // 不是障碍物 
            if (!map_maze[x * map_width + y].s_is_in_closetable){ // 不在闭表中  
                if (map_maze[x * map_width + y].s_is_in_opentable){ // 在open表中 
                    // 需要判断是否是一条更优化的路径  
                    // 检查如果用新的路径 (就是经过C 的路径) 到达它的话, G值是否会更低一些, 
                    // 如果新的G值更低, 那就把它的 "父方格" 改为目前选中的方格 C, 
                    // 然后重新计算它的 F 值和 G 值 (H 值不需要重新计算, 因为对于每个方块, H 值是不变的). 
                    // 如果新的 G 值比较高, 就说明经过 C 再到达 D 不是一个明智的选择, 因为它需要更远的路, 这时我们什么也不做.
                    if (map_maze[x * map_width + y].s_g > curr_node.s_g + w){ //如果更优化
                        map_maze[x * map_width + y].s_g = curr_node.s_g + w;
                        map_maze[x * map_width + y].s_parent = curr_node;
    
                        for (i = 0; i < open_node_count; ++i) {
                            if (open_table[i].s_x == map_maze[x * map_width + y].s_x && open_table[i].s_y == map_maze[x * map_width + y].s_y) {
                                break;
                            }
                        }
    
                        adjust_heap(i); // 下面调整点  
                    }
                }else{// 不在open中  
                    map_maze[x * map_width + y].s_g = curr_node.s_g + w;
                    map_maze[x * map_width + y].s_h = Math.abs(end_node.s_x - x) + Math.abs(end_node.s_y - y);
                    map_maze[x * map_width + y].s_parent = curr_node;
                    map_maze[x * map_width + y].s_is_in_opentable = 1;
                    open_table[open_node_count++] = (map_maze[x * map_width + y]);
                }
            }
        }  
    }
    
    // 邻居处理
    function get_neighbors(curr_node, end_node) {
        var x = curr_node.s_x;
        var y = curr_node.s_y;
    
        // 下面对于8个邻居进行处理!  
        //  直线损耗10 斜线损耗14
        if ((x + 1) >= 0 && (x + 1) < map_height && y >= 0 && y < map_width) {
            insert_to_opentable(x + 1, y, curr_node, end_node, 10);
        }
    
        if ((x - 1) >= 0 && (x - 1) < map_height && y >= 0 && y < map_width) {
            insert_to_opentable(x - 1, y, curr_node, end_node, 10);
        }
    
        if (x >= 0 && x < map_height && (y + 1) >= 0 && (y + 1) < map_width) {
            insert_to_opentable(x, y + 1, curr_node, end_node, 10);
        }
    
        if (x >= 0 && x < map_height && (y - 1) >= 0 && (y - 1) < map_width) {
            insert_to_opentable(x, y - 1, curr_node, end_node, 10);
        }
    
        if ((x + 1) >= 0 && (x + 1) < map_height && (y + 1) >= 0 && (y + 1) < map_width) {
            insert_to_opentable(x + 1, y + 1, curr_node, end_node, 10 + 4);
        }
    
        if ((x + 1) >= 0 && (x + 1) < map_height && (y - 1) >= 0 && (y - 1) < map_width) {
            insert_to_opentable(x + 1, y - 1, curr_node, end_node, 10 + 4);
        }
    
        if ((x - 1) >= 0 && (x - 1) < map_height && (y + 1) >= 0 && (y + 1) < map_width) {
            insert_to_opentable(x - 1, y + 1, curr_node, end_node, 10 + 4);
        }
    
        if ((x - 1) >= 0 && (x - 1) < map_height && (y - 1) >= 0 && (y - 1) < map_width) {
            insert_to_opentable(x - 1, y - 1, curr_node, end_node, 10 + 4);
        }
    }
    
    // 0. 初始化01地图
    
    // 1. 从起点A开始, 把它作为待处理的方格存入一个"开启列表", 开启列表就是一个等待检查方格的列表.
    
    // 2. 寻找起点A周围可以到达的方格, 将它们放入"开启列表", 并设置它们的"父方格"为A.
    
    // 3. 从"开启列表"中删除起点 A, 并将起点 A 加入"关闭列表", "关闭列表"中存放的都是不需要再次检查的方格
    
    // 4. 从 "开启列表" 中选择 F 值最低的方格 C (绿色起始方块 A 右边的方块),检查它所有相邻并且可以到达 (障碍物和 "关闭列表" 的方格都不考虑) 的方格. 如果这些方格还不在 "开启列表" 里的话, 将它们加入 "开启列表", 计算这些方格的 G, H 和 F 值各是多少, 并设置它们的 "父方格" 为 C.
    
    // 5. 如果某个相邻方格 D 已经在 "开启列表" 里了, 检查如果用新的路径 (就是经过C 的路径) 到达它的话, G值是否会更低一些, 如果新的G值更低, 那就把它的 "父方格" 改为目前选中的方格 C, 然后重新计算它的 F 值和 G 值 (H 值不需要重新计算, 因为对于每个方块, H 值是不变的). 如果新的 G 值比较高, 就说明经过 C 再到达 D 不是一个明智的选择, 因为它需要更远的路, 这时我们什么也不做.
    
    // 就这样, 我们从 "开启列表" 找出 F 值最小的, 将它从 "开启列表" 中移掉, 添加到 "关闭列表". 再继续找出它周围可以到达的方块, 如此循环下去...
    
    // 那么什么时候停止呢? —— 当我们发现 "开始列表" 里出现了目标终点方块的时候, 说明路径已经被找到.
    
    // 最后以终点为起点通过 "父方块" 可以依次索引到最初的 "起始方块", 这样就得到了路径
    
    
    // 0.(此处是否可优化下map_maze的初始化,不必每次都清空push一次???)
    function astar_init(map){
        open_table = [];
        close_table = [];
        path_stack = [];
        map_maze = [];
    
        map_height = map.height;
        map_width = map.width;
    
        is_found = 0;
        open_node_count = 0;
        close_node_count = 0;
        top = -1;
    
        for (var i = 0; i < map.length; i++){
            for(var j = 0; j < map.width; j++){
                var node = {};
                // F = G + H 其中,F 是从起点经过该点到终点的总路程,G 为起点到该点的“已走路程”,H 为该点到终点的“预计路程”。
                node.s_g = 0; // g值
                node.s_h = 0;
                node.s_is_in_closetable = 0;
                node.s_is_in_opentable = 0;
                node.s_style = map.data[i * map.width + j]; // 数据类型 0 1
                node.s_x = i;
                node.s_y = j;
                node.s_parent = null;
                map_maze.push(node);
    
                path_stack.push(null);
                open_table.push(null);
                close_table.push(null);
            }
        }
    }
    
    // 1. A*核心代码
    function astar_search(map, src_x, src_y, dst_x, dst_y){
        var path = [];
        if(src_x == dst_x && src_y == dst_y){
            console.log("起点==终点!");
            return path;
        }
    
        // 初始化map
        astar_init(map);
    
        //1. 从起点A开始, 把它作为待处理的方格存入一个"开启列表", 开启列表就是一个等待检查方格的列表.
        var start_node = map_maze[src_y * map.width + src_x];
        var end_node = map_maze[dst_y * map.width + dst_x];
        var curr_node = null;
    
        open_table[open_node_count++] = start_node;
    
        start_node.s_is_in_opentable = 1; // 加入open表
        start_node.s_g = 0;
        // 曼哈顿距离
        start_node.s_h = Math.abs(end_node.s_x - start_node.s_x) + Math.abs(end_node.s_y - start_node.s_y);
        start_node.s_parent = null;
    
        is_found = 0;
    
        while(1){
            curr_node = open_table[0]; // open表的第一个点一定是f值最小的点(通过堆排序得到的)  
            open_table[0] = open_table[--open_node_count]; // 最后一个点放到第一个点,然后进行堆调整  
            adjust_heap(0); // 调整堆
    
            close_table[close_node_count++] = curr_node; // 当前点加入close表  
            curr_node.s_is_in_closetable = 1; // 已经在close表中了 
    
            if (curr_node.s_x == end_node.s_x && curr_node.s_y == end_node.s_y) // 终点在close中,结束  
            {
                is_found = 1;
                break;
            }
    
            get_neighbors(curr_node, end_node); // 对邻居的处理  
    
    
            if (open_node_count == 0) // 没有路径到达  
            {
                is_found = 0;
                break;
            }
        }    
    
        if(is_found){
            curr_node = end_node;
    
            while(curr_node){
                path_stack[++top] = curr_node;
                curr_node = curr_node.s_parent;
            }
    
            while (top >= 0) // 下面是输出路径看看~  
            {
                console.log(path_stack[top].s_y, path_stack[top].s_x);
                path.push(cc.v2(path_stack[top].s_y, path_stack[top].s_x));
                top--;
            }
        }else{
            console.log("么有找到路径");    
        }
        return path;
    }
    
    module.exports = {
        search: astar_search,
    };
  • 相关阅读:
    (转)Python中的__init__和__new__
    PEP8
    python lstrip()函数
    python中的生成器跟迭代器
    callback
    关于0.0.0.0这个ip的疑问
    Python import中相对路径的问题
    python读取excel
    git本地管理多个密钥/账户
    词法分析之有确定、不确定自动机及其生成器
  • 原文地址:https://www.cnblogs.com/orxx/p/10559253.html
Copyright © 2011-2022 走看看