不同的估价函数对搜索算法性能的影响
曼哈顿距离和欧几里得距离是两种不同的启发式函数,它们都可以用来进行 A* 搜索算法。曼哈顿距离在计算时只考虑横纵方向上的距离,而欧几里得距离则考虑了所有方向上的距离。因此,在一些情况下,曼哈顿距离的计算方式可能更适合迷宫寻路问题的启发式函数。
在迷宫问题中,使用曼哈顿距离作为启发式函数可以得到较好的效果。因为在这个问题中,每个节点的移动代价都是1,而曼哈顿距离可以直接估计两点之间的移动代价,所以使用曼哈顿距离可以准确地估计每个节点到目标点的代价,并且能够快速收敛到最优解。
使用欧几里得距离作为启发式函数可能会导致搜索算法在空间上浪费大量的时间和资源,因为欧几里得距离不能准确地估计每个点到目标点的代价,可能会导致搜索算法沿着错误的方向进行,并且需要扩展更多的节点才能找到最优解。
流程图
源程序代码
import time
import heapq
# 5*5迷宫,数字0表示可通过,数字1表示障碍物
maze = [
[0, 0, 0, 0, 0],
[0, 1, 0, 1, 0],
[0, 1, 0, 1, 0],
[0, 1, 0, 1, 0],
[0, 0, 0, 1, 0]
]
start = (0, 0) # 初始点位置
goal = (4, 4) # 目标点位置
# 定义状态节点类
class Node:
def __init__(self, position, parent, g, h):
self.position = position # 节点位置
self.parent = parent # 父节点
self.g = g # 到达此节点所需步数
self.h = h # 启发式函数
# 计算f值
def f(self):
return self.g + self.h
# 定义小于运算符用于堆排序
def __lt__(self, other):
return self.f() < other.f()
# 获取启发式函数h1值(曼哈顿距离)
def get_h1(position):
return abs(goal[0] - position[0]) + abs(goal[1] - position[1])
# 获取启发式函数h2值(欧几里得距离)
def get_h2(position):
return ((goal[0] - position[0]) ** 2 + (goal[1] - position[1]) ** 2) ** 0.5
# 获取一个节点的后继状态列表
def get_successors(node, h_func):
successors = []
for i in range(-1, 2):
for j in range(-1, 2):
if i == 0 and j == 0:
continue
new_x = node.position[0] + i
new_y = node.position[1] + j
if new_x < 0 or new_x >= len(maze) or new_y < 0 or new_y >= len(maze[0]):
continue
if maze[new_x][new_y] == 1: # 障碍物
continue
new_position = (new_x, new_y)
new_g = node.g + 1
new_h = h_func(new_position) # 使用传递进来的启发式函数计算新节点的启发式值
new_node = Node(new_position, node, new_g, new_h)
successors.append(new_node)
return successors
# A*算法求解迷宫寻路问题
def astar_search(start_position, goal_position, h):
start_node = Node(start_position, None, 0, h(start_position))
pq = []
heapq.heappush(pq, (start_node.f(), start_node))
visited = set()
nodes_generated = 0
while pq:
current_node = heapq.heappop(pq)[1]
if current_node.position == goal_position:
path = []
while current_node:
path.append(current_node.position)
current_node = current_node.parent
return path[::-1], nodes_generated
if current_node.position in visited:
continue
visited.add(current_node.position)
# 正确传递启发式函数 h 到 get_successors()
successors = get_successors(current_node, h)
nodes_generated += len(successors)
for successor in successors:
if successor.position not in visited:
heapq.heappush(pq, (successor.f(), successor))
return None, nodes_generated
if __name__ == '__main__':
print('迷宫:')
for row in maze:
print(row)
# 使用启发式函数h1
start_time = time.time()
path, nodes_generated = astar_search(start, goal, get_h1)
end_time = time.time()
print('使用启发式函数h1:')
print('路径: ', path)
print('生成节点数: ', nodes_generated)
print('运行时间: ', end_time - start_time)
# 使用启发式函数h2
start_time = time.time()
path, nodes_generated = astar_search(start, goal, get_h2)
end_time = time.time()
print('使用启发式函数h2:')
print('路径: ', path)
print('生成节点数: ', nodes_generated)
print('运行时间: ', end_time - start_time)
运行结果:
结果分析
这段代码实现了 A* 算法来解决迷宫寻路问题。迷宫是一个 5x5 的二维数组,其中 0 表示可以通过的位置,1 表示障碍物。起点是 (0,0),终点是 (4,4)。在此代码中,我们通过定义两个不同的启发式函数h1和h2来比较它们对算法寻路效率的影响。h1函数采用曼哈顿距离来计算节点到目标点的估价值,h2函数则采用欧几里得距离。结果显示,在该迷宫中,无论使用h1还是h2函数,A*算法都能找到起点到终点的唯一最短路径(长度为7),生成的节点数分别为40和43个
实验总结
通过对本次实验,我们实现了5*5迷宫路径问题的求解。在 5*5 迷宫路径问题中,我们采用了不同的启发式函数,如曼哈顿距离和欧几里得距离,并较为有效地解决了问题。当下许多问题都可以通过搜索算法解决,其中 A* 算法是广泛应用的一种。A* 算法结合了 Dijkstra 算法和启发式函数方法,可在多个领域的计算机科学中使用。它具有完备性、最优性和高效性,特别是在小型搜索空间上。