c - 用于在网格上查找路径的最有效内存算法

标签 c algorithm performance memory

可用于查找从一个网格方 block 到另一个网格方 block 的路径的内存效率最高的算法是什么?网格可能有无法跨越的障碍。成为最短路径不是必需的,但肯定是一种奖励。该算法将用 C 编码(C++ 可用,但我避免使用它以减少内存使用)并在只有 2048 字节 SRAM 的 ATmega328 芯片上运行。 CPU 效率并不是最重要的。

编辑: 网格是 16 x 32 正方形,每个由一位表示。因此,总内存使用量为 64 字节。网格存储为无符号字符的二维数组,所有 2048 字节都可用。输出将是一个整数数组,引用应采用的正方形。

如果正方形中有障碍物,则正方形数组的值为 1 而不是零。这些方 block 应该像墙一样对待。

最佳答案

这是一个可能适合 2048 字节的算法的未完成想法,是我在尝试寻找非递归泛洪填充变体时想到的。

第一步是创建一个额外的 32×16 8 位值数组;这使用 512 字节。然后水平遍历网格,并对相邻可到达方 block 的运行进行编号,如下图所示:

grid path numbered

对于 32×16 网格,最大游程数为 256(例如棋盘图案或垂直条纹),因此此编号适合 8 位值。

第二步是垂直遍历网格,并将相邻的运行分组:

After checking vertical line 1:
{0A,11,1A}
{2E}
{44,50,5C}
{72}
{87,8F,98}

After checking vertical line 2:
{0A,11,1A,00,24}
{2E}
{44,50,5C,37,69}
{72}
{87,8F,98,7C}

After checking vertical line 2:
{0A,11,1A,00,24,12,2F}
{2E}
{44,50,5C,37,69,51,73}
{72}
{87,8F,98,7C,90}

...等等,如果它们被相邻的运行链接,则合并组。如果最后,起始方 block 和目标方 block 的数量在同一组中,则表示存在路径。

现在,如果您将组存储为简单的列表,如上例所示,这实际上并没有为您提供路径;它只是告诉您哪些方 block 可以从起始方 block 和目标方 block 到达,但一条路径可能不需要穿过所有这些方 block 。

如果您将组存储在一个数据结构中,您知道哪些运行是相互连接的,那么它就变成了更小空间中的“通过图形的最短路径”问题。我不确定哪种数据结构最适合剩余的 1536 字节。

(欢迎任何人尝试进一步发展这个想法。)


此方法可用于在运行其他算法之前简化网格。首先,运行的分组识别网格中无法到达的部分;这些可以在原始网格或其副本中标记为墙。其次,它识别死角;仅连接到另一个运行(并且不包含起点或目标方 block )的运行是不必要的弯路,也可以这样标记。 (这应该重复:删除单连接运行可能会显示另一个运行是单连接的。)

grid path simplified

通过删除无法访问和单链接的运行简化了网格

再次运行该算法,但使用垂直运行和水平分组,可以消除额外的死角。


下面的 JavaScript 片段是算法第一部分的简单代码示例:使用图像中的示例网格,它对运行进行编号,将它们分配给组,必要时合并组,然后检查是否开始和目标方 block 是否在同一组中,即是否有路径。

分组方法可能不是最有效的,尤其是在合并组时,但它使用最大 256 字节(运行数×8 位值)的固定大小数组,这在有限内存中可能是最好的情况。

function gridPath(grid, x1, y1, x2, y2) {
    var runs = [], rcount = 0;
    for (var i = 0; i < 16; i++) {           // number runs
        var start = true; runs[i] = [];
        for (var j = 0; j < 32; ++j) {
            if (grid[i][j] == 0) {           // found empty cell
                if (start) ++rcount;         // start of new run
                runs[i][j] = rcount - 1;
                start = false;
            }
            else start = true;               // found blocked cell
        }
    }
    var groups = [], gcount = 0;
    for (var i = 0; i < rcount; i++) groups[i] = 0xFF;

    for (var j = 0; j < 32; ++j) {           // assign runs to groups
        var g = [];
        for (var i = 0; i < 16; ++i) {
            if (grid[i][j] == 0) g.push(runs[i][j]);
            if ((grid[i][j] == 1 || i == 15) && g.length > 0) {
                insertGroup(g);
                g = [];
            }
        }
    }     
    return groups[runs[y1][x1]] == groups[runs[y2][x2]];

    function insertGroup(g) {
        var matches = [];
        for (var i = 0; i < g.length; i++) { // check if runs are already in group
            if (groups[g[i]] != 0xFF && matches.indexOf(groups[g[i]]) < 0) {
                matches.push(groups[g[i]]);
            }
        }
        if (matches.length == 0) matches.push(gcount++); // start new group
        for (var i = 0; i < g.length; i++) { // add runs to group
            groups[g[i]] = matches[0];
        }
        if (matches.length > 1) {            // merge groups
            for (var i = 0; i < rcount; i++) {
                if (matches.indexOf(groups[i]) > 0) groups[i] = matches[0];
            }
        }
    }
}

var grid = [[1,0,1,0,1,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,1,0,0,0],
            [0,0,0,1,0,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1,0],
            [0,1,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,1,0,0],
            [0,0,1,0,1,0,1,0,1,0,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,1,1,0,1,0,0,1],
            [1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1,0,0,1,0],
            [0,1,0,0,0,1,0,0,0,0,1,0,1,0,0,1,1,1,0,0,1,0,1,1,0,0,0,0,0,1,0,1],
            [1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,1,1,1,1,0,1,0],
            [0,0,0,1,0,0,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0,0,0],
            [0,1,0,0,0,1,0,0,0,1,1,0,1,0,0,1,0,0,1,0,1,0,1,0,1,0,1,0,0,0,1,0],
            [0,0,1,0,1,0,1,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,1,0,0,1,0,1,0,0],
            [1,0,0,1,0,0,0,1,0,0,0,1,0,0,1,1,1,0,0,1,0,0,1,0,0,0,1,0,1,0,0,1],
            [0,1,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,1,0,1,0,0,0,1,0,0,1,0],
            [1,0,1,0,1,0,1,0,1,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,1,0,1,0,1,0,0,1],
            [0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,1,0,0],
            [0,1,0,0,0,1,0,0,0,1,0,0,1,1,1,1,1,1,1,0,0,1,0,0,1,0,0,1,0,0,1,0],
            [0,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0]];
document.write(gridPath(grid, 0, 15, 15, 7));

关于c - 用于在网格上查找路径的最有效内存算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38554515/

相关文章:

C++内存流到C文件流

c - Mingw - 从规范文件中删除选项

c - Argc 不适用于我的 C 程序,正在获取核心转储

javascript - 试图弄清楚如何获得立方体的当前面

java - 在 Java 中收集符号出现的最快方法是什么

performance - GXT 性能问题

c - 如何在 C 中使用 GDI+?

algorithm - 遍历二叉树的方法数

java - 交换二维数组的行和列

sql - Spark sql 查询与数据帧函数