A*算法求解15数码问题

目录

一、问题描述

二、算法简介

三、算法步骤

四、评估函数

五、参考资料

六、源代码(Java实现)


一、问题描述

利用A*算法进行表1到表2的转换,要求空白块移动次数最少。

转换规则为:空白块只可以与上下左右四个方向的相邻数字交换。

       表1 起始状态                     表2 目标状态

         

二、算法简介

A*算法是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法。该算法综合了Best-First Search和Dijkstra算法的优点:在进行启发式搜索提高算法效率的同时,基于评估函数计算损失,保证找到一条最优路径。

算法能否找到最优解的关键在于评估函数的选择,A*算法的评估函数表示为:f(n) = g(n) + h(n)

f(n) 是从初始状态经由状态n到目标状态的代价估计

g(n) 是在状态空间中从初始状态到状态n的实际代价

h(n) 是从状态n到目标状态的最佳路径的估计代价

例如在8数码问题中,g(n) 表示状态空间树中搜索的层数,h(n) 表示状态n与目标状态中元素位置不同的元素个数。

三、算法步骤

设定两个集合,open集,close集

1.  将起始点加入open集(设置父亲节点为空)

2.  在open集中选着一个f(n)值最小的节点作为当前节点

    2.1 将当前节点从open集中移除,添加到close集

    2.2 如果当前节点为终点节点,那么结束搜索

    2.3 处理当前节点的所有邻接节点,规则如下:

       如果不在open集中,那么就将其添加到open集,并将该节点的父节点为当前节点

       如果已经添加到open集中,重新计算f(n)值,如果f(n)值小于先前的f(n)值,那么就更新open集中相应节点的f(n)

       如果该节点不可通过或者已经被添加到close集,那么不予处理

3、如果open集不为空,那么转到步骤2继续执行。

四、评估函数

1.   f(n) = 状态n状态空间树中的搜索深度 + 状态n与目标状态不同的元素个数

效果:与8数码问题使用了相同的评估函数,大概跑了30W步无法求出解

评价:效果极差,15数码问题的状态空间树要远复杂于8 数码问题,且15数码问题中空白块的移动更为复杂,此评估函数不适用。

2.   f(n) = 状态n状态空间树中的搜索深度 + 状态n与目标状态各个位置数字偏差的绝对值

效果:随着搜索的进行,空白块的移动集中在表格上部,表格下部几乎不移动 ,无法求出解

评价:因为下部数字较大,移动后差值较大造成评估值较大,因此搜索集中在了数值较小的部分,效果很差。

3.   f(n) = 状态n状态空间树中的搜索深度 + 状态n与目标状态各个元素的路径差值(一维数组各元素的距离差之和)

效果:空白块最终移动55步得到目标状态。

评价:效果比较理想,但h(n)还可继续优化。

4.   f(n) = 状态n状态空间树中的搜索深度 + 状态n与目标状态各个元素的曼哈顿距离

效果:空白块最终移动41步得到目标状态。

评价:效果理想。

实际上,1和2的评估函数效果大致相同,都将搜索局限在了一部分导致无法计算出问题的解。3实际是以一维数组各元素的距离差之和估计状态n到目标状态的曼哈顿距离,但此估计方式和计算平面两点的曼哈顿距离存在较大误差,因此只求解出可行解。

五、参考资料

A*算法详解,看完后全懂了

启发式搜索浅谈,解决八数码问题

六、源代码(Java实现)

Github地址:A*算法求解15数码问题

  • 14
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
数码问题是指在3x3的九宫格中,给定八个数字和一个空格,要求将数字移动,最终达到目标状态。其中,每个数字可以向上、下、左、右四个方向移动,但不能跨越其他数字,也不能移动到九宫格外。 A*算法是一种启发式搜索算法,它通过估计到达目标的成本来决定哪些节点应该先被扩展。在八数码问题中,我们可以用曼哈顿距离来作为启发函数,即当前状态与目标状态之间所有数字的曼哈顿距离之和。曼哈顿距离指的是两个点在网格中沿着网格线走的距离之和。 具体实现步骤如下: 1. 定义状态表示:使用一个3x3的矩阵来表示当前状态,其中0表示空格。 2. 定义状态扩展:对于一个状态,枚举空格可以移动的四个方向,将空格与相邻位置的数交换,生成新的状态。 3. 定义启发函数:计算当前状态与目标状态之间所有数字的曼哈顿距离之和。 4. 定义状态比较:比较两个状态是否相同。 5. 定义搜索过程:使用A*算法进行搜索,将状态按照启发函数的值从小到大排序,每次扩展启发函数值最小的状态,并将它的后继状态加入到搜索队列中。如果搜索到目标状态,搜索结束。 6. 输出解路径:在搜索过程中,记录每个状态的父状态,最终从目标状态开始回溯到初始状态,得到解路径。 具体实现过程可以参考以下Python代码: ```python import heapq class Puzzle: GOAL = [[1, 2, 3], [4, 5, 6], [7, 8, 0]] def __init__(self, state): self.state = state self.f = self.g = self.h = 0 self.parent = None def __lt__(self, other): return self.f < other.f def __eq__(self, other): return self.state == other.state def __hash__(self): return hash(str(self.state)) def is_goal(self): return self.state == Puzzle.GOAL def get_neighbors(self): neighbors = [] row, col = self.find(0) for dr, dc in [(1, 0), (0, 1), (-1, 0), (0, -1)]: nr, nc = row + dr, col + dc if 0 <= nr < 3 and 0 <= nc < 3: neighbor = self.clone() neighbor.swap(row, col, nr, nc) neighbors.append(neighbor) return neighbors def find(self, value): for i in range(3): for j in range(3): if self.state[i][j] == value: return i, j return None def swap(self, r1, c1, r2, c2): self.state[r1][c1], self.state[r2][c2] = self.state[r2][c2], self.state[r1][c1] def clone(self): clone = Puzzle([row[:] for row in self.state]) clone.g = self.g + 1 clone.h = clone.manhattan_distance() clone.f = clone.g + clone.h clone.parent = self return clone def manhattan_distance(self): distance = 0 for i in range(3): for j in range(3): if self.state[i][j] != 0: r, c = self.find(self.state[i][j]) distance += abs(i - r) + abs(j - c) return distance def print_path(self): path = [] node = self while node is not None: path.append(node) node = node.parent path.reverse() for node in path: print(node) def solve(start): open_set = [start] closed_set = set() while open_set: current = heapq.heappop(open_set) if current.is_goal(): return current.print_path() closed_set.add(current) for neighbor in current.get_neighbors(): if neighbor in closed_set: continue if neighbor not in open_set: heapq.heappush(open_set, neighbor) else: existing = open_set[open_set.index(neighbor)] if neighbor.g < existing.g: existing.g = neighbor.g existing.parent = neighbor.parent existing.f = existing.g + existing.h if __name__ == '__main__': start = Puzzle([[1, 2, 0], [4, 5, 3], [7, 8, 6]]) start.h = start.manhattan_distance() start.f = start.g + start.h solve(start) ``` 在这个实现中,我们使用了一个优先队列来保存待扩展的状态,每次从中选择f值最小的状态进行扩展。对于已经搜索过的状态,我们使用一个集合来保存,避免重复搜索。在搜索结束后,我们通过回溯记录的父状态,输出解路径。 这个算法的时间复杂度为O(b^d),其中b是每个状态的平均分支因子,d是目标状态的深度。在实践中,A*算法通常能够在合理的时间内找到解,但在某些情况下可能会陷入无限循环或者搜索空间过大导致运行时间超过预期。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值