算法——a*寻路

把地图划分成格子
设置两个表:
openList:放的是当前可到达的格子。最初的起点先放入这个表
closeList:放的是格子数据被锁死不需要再修改的格子

格子的数据包括:
G:从起点到这个格子移动的代价,或者说距离。
H:这个格子到终点的估算代价/距离。用曼哈顿距离表示(即该格子与终点在x轴和y轴距离上的和:d(i,j)=|X1-X2|+|Y1-Y2|.)
F:G+H
父节点parent:与G值的计算有关。G表示了从起点到这个格子的路径消耗,parent指明了路径的方向,也就是parent为G所表示的路径上该格子的前一个格子

重复下面的步骤:
1.从openList中选中F值最小的格子,作为当前格
2.把当前格换到closeList。因为当前格不会有新的更短的到起始格子的路径消耗。
3.以当前格寻找新的可到达的格子,即周围的相邻格子。
对这些格子进行判断:
*如果格子不可通过或者在closeList忽略
*如果格子不在openList,已当前格作为父节点,计算格子的FGH值,放入openList
*如果格子在openList,格子有旧的路径消耗(旧的父节点和G值),用当前格作为新的路径来计算G值,与旧的G值比较。如果新的G值小,则把格子父节点设为当前格,G值刷新。也就是以当前格子作为新路径和格子记录的旧路径比较看哪个是这个格子到起点的最短路径消耗。并记录下来

重复上面三个步骤,直到:
1.终点格子被加入了closeList,即在openList里找到的F值最小的格子是终点格子。以终点格子的父节点倒推回起点格子便是最短路径消耗
2.openList已经空了,表示路径不存在,起点无法到达终点

总结:以起点开始往外扩散能到达的格子。并确定离终点最近的格子到起点的最短路径消耗。直到扩散的格子涵盖了终点,或无法在扩散。

下面的例子,是我在项目中用到的a*

function ElementData:getShortestPath( toData )
	--toData是终点
    local closeTable = {}
    --把起始点放入open表,sureRoute是G值,到起点的距离消耗
    local openTable = {[self._posId] = {["id"] = self._posId, ["x"] = self._x, ["y"] = self._y, ["sureRoute"] = 0}}
    
    --判断是否重复上面的三个步骤
    local isContinue, node = self:_AStarAlgorithm( openTable, closeTable, toData)
    while (isContinue) do
        isContinue, node = self:_AStarAlgorithm( openTable, closeTable, toData)
    end

	--找到终点的话,用终点的parent逆推路径
    if node then
        local temp = {}
        table.insert(temp, node.id)
        while (node.parent) do
            node = node.parent
            table.insert(temp, node.id)
        end
        local step = {}
        local num = #temp
        for i = 1, num do
            table.insert(step, temp[num - i + 1])
        end
        return step
    end
    return nil
end

function ElementData:_AStarAlgorithm( openTable, closeTable, toData)
	--如果open表空,没找到路径
    if next(openTable) == nil then
        return false
    else
    	--找到open表里F值最小的格子作当前格
        local node = self:_getFirstPriorityNode(openTable)
        --如果当前格是终点,返回
        if node.id == toData:getId() then
            return false, node
        else
        	--当前格放入close表
            closeTable[node.id] = node
            --x,y的判断是项目里的业务逻辑,不用管,这个步骤是将当前格node的上下左右四个点加入open表作判断
            if node.x > 1 then
                local nextData = self:getGridDataByPos(node.x - 1, node.y)
                self:_updateOpenTable(openTable, closeTable, nextData, node, toData)
            end
            if node.x < 5 then
                local nextData = self:getGridDataByPos(node.x + 1, node.y)
                self:_updateOpenTable(openTable, closeTable, nextData, node, toData)
            end
            if node.y > 1 then
                local nextData = self:getGridDataByPos(node.x, node.y - 1)
                self:_updateOpenTable(openTable, closeTable, nextData, node, toData)
            end
            if node.y < self._rows - 2 then
                local nextData = self:getGridDataByPos(node.x, node.y + 1)
                self:_updateOpenTable(openTable, closeTable, nextData, node, toData)
            end
            
            openTable[node.id] = nil
        end
        return true
    end
end

function ElementData:_updateOpenTable(openTable, closeTable, nextData, node, toData)
	--如果格子在close表则忽略
    if closeTable[nextData:getId()] then
        return
    end
    --格子可以到达的判断
    if not nextData:cannotMove() and nextData:getY() ~= 1 then
    	--sureRoute为当前格的G值,即当前格到起点的距离消耗
        local sureRoute = node.sureRoute
        local nextNode = openTable[nextData:getId()]
        --判断新的可到达格子是否已经在open表(即有旧的可到达格子路径消耗)
        if nextNode then
        	--sureRoute+1,以当前格作路径算新格子到起点的距离消耗(G值),判断是否小于旧的路径消耗
            if sureRoute + 1 < nextNode["sureRoute"] then
            	--小于的话更新新的G值(sureRoute)和F值(route),并设置父节点为当前格(新的最短距离消耗的路径方向)
                nextNode["sureRoute"] = sureRoute + 1
                nextNode["route"] = sureRoute + 1 + math.abs(toData:getX() - nextData:getX()) + math.abs(toData:getY() - nextData:getY())
                nextNode["parent"] = node
            end
        else
        	--没有旧路径消耗直接设置新格子数据
            openTable[nextData:getId()] = {["id"] = nextData:getId(), ["x"] = nextData:getX(), ["y"] = nextData:getY(), ["parent"] = node, ["sureRoute"] = sureRoute + 1, ["route"] = sureRoute + 1 + math.abs(toData:getX() - nextData:getX()) + math.abs(toData:getY() - nextData:getY())}
        end
    end
end

function ElementData:_getFirstPriorityNode(openTable)
    local priorityNode
    --route是F值
    for k, v in pairs(openTable) do
        if not priorityNode then
            priorityNode = v
        elseif v.route < priorityNode.route then
            priorityNode = v
        end
    end
    return priorityNode
end

注:如果不考虑H值(即不在openlist/候选集寻找离终点最近的格子),算法就是Dijkstra算法。是贪心思想的实现。

贪心思想:将问题建立数学模型,再分成若干子问题,对子问题求解。寻找每个子问题的局部最优解。通过子问题局部最优解合成原问题的解。
贪心无法确保得到的解一定是最优解。通常用来解最大或最小问题。

例如:Dijkstra就是将分解起点终点之间每个可到达的目标点,寻找起点到这些目标点的最小距离。再根据每个目标点的最小距离合成起点到终点的最小距离。a*则是在选择计算目标点的算法里通过曼哈顿距离(启发函数,通常是曼哈顿算法)选择离终点最近的目标点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值