深圳幻海软件技术有限公司 欢迎您!

A*算法求解八数码难题(python实现)

2023-04-13

目录文章目录前言一、八数码难题是什么?二、算法详解1.启发函数(曼哈顿距离)2.状态移动处理3.A*搜索并返回路径 三、完整代码(注释很详尽)总结前言        本文用python实现A*算法解决了八数码问

目录

文章目录

前言

一、八数码难题是什么?

二、算法详解

1.启发函数(曼哈顿距离)

2.状态移动处理

3. A*搜索并返回路径

 三、完整代码(注释很详尽)

总结



前言

        本文用python实现A*算法解决了八数码问题,有被八数码问题困扰的uu们,抓紧时间,进入正文,一定会对你有所帮助!

一、八数码难题是什么?


       八数码问题是在一个3×3的棋盘上有1−8位数字随机分布,以及一个空格,与空格相连的棋子可以滑动到空格中,问题的解是通过空格滑动,使得棋盘转化为目标状态,如下图所示。

7  2 4                     0   1   2

5 0 6     ----------->  3  4    5

8 3 1                      6   7   8

init state                target state

        为了简化问题的输入,首先将空格用数字0表示,然后将3×3的棋盘用9位长的字符串表示,则上图的初始状态为724506831,目标状态为012345678.

        对于上图的初始状态,将数字2移动到空格,称之为u操作(空格上移),将数字3移动到空格,称之为d操作(空格下移),将数字5移动到空格,称之为l操作(空格左移),将数字6移动到空格,称之为r操作(空格右移)
 

二、算法详解

1.启发函数(曼哈顿距离)

  1. def calcDistH(self, src_map, dest_map):
  2. #采用曼哈顿距离作为估值函数
  3. cost = 0
  4. for i in range(len(src_map)):
  5. if (src_map[i] != '0'):
  6. cost += abs(int(src_map[i])//3-i//3) + \
  7. abs(int(src_map[i]) % 3-i % 3)
  8. return cost

2.状态移动处理

  1. def moveMap(self, cur_map, i, j):
  2. cur_state = [cur_map[i] for i in range(9)]
  3. cur_state[i] = cur_state[j]
  4. cur_state[j] = '0'
  5. return "".join(cur_state)

3. A*搜索并返回路径

  1. def salvePuzzle(self, init, targ):
  2. #传入初始状态init,目标状态targ,返回移动路径(string形式)
  3. open = [(init, self.calcDistH(init, targ))]
  4. #open表元素(状态,f值)
  5. closed = {}
  6. dict_depth = {init: 0}
  7. #深度表,用来记录节点是否被访问过及对应的深度
  8. dict_link = {}
  9. #关系表,用来记录父子关系,孩子-->父亲
  10. dict_dirs = {'u': [-1, 0], 'd': [1, 0], 'l': [0, -1], 'r': [0, 1]}
  11. #移动方向,对应二维坐标的变化
  12. dirs = ['l', 'r', 'u', 'd']
  13. path = []
  14. #用来记录移动路径
  15. while (len(open)):
  16. open.sort(key=lambda open: open[1])
  17. #每循环一次对列表按由小到大排序一次
  18. while (open[0][0] in closed):
  19. open.pop([0])
  20. #如果表头元素在closed表中,移出open表
  21. if (open[0][0] == targ):
  22. print("Successfully!")
  23. break
  24. top = open[0]
  25. open[0:1] = []
  26. closed[top[0]] = top[1]
  27. #取表头元素,并将表头元素移出open表,压入closed表
  28. cur_index = top[0].index('0')
  29. for i in range(4):
  30. x = cur_index // 3 + dict_dirs[dirs[i]][0]
  31. y = cur_index % 3 + dict_dirs[dirs[i]][1]
  32. if (x >= 0 and x < 3 and y >= 0 and y < 3):
  33. next_index = x*3+y
  34. next_state = self.moveMap(top[0], cur_index, next_index)
  35. depth = dict_depth[top[0]]+1
  36. #当前节点不在深度表中,压入深度表和open表,并建立父子关系
  37. if ((next_state in dict_depth) == False):
  38. dict_depth[next_state] = depth
  39. open.append(
  40. (next_state, depth+self.calcDistH(next_state, targ)))
  41. dict_link[next_state] = top[0]
  42. else:
  43. #当前节点在深度表中且当前深度小于深度表中的值,更新深度表,建立父子关系
  44. if (depth < dict_depth[next_state]):
  45. dict_depth[next_state] = depth
  46. dict_link[next_state] = top[0]
  47. #如果当前节点在closed表中,将其移出closed表,将更新后的节点移入open表
  48. if (next_state in closed):
  49. del closed[next_state]
  50. open.append(next_state, depth +
  51. self.calcDistH(next_state, targ))
  52. #循环结束,路径关系全部在dict_link中,从目标状态出发寻找路径
  53. s = targ
  54. while (s != init):
  55. move = s.index('0')-dict_link[s].index('0')
  56. if (move == -1):
  57. path.append('l')
  58. elif (move == 1):
  59. path.append('r')
  60. elif (move == -3):
  61. path.append('u')
  62. else:
  63. path.append('d')
  64. s = dict_link[s]
  65. path.reverse()
  66. #将path逆序(如果想要打印出路径每一步的状态,只需要按照path和init就能实现)
  67. print("SearchPath->","".join(path))
  68. return "".join(path)

 三、完整代码(注释很详尽)

  1. class Astar:
  2. def salvePuzzle(self, init, targ):
  3. #传入初始状态init,目标状态targ,返回移动路径(string形式)
  4. open = [(init, self.calcDistH(init, targ))]
  5. #open表元素(状态,f值)
  6. closed = {}
  7. dict_depth = {init: 0}
  8. #深度表,用来记录节点是否被访问过及对应的深度
  9. dict_link = {}
  10. #关系表,用来记录父子关系,孩子-->父亲
  11. dict_dirs = {'u': [-1, 0], 'd': [1, 0], 'l': [0, -1], 'r': [0, 1]}
  12. #移动方向,对应二维坐标的变化
  13. dirs = ['l', 'r', 'u', 'd']
  14. path = []
  15. #用来记录移动路径
  16. while (len(open)):
  17. open.sort(key=lambda open: open[1])
  18. #每循环一次对列表按由小到大排序一次
  19. while (open[0][0] in closed):
  20. open.pop([0])
  21. #如果表头元素在closed表中,移出open表
  22. if (open[0][0] == targ):
  23. print("Successfully!")
  24. break
  25. top = open[0]
  26. open[0:1] = []
  27. closed[top[0]] = top[1]
  28. #取表头元素,并将表头元素移出open表,压入closed表
  29. cur_index = top[0].index('0')
  30. for i in range(4):
  31. x = cur_index // 3 + dict_dirs[dirs[i]][0]
  32. y = cur_index % 3 + dict_dirs[dirs[i]][1]
  33. if (x >= 0 and x < 3 and y >= 0 and y < 3):
  34. next_index = x*3+y
  35. next_state = self.moveMap(top[0], cur_index, next_index)
  36. depth = dict_depth[top[0]]+1
  37. #当前节点不在深度表中,压入深度表和open表,并建立父子关系
  38. if ((next_state in dict_depth) == False):
  39. dict_depth[next_state] = depth
  40. open.append(
  41. (next_state, depth+self.calcDistH(next_state, targ)))
  42. dict_link[next_state] = top[0]
  43. else:
  44. #当前节点在深度表中且当前深度小于深度表中的值,更新深度表,建立父子关系
  45. if (depth < dict_depth[next_state]):
  46. dict_depth[next_state] = depth
  47. dict_link[next_state] = top[0]
  48. #如果当前节点在closed表中,将其移出closed表,将更新后的节点移入open表
  49. if (next_state in closed):
  50. del closed[next_state]
  51. open.append(next_state, depth +
  52. self.calcDistH(next_state, targ))
  53. #循环结束,路径关系全部在dict_link中,从目标状态出发寻找路径
  54. s = targ
  55. while (s != init):
  56. move = s.index('0')-dict_link[s].index('0')
  57. if (move == -1):
  58. path.append('l')
  59. elif (move == 1):
  60. path.append('r')
  61. elif (move == -3):
  62. path.append('u')
  63. else:
  64. path.append('d')
  65. s = dict_link[s]
  66. path.reverse()
  67. #将path逆序(如果想要打印出路径每一步的状态,只需要按照path和init就能实现)
  68. print("SearchPath->","".join(path))
  69. return "".join(path)
  70. def calcDistH(self, src_map, dest_map):
  71. #采用曼哈顿距离作为估值函数
  72. cost = 0
  73. for i in range(len(src_map)):
  74. if (src_map[i] != '0'):
  75. cost += abs(int(src_map[i])//3-i//3) + \
  76. abs(int(src_map[i]) % 3-i % 3)
  77. return cost
  78. def moveMap(self, cur_map, i, j):
  79. cur_state = [cur_map[i] for i in range(9)]
  80. cur_state[i] = cur_state[j]
  81. cur_state[j] = '0'
  82. return "".join(cur_state)
  83. #本程序实现了Astar类,可通过创建Astar对象来调用相关方法
  84. #以下仅为测试用例
  85. test=Astar()
  86. test.salvePuzzle("724506831","012345678")

总结

        以上就是今天要讲的内容,本文介绍了如何使用python实现A*算法,解决八数码难题,希望能够对uu们有所帮助,如果有uu们有不懂的问题或者有更好的创意和建议,可以在评论区留下宝贵意见,随时欢迎私信博主,给博主点个关注,留个赞,博主不胜感激

文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览43944 人正在系统学习中