当前位置:   article > 正文

八数码难题——启发算法_八数码open表与closed表

八数码open表与closed表

启发算法和盲目搜索算法,同样需要open表和closed表,最大的不同之处是:每个节点有一个启发值,然后优先搜索启发值小的节点。

因为每个节点都有一个启发值属性,需把启发值和节点一同放入open表,所以管理open表的方式略有不同,这里定义了一个新的expand_new函数管理open。还有,open表也不再按先进先出或后进先出的原则弹出节点,而是按启发值大小弹出节点。此外还需要定义一个按启发值大小弹出节点的新函数pop。

代码在前面盲目搜索(带路径的)的基础编写,加入启发函数部分。

  1. from cha2_8数码_带路径 import find_path
  2. import cha2_8数码_带路径 as ch
  3. # 这里两种方式引入前面盲目搜索(带路径)代码,一个是只引入函数find_path,一个是整个文件引入为ch(作为当下的文件中的一个对象),可以ch.xxx 访问ch中定义的变量和函数。但是有一点需要注意,就是 if __name__ == "__main__": 后面的不被导入。
  4. # 全局变量:
  5. closed = [] # 字典
  6. # open = [] # 此处不再定义open,用导入的ch 中的open,方式 ch.open,
  7. son_father = {} # 记录搜索树的字典。
  8. # 定义函数。原来的expand 需要重新定义,因为需要计算节点的启发函数,并把节点的启发函数值当作节点的属性一起放到open中。原来压入节点的是str,现在压入的节点是这样的列表 [g(str),h(str),str],还需要定义计算h(str) 的函数
  9. def expand_new(now,endlayout):
  10. zero_pos = now[2].index("0")
  11. allowed_pos = ch.dic_of_rule[zero_pos] #当前可进行交换的位置集合
  12. for k in allowed_pos:
  13. newstr = ch.swap_chr(now[2],k, zero_pos) # 对每个允许的交换执行交换
  14. if newstr not in closed: # 检测newstr在不在closed中
  15. ch.open.append([now[0]+1, heu(newstr,endlayout), newstr])
  16. if son_father.get(newstr) == None:
  17. son_father[newstr] = now[2]
  18. return
  19. def pop(open): # 在open中弹出启发函数值最小的节点,启发值就是 g + h
  20. w = min(open, key=lambda x: x[0] + x[1]) #试试X[0]**2+x[1],能找到移动21步的解
  21. open.remove(w) # 删除 w。
  22. return w
  23. # 下面定义了三个启发函数heu_1, heu_2, heu_3
  24. def heu_1(strr,endlayout): # 用strr中各字符的位置和在目标中的位置的差,作为启发函数
  25. total_var = 0
  26. for i in [str(k) for k in range(9)]: # 所有差累加
  27. total_var = total_var + abs(strr.index(i)-endlayout.index(i))
  28. return total_var
  29. def heu_2(strr,endlayout): # 所有差的最大值
  30. max_var = 0
  31. for i in [str(k) for k in range(9)]:
  32. if abs(strr.index(i)-endlayout.index(i)) > max_var:
  33. max_var = abs(strr.index(i)-endlayout.index(i))
  34. return max_var
  35. def heu_3(strr,endlayout): # 所有差的平方和。
  36. total_var = 0
  37. for i in [str(k) for k in range(9)]:
  38. total_var = total_var + abs(strr.index(i)-endlayout.index(i))**2
  39. return total_var
  40. # 主程序
  41. if __name__ == "__main__":
  42. heu = heu_3 # 选择第三个启发函数,选择heu_2费时较长。
  43. startlayout = "541203786"
  44. endlayout = "123804765"
  45. son_father[startlayout] = -1
  46. ch.open.append([0,heu(startlayout,endlayout),startlayout])
  47. iter = 0
  48. while 1:
  49. print(iter)
  50. iter +=1
  51. if len(ch.open) == 0:
  52. print('搜索到头了,没有移动方案能移动到目标')
  53. break
  54. else:
  55. # now = open.popleft()
  56. now = pop(ch.open) # 弹出 启发函数值最小的节点
  57. closed.append(now[2]) # 放入closed
  58. if now[2] == endlayout: # 如到达目标
  59. print('能移动到目标,共搜索步数:', iter)
  60. path = find_path(son_father,endlayout) # 生成移动路径
  61. print('需要移动 {} 步,\n移动路径是:{}'.format(len(path),path))
  62. break
  63. else:
  64. expand_new(now,endlayout) # 展开当前now 节点,管理open和son_father

运行结果:

能移动到目标,总搜索步数: 208

需要移动 25 步,

移动路径是:deque(['541203786', '501243786', '051243786', '251043786', '251403786', '201453786', '210453786', '213450786', '213405786', '213485706', '213485760', '213480765', '210483765', '201483765', '281403765', '281043765', '081243765', '801243765', '810243765', '813240765', '813204765', '813024765', '013824765', '103824765', '123804765'])

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/379737
推荐阅读
相关标签
  

闽ICP备14008679号