以下是通过阅读这个链接(写的真挺不错,而且作者还提供了一些可视化的操作,方便理解各个算法的异同,强烈推荐),自己收获的一些浅俗的理解和总结。如有偏差和错误还望评论区指正。
a.没有任何约束条件的向外扩张,遍历保存所有节点的来源节点,存至came_from字典中:
frontier = Queue()
frontier.put(start )
came_from = dict() # path A->B is stored as came_from[B] == A
came_from[start] = None
while not frontier.empty():
current = frontier.get()
for next in graph.neighbors(current):
if next not in came_from:
frontier.put(next)
came_from[next] = current
b.从目标点goal出发,在came_from中逆向寻找起点,未找到起点时反复进行循环,直到找到起点为止,之后将目标点加入寻路路径中,将路径反转:
current = goal
path = []
while current != start:
path.append(current)
current = came_from[current]
path.append(start) # optional
path.reverse() # optional
优化.在上述a搜寻came_from时加入判断条件,若搜寻到目标节点,则跳出循环:
frontier = Queue()
frontier.put(start )
came_from = dict()
came_from[start] = None
while not frontier.empty():
current = frontier.get()
if current == goal:
break
for next in graph.neighbors(current):
if next not in came_from:
frontier.put(next)
came_from[next] = current
在某些情况下,每一步的消耗(时间、物理,等等)是不同的,所以需要新加一个参数cost_so_far来记录从起始位置到当前位置所消耗的总成本。所以在添加came_from节点时,先考虑对比一下cost,从中找出消耗最小的节点将其加入came_from。
frontier = PriorityQueue() #Queue(队列)->PriorityQueue(优先队列)
frontier.put(start, 0)
came_from = dict()
cost_so_far = dict()
came_from[start] = None
cost_so_far[start] = 0
while not frontier.empty():
current = frontier.get()
if current == goal:
break
for next in graph.neighbors(current):
new_cost = cost_so_far[current] + graph.cost(current, next)
if next not in cost_so_far or new_cost < cost_so_far[next]:
cost_so_far[next] = new_cost
priority = new_cost
frontier.put(next, priority)
came_from[next] = current
注意事项:PriorityQueue需要返回优先返回队列中最小值(python中如果是数值的话默认会从大到小排列,其他类型的话需要自定义)。
由于最小消耗并不一定是最短路径,所以有了下面的启发式搜寻。
大部分情况下我们不需要向任意方向去搜寻路径,只需要去向目标点的方向去查找路径。所以在 Greedy Best First Search中我们不把cost作为优先队列去考虑,而是始终将下一个节点和终点的距离加入队列中,然后找出最短距离,取出这个节点,再从这个节点出发去寻找下一个节点,循环,直至找到终点。
frontier = PriorityQueue()
frontier.put(start, 0)
came_from = dict()
came_from[start] = None
while not frontier.empty():
current = frontier.get()
if current == goal:
break
for next in graph.neighbors(current):
if next not in came_from:
priority = heuristic(goal, next)
frontier.put(next, priority)
came_from[next] = current
优点:地形简单的话,搜寻路径会很快。
缺点:地形复杂的话,如图...
算法直奔目标点去,结果发现此路不通,然后往回搜寻,拐了个大弯,结果找到的路径并非最短路径。
解决办法:A星算法。
Dijkstra’s Algorithm可以找到最短路径,但是却比较浪费时间;Greedy Best First Search相对来说比较快,但是却找不到最短路径。
A星算法则既使用了Dijkstra’s Algorithm来获得起点到当前位置的实际距离消耗,又使用了当前距离到终点的预估距离作为判断,将两者结果相加获得的值加入优先队列中,从中取得最小的值,即为下一个移动的目标点。
frontier = PriorityQueue()
frontier.put(start, 0)
came_from = dict()
cost_so_far = dict()
came_from[start] = None
cost_so_far[start] = 0
while not frontier.empty():
current = frontier.get()
if current == goal:
break
for next in graph.neighbors(current):
new_cost = cost_so_far[current] + graph.cost(current, next)
if next not in cost_so_far or new_cost < cost_so_far[next]:
cost_so_far[next] = new_cost
priority = new_cost + heuristic(goal, next)
frontier.put(next, priority)
came_from[next] = current
1.假如你的游戏需要全方位的寻找路径,那么优先使用Breadth First Search或者Dijkstra’s 算法。如果你的每一步消耗都是一样的,则使用Breadth First Search,反之,则选择Dijkstra’s 算法。
2.假如你只需要寻找起点到目标点路径,则优先使用A星算法。
以上算法Python、C++、C#实现示例
这是自己实现的一个demo,使用Unity+C#,效果如图:
资源链接