【问题标题】:Lua pathfinding code needs optimizationLua寻路代码需要优化
【发布时间】:2016-01-19 23:04:24
【问题描述】:

在我的代码上工作了一段时间后,优化了最明显的东西,我得到了这样的结果:

function FindPath(start, finish, path)
    --Define a table to hold the paths
    local paths = {}
    --Make a default argument
    path = path or {start}
    --Loop through connected nodes
    for i,v in ipairs(start:GetConnectedParts()) do
        --Determine if backtracking
        local loop = false
        for i,vv in ipairs(path) do
            if v == vv then
                loop = true
            end
        end
        if not loop then
            --Make a path clone
            local npath = {unpack(path)}
            npath[#npath+1] = v
            if v == finish then
                --If we reach the end add the path
                return npath
            else
                --Otherwise add the shortest part extending from this node
                paths[#paths+1] = FindPath(v, finish, npath) --NOTED HERE
            end
        end
    end
    --Find and return the shortest path
    if #paths > 0 then
        local lengths = {}
        for i,v in ipairs(paths) do
            lengths[#lengths+1] = #v
        end
        local least = math.min(unpack(lengths))
        for i,v in ipairs(paths) do
            if #v == least then
                return v
            end
        end
    end
end

问题是,注释的行出现某种游戏脚本超时错误(我认为这是因为没有屈服的大规模递归)。我也觉得一旦这个问题得到解决,即使在 pacman 板的规模上,它也可能会相当慢。有没有办法可以进一步优化它,或者我可以研究类似的更好的方法?

更新:由于效率低下,我最终决定放弃我的算法,并实现了一个用于寻路的 Dijkstra 算法。任何对源代码感兴趣的人都可以在这里找到:http://pastebin.com/Xivf9mwv

【问题讨论】:

    标签: performance optimization lua path-finding roblox


    【解决方案1】:

    您知道 Roblox 为您提供 PathfindingService 吗?它使用 C 端 A* 路径计算非常快。我建议使用它

    http://wiki.roblox.com/index.php?title=API:Class/PathfindingService

    【讨论】:

    • 我知道这一点,并且我经常使用它,但我正在为需要 AI 来行走人行道系统的朋友编写代码(PathfindingService 会让他们穿过马路)
    • 那我们可以得到你的 FindPath 代码吗?我怀疑可能是该功能导致速度变慢。
    • 哦,它是递归的……在那种情况下,我希望是的,问题是 ROBLOX 表示的递归限制。我建议以一种新的方式创建你的节点。也许计算人行道的长度而不是连接部分。我会在创建节点路径的人行道上每 5 个左右的螺柱创建一个网格。
    • 如果我降低手术成本,我也许可以让它发挥作用。
    • 我有一个 pacman 板,里面装满了 4*0.1*4 大小的节点。棋盘是 28 x 36 个节点,有 360 个节点是可步行的,其余的都是墙。这是我用来测试的。
    【解决方案2】:

    尝试重塑您的算法以利用尾调用。这是 Lua 中一个很棒的机制。

    尾调用是一种递归,您的函数将函数调用作为它所做的最后一件事返回。 Lua 有适当的尾调用实现,它会在场景下将此递归装扮成“goto”,因此您的堆栈永远不会崩溃。

    将“路径”作为 FindPath 的参数之一传递可能会有所帮助。

    【讨论】:

    • 如果 roblox 使用 Lua 5.2 就好了,但是可惜它使用了 Lua 5.1
    • 此代码适用于 ROBLOX,其中尾调用实际上已禁用。
    • @EinsteinK 它们不是禁用,而是未实现。 ROBLOX 使用 Lua 5.1 的修改版本,但 Lua 仅在 5.2 中引入了尾调用。
    • @warspyking Lua 5.1 确实有尾调用:lua.org/manual/5.1/manual.html,见 2.5.8 - 函数调用。 ROBLOX 确实有尾调用,但在不到 3 年前就将它们删除了。这是 Stravant 关于它的几乎古老的论坛帖子:forum.roblox.com/Forum/ShowPost.aspx?PostID=132422268。另外,您是从哪里得知尾调用仅在 5.2 中添加的?
    • @Einstein 我找不到我找到它的地方...它在官方 Lua 源上,但我想它已修复。
    【解决方案3】:

    我看到了你关于放弃代码的编辑,但只是为了帮助其他人在这个问题上绊倒:

    • ipairs 比pairs 慢,它比numeric for 循环慢。 如果性能很重要,请不要使用 ipairs,而是使用 for i=1,#tab 循环
    • 如果要克隆表,请使用 for 循环。有时,您必须使用 unpack (返回动态的尾随 nil 数量),但这种情况不是。解包也是一个缓慢的功能。

    pairs 或数字 for 循环替换 ipairs 并使用循环代替 unpack 将提高性能很多强>.

    如果要获取表中的最小值,请使用此代码 sn-p:

    local lowestValue = values[1]
    for k,v in pairs(values) do
        if v < lowestValue then
            lowestValue = k,v
        end
    end
    

    这可以为您的路径示例重写如下:

    local least = #path[1]
    for k,v in pairs(path) do
        if #v < least then
            least = v
        end
    end
    

    我不得不承认,你很有创造力。没有多少人会使用 math.min(unpack(tab)) (不算它不好的事实)

    【讨论】:

    • ipairs 比pairs 更快,因为它只遍历ARRAY 部分,而不是哈希。不知道你从哪里得到的想法对更快。
    • 为什么要解包用于数学的表。min bad unpack 是为了解包表。使用它来解压它作为 math.min 的 args 对我来说似乎是相当合乎逻辑的。
    • 我测试了你的 for loop VS unpack 参数。我克隆了一个包含 7999 个项目的数组,1000 次并输出红色它们花了多长时间。 for 循环用了 9 秒,而 unpack 用了 2 秒。(当然我的测试是在 Lua 5.2 中进行的,但肯定会在 roblox 上给出大致相同的结果)
    • 我刚刚使用 10k 和 5M 条目的数组测试了 pair VS ipairs。只有在 10k 的情况下,ipairs 有时才略快于pairs。大多数时候,对仍然更快,尽管差异几乎不值得一提。
    • 使用 unpack 并没有那么糟糕,因为它实际上比使用 forloop 克隆表要快。但是,Unpack 只能解压 7999 个元素。当 tab 包含 8k 个元素时执行 unpack(tab) 会导致错误 too many results to unpack。获取表中的最小值确实比 for 循环快(大约 10 次),但您的代码也使用循环从最小值获取实际路径。在至少 50% 的情况下,仅比较“v == least”就已经使您的代码变慢了。结合#v,可以肯定的是,大多数情况下使用 unpack 方法会更慢。
    猜你喜欢
    • 1970-01-01
    • 2011-11-06
    • 2018-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多