纯lua脚本搜索算法优化

为了方便后期的热更新需求,项目基本上全部逻辑都使用了纯lua实现,最近遇到一个大地图寻路的算法问题,有两百多个城市的拓扑结构图,寻找最短路径,算法本身没有什么好讲的,A*的广度优先算法,但在lua层照搬A*的实现逻辑,会有极大的性能压力,要求算法本身做些有利于脚本的优化。

优化思路如下:
1.A*需要开放节点列表,关闭节点列表,涉及大量的链表操作,在lua里从表结构里删除和向表结构里添加节点都是非常耗费性能的操作,基本思路是用一个表结构来同时实现开放节点列表和关闭节点列表。

local searchNodes = {}
searchNodes[Node2] = 权重  --开放节点
searchNodes[Node3] = false --关闭节点 

2.A*查找最优子节点时,需要遍历开放节点列表,选取当前最优扩展节点,通常采取的做法是对开放节点列表做一次排序(也有算法在插入时维护开放节点的有序性),选取最优节点。但对lua而言,排序是很耗性能的操作,并切要求必须维持开放节点列表是一个数组,这样不管删除和插入都会导致数组的后续元素更新,所以采用筛取法来获取最优节点。

local function onSearchGood()
    local cNode = nil
    local cStep = 10000000000
    for cityId,cityStep in pairs(searchNodes) do
        if cityStep and cStep > cityStep then
            cNode = cityId
            cStep = cityStep
        end
    end
    if cNode then
        searchNodes[cNode] = false
    end
    return cNode
end

3.路径缓存,根据以上两步基本能处理节点扩展的问题,剩下的就是当前路径的缓存问题,c++的常规实现是通过一个链表实现,让当前节点指向其父节点,就可以反向根据某个节点推导出其从开始节点到当前节点的路径。最近在参照别的算法实现时,发现一个在脚本中非常巧妙的实现方式,就是用一个字符串巧妙地实现路径缓存的问题。

local searchPathes = {}
searchPathes[child] = searchPathes[parent]..">"..child

这样通过查找这个searchPathes就可以方便地知道当前节点来源路径 n1>n2>n3... ,通过字符串的分割,就可以获得整个完整的路径。

– 寻找最短路径

function WorldUtils.findPath2(startId,endId)

    if not startId or not endId then
        return
    end

    local path = {}
    local pathInfo = {}
    local openPoints = {}

    local curNation = xxx --当前阵营
    local cityListInfo = xxx -- 城市附属数据

    path[startId] = 0
    pathInfo[startId] = startId
    local function onSearchNode2(start)

        if not start then
            return
        end

        local cityInfo = _cc_config:getMapCityById(start) --城市联通数据
        local childs = cityInfo.connect_city -- childs

        for _,child in ipairs(childs) do

            local childId = child[1]
            if childId == endId then
                openPoints = {}
                pathInfo[childId] = pathInfo[start].." "..childId
                return
            end

            local currCityInfo = cityListInfo:getCityBaseInfo(childId)
            if currCityInfo then
                if curNation == currCityInfo.nation then

                    local distance = 1
                    if currCityInfo.state == 2 then
                        distance = 1000
                    end

                    local oldStep = path[childId]
                    local newStep = path[start] + distance 
                    -- 之前设置的距离大于新计算出来的距离
                    if not oldStep or oldStep > newStep  then
                        oldStep = newStep
                        path[childId] = newStep
                        pathInfo[childId] = pathInfo[start].." "..childId
                    end 

                    if false ~= openPoints[childId] then
                        openPoints[childId] = oldStep
                    end
                end
            end
        end 
    end 

    local function onSearchGood()  --最优节点

        local cNode = nil
        local cStep = 10000000000
        for cityId,cityStep in pairs(openPoints) do
            if cityStep and cStep > cityStep then
                cNode = cityId
                cStep = cityStep
            end
        end
        if cNode then
            openPoints[cNode] = false
        end
        return cNode
    end

    local function onSearchNereast(start)  --扩展节点
        openPoints[start] = 0
        while true do
            local city = onSearchGood()
            if not city then
                break
            else
                onSearchNode2(city)
            end
        end
    end
    --local currTime = socket.gettime()
    onSearchNereast(startId)
    --dump(socket.gettime() - currTime)
    if pathInfo[endId] then
        return string.split(pathInfo[endId]," ")
    end 
    return nil 
end 

你可能感兴趣的:(编程技术,lua,算法,脚本,搜索,优化)