启发式搜索算法:A算法(全局、局部择优算法)+A*算法 解决八数码问题

本文详细介绍了A算法的全局与局部择优搜索策略,以及A*算法的原理。通过八数码难题为例,展示了两种算法在解决问题时的不同策略,全局择优搜索类似于广度优先,局部择优更接近深度优先。A*算法通过对估价函数的限制,确保找到最优解。在解决八数码问题时,A*算法结合实际代价值和启发式信息,找到最佳路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考博客:人工智能搜索策略:A*算法

1.A 算法

在图搜索算法中,如果能在搜索的每一步都利用估价函数f(n)=g(n)+h(n)对Open表中的节点进行排序,则该搜索算法为A算法。由于估价函数中带有问题自身的启发性信息,因此,A算法又称为启发式搜索算法。
对启发式搜索算法,又可根据搜索过程中选择扩展节点的范围,将其分为全局择优搜索算法局部择优搜索算法
`

1.1.全局择优算法

在全局择优搜索中,每当需要扩展节点时,总是从Open表的所有节点中选择一个估价函数值最小的节点进行扩展。其搜索过程可能描述如下:

(1)把初始节点S0放入Open表中,f(S0)=g(S0)+h(S0);
(2)如果Open表为空,则问题无解,失败退出;
(3)把Open表的第一个节点取出放入Closed表,并记该节点为n;
(4)考察节点n是否为目标节点。若是,则找到了问题的解,成功退出;
(5)若节点n不可扩展,则转到第(2)步;
(6)扩展节点n,生成子节点ni(i=1,2,……),计算每一个子节点的估价值f(ni) (i=1,2,……),并为每一个子节点设置指向父节点的指针,然后将这些子节点放入Open表中;
(7)根据各节点的估价函数值,对Open表中的全部节点按从小到大的顺序重新进行排序;
(8)转第(2)步。

由于上述算法的第(7)步要对Open表中的全部节点按其估价函数值从小到大重新进行排序,这样在算法第(3)步取出的节点就一定是Open表的所有节点中估价函数值最小的一个节点。因此,它是一种全局择优的搜索方式

对上述算法进一步分析还可以发现:
如果取估价函数f(n)=g(n),则它将退化为代价树的广度优先搜索;
如果取估价函数f(n)=h(n),则它将退化为广度优先搜索。
可见,广度优先搜索和代价树的广度优先搜索是全局择优搜索的两个特例

1.1.1.求解八数码

例 1: 八数码难题。 设问题的 初始状态S0 和 目标状态Sg 如图所示,估价函数与请用全局择优搜索解决该题。

在这里插入图片描述
图1 八数码难题的全局择优搜索树
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述





1.2.局部择优算法

在局部择优搜索中,每当需要扩展节点时,总是从刚生成的子节点中选择一个估价函数值最小的节点进行扩展。其搜索过程可描述如下:

(1)把初始节点S0放入Open表中,f(S0)=g(S0)+h(S0);
(2)如果Open表为空,则问题无解,失败退出;
(3)把Open表的第一个节点取出放入Closed表,并记该节点为n;
(4)考察节点n是否为目标节点。若是,则找到了问题的解,成功退出;
(5)若节点n不可扩展,则转到第(2)步;
(6)扩展节点n,生成子节点ni(i=1,2,……),计算每一个子节点的估价值f(ni) (i=1,2,……),并 按估价值从小到大的顺序依次放入Open表的首部(不是全部排序,而是这个扩展节点n生成的子节点),并为每一个子节点设置指向父节点的指针,然后转第(2)步。

由于这一算法的第六步仅仅是把刚生成的子节点按其估价函数值从小到大放入Open表中,这样在算法第(3)步取出的节点仅是刚生成的子节点中估价函数值最小的一个节点。因此,它是一种局部择优的搜索方式。

对这一算法进一步分析也可以发现:
如果取估价函数f(n)=g(n),则它将退化为代价树的深度优先搜索;
如果取估价函数f(n)=d(n),则它将退化为深度优先搜索。
可见,深度优先搜索和代价树的深度优先搜索是局部择优搜索的两个特例

1.2.1.求解八数码

和用全局择优算法一样(估价函数一样),但扩展的顺序不一样
局部更类似深度,全局跟类似广度
如下图片可体现区别:

在此仅画出权重f(x),对于八数码的各个变化不再详细画出。在这里插入图片描述
在这里插入图片描述
在这里插入图片描述





2.A*算法

上一节讨论的启发式搜索算法,都没有对估价函数f(n)做任何限制。实际上,估价函数对搜索过程是十分重要的,如果选择不当,则有可能找不到问题的解,或者找到的不是问题的最优解。为此,需要对估价函数进行某些限制。A* 算法就是对估价函数加上一些限制后得到的一种启发式搜索算法。
假设f*(n)为从初始节点S0出发,约束经过节点n到达目标节点的最小代价值。估价函数f(n)则是f*(n)的估计值。
显然,f*(n)应由以下两部分所组成:

  • 一部分是从初始节点S0到节点n的最小代价,记为g*(n);
  • 另一部分是从节点n到目标节点的最小代价,记为h*(n),当问题有多个目标节点时,应选取其中代价最小的一个。

因此有 f*(n)=g*(n) +h*(n)

把估价函数f(n)与 f*(n)相比

  • g(n)是对g*(n)的一个估计
  • h(n)是对h*(n)的一个估计。

在这里插入图片描述

则称得到的算法为A*算法

2.1 解决八数码难题

g*(n)为起点n状态的最短路径代价值;换句话说,就是层数
h*(n)是n状态目的状态的最短路径的代价值。换句话说,“不在位”数码个数至少要移动多少位才能变成目标数码对应的位置
这样,f*(n)就是起点出发通过n状态而到达目的状态的最佳路径的总代价值。

在该图中,从这些值还可以看出,最佳路径上的节点都有 f*=g*+h*=4.

在这里插入图片描述

### 启发式算法解决八数码问题 #### A*算法简介 A*算法是一种高效的启发式搜索方法,广泛应用于路径规划和图搜索领域。它通过综合考虑当前状态的实际代价(g(n))和预估未来代价(h(n)),计算总评价函数f(n)=g(n)+h(n)[^1]。 #### 八数码问题描述 八数码问题是经典的NP难题之一,其目标是从初始布局移动空白格子使得最终达到指定的目标布局。此问题可以被建模成一个状态空间搜索问题,在这个模型里每一个可能的状态都是棋盘的一种排列方式[^2]。 #### Python代码实现示例 下面提供了一个基于Python的简单版本A*算法解决八数码问题: ```python import heapq class PuzzleState: def __init__(self, board, parent=None, move=""): self.board = tuple(board) self.parent = parent self.move = move if self.parent is not None: self.g = parent.g + 1 else: self.g = 0 self.h = self.heuristic() def heuristic(self): """曼哈顿距离作为启发函数""" distance = 0 goal_positions = {1: (0, 0), 2: (0, 1), 3: (0, 2), 4: (1, 0), 5: (1, 1), 6: (1, 2), 7: (2, 0), 8: (2, 1)} for i in range(9): value = self.board[i] if value != 0 and value != 'B': target_x, target_y = divmod(goal_positions[value][0], 3), \ divmod(goal_positions[value][1], 3) current_x, current_y = divmod(i, 3) distance += abs(current_x - target_x) + abs(current_y - target_y) return distance @property def f(self): return self.g + self.h def get_blank_position(self): index = list(self.board).index('B') return divmod(index, 3) def generate_children(self): children = [] blank_row, blank_col = self.get_blank_position() moves = [ ("UP", blank_row - 1, blank_col), ("DOWN", blank_row + 1, blank_col), ("LEFT", blank_row, blank_col - 1), ("RIGHT", blank_row, blank_col + 1) ] for direction, row, col in moves: if 0 <= row < 3 and 0 <= col < 3: new_board = list(self.board) swap_index = row * 3 + col old_index = blank_row * 3 + blank_col temp = new_board[swap_index] new_board[swap_index] = 'B' new_board[old_index] = temp child_state = PuzzleState(new_board, self, direction) children.append(child_state) return children def a_star_search(initial_state, goal_state=('B', 1, 2, 3, 4, 5, 6, 7, 8)): open_set = [] closed_set = set() initial_node = PuzzleState(initial_state) heapq.heappush(open_set, (initial_node.f, initial_node)) while open_set: _, current_node = heapq.heappop(open_set) if current_node.board == goal_state: path = [] node = current_node while node is not None: path.insert(0, (node.board, node.move)) node = node.parent return path closed_set.add(current_node.board) for child in current_node.generate_children(): if child.board in closed_set: continue found_in_open = False for idx, (_, existing_child) in enumerate(open_set): if existing_child.board == child.board: found_in_open = True if child.f < existing_child.f: del open_set[idx] heapq.heapify(open_set) if not found_in_open: heapq.heappush(open_set, (child.f, child)) return [] if __name__ == "__main__": start = ('B', 1, 3, 4, 2, 5, 7, 8, 6) solution_path = a_star_search(start) print(f'Solution Path Length: {len(solution_path)}\nPath:') for state, action in solution_path: print(state, "-", action or "Start") ``` 以上代码定义了`PuzzleState`类表示每个状态,并实现了必要的属性与方法;利用优先队列管理待扩展节点集合open_set并记录已经访问过的closed_set以避免重复处理相同状态[^2]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你说的白是什么白_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值