基于Python的深度优先搜索和广度搜索----解决迷宫最短路径问题
好习惯,讲问题先上图:
第二个好习惯,先上完整的代码:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import random
matplotlib.rcParams["font.sans-serif"] = ["SimHei"]
matplotlib.rcParams["axes.unicode_minus"] = False
def init():
global map
global xmax
global ymax
map = np.zeros((xmax+2,ymax+2))
map[0][:] = -1
map[xmax+1][:] = -1
for i in range(xmax+2):
map[i][0] = -1
map[i][-1] = -1
map[2][2:8] = -1
map[1][5:9] = -1
map[4][2:8] = -1
for i in range(3,7):
map[i][6] = -1
for i in range(4,8):
map[i][8] = -1
for i in range(6,9):
map[i][3] = -1
for i in range(8,11):
map[i][5] = -1
print(map)
def deepFirstSearch(steps,x,y):
global map
current_step = steps + 1
map[x][y] = current_step
next_step = current_step + 1
#遍历周围4个点:
#节点不是-1表示非障碍:
#0表示没遍历过 步数加1
#里面比当前的next_step大 说明不是最优方案
if map[x-1][y] != -1 \
and (map[x-1][y]>next_step or map[x-1][y]==0) :
deepFirstSearch(current_step,x-1,y)
if map[x][y-1] != -1 \
and (map[x][y-1]>next_step or map[x][y-1]==0) :
deepFirstSearch(current_step,x,y-1)
if map[x][y+1] != -1 \
and (map[x][y+1]>next_step or map[x][y+1]==0) :
deepFirstSearch(current_step,x,y+1)
if map[x+1][y] != -1 \
and (map[x+1][y]>next_step or map[x+1][y]==0) :
deepFirstSearch(current_step,x+1,y)
def find_way(end):
global map
dirs = [(0,-1),(-1,0),(0,1),(1,0)]
track = [[end]]
for k in range(1,int(map[end[0]][end[1]])):
track0 = []
for i in track[-1]:
for j in range(4):
if (int(map[int(i[0]) + dirs[j][0]][int(i[1]) + dirs[j][1]]) == int(map[i[0]][i[1]]) - 1) &\
([i[0] + dirs[j][0],i[1] + dirs[j][1]] not in track0):
track0.append([i[0] + dirs[j][0],i[1] + dirs[j][1]])
track.append(track0)
return track
if __name__ == "__main__":
xmax = 10
ymax = 10
start = [1,1]
end = [1,ymax]
map = []
init()
oldmap = map.copy()
pathmap = map.copy()
deepFirstSearch(-1,start[0],start[1])
print(map[end[0]][end[1]])
for i in range(xmax+2):
for j in range(ymax+2):
if map[i][j] == -1:
plt.scatter(i,j,s=5,c="black")
track = find_way(end)
for i in track:
p = i[0]
plt.scatter(p[0],p[1],s=3,c="yellow")
plt.scatter(start[0],start[1],s=3,c="red")
plt.scatter(end[0],end[1],s=3,c="blue")
plt.title("深度优先搜索最短路径!")
plt.savefig("1.jpg")
地图初始化部分:
额,我觉得吧,地图嘛,美观就行,随便化,是吧。
为了方便,不需要特别为边缘部分写一段代码,我给地图加了一个边框。地图不可通行部分是用-1表示的
虽然numpy对这点数据来说跑起来没啥帮助,但是习惯了,对,楼主是计算物理本家的
换一个大一点的数据跑一下呢?
其实这是有 递归深度 的限制的,Python里面一般是1000次 大概在楼主的例子是32*32的地图模拟。针对递归栈溢出,可以调整默认深度,但是再大的深度还是有限的,而且会占用更多的内存。
而且,事实上,压根没法搞那么多递推。。。
这里小调整一下,还是可以到四五十*四五十的。
这说明我们的思路是有很大的改进空间的,
不过,如果只是应对作业的话,应该够了。
未来的物理学家留言
import sys
sys.setrecursionlimit(3000)
那么怎么优化呢,我会给在最后的,当然,那得等我先想出来。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import random
matplotlib.rcParams["font.sans-serif"] = ["SimHei"]
matplotlib.rcParams["axes.unicode_minus"] = False
def init():
global map
global xmax
global ymax
map = np.zeros((xmax+2,ymax+2))
map[0][:] = -1
map[xmax+1][:] = -1
for i in range(xmax+2):
map[i][0] = -1
map[i][-1] = -1
map[2][2:8] = -1
map[1][5:9] = -1
map[4][2:8] = -1
for i in range(3,7):
map[i][6] = -1
for i in range(4,8):
map[i][8] = -1
for i in range(6,9):
map[i][3] = -1
for i in range(8,11):
map[i][5] = -1
print(map)
然后是 deepFirstSearch部分了
顺便一提,实际上,如果想模拟得更细致的话,可以用六角形地图,这里用的是四方形地图,显然和实际有些差别。。。
def deepFirstSearch(steps,x,y):
global map
current_step = steps + 1
map[x][y] = current_step
next_step = current_step + 1
#遍历周围4个点:
#节点不是-1表示非障碍:
#0表示没遍历过 步数加1
#里面比当前的next_step大 说明不是最优方案
if map[x-1][y] != -1 \
and (map[x-1][y]>next_step or map[x-1][y]==0) :
deepFirstSearch(current_step,x-1,y)
if map[x][y-1] != -1 \
and (map[x][y-1]>next_step or map[x][y-1]==0) :
deepFirstSearch(current_step,x,y-1)
if map[x][y+1] != -1 \
and (map[x][y+1]>next_step or map[x][y+1]==0) :
deepFirstSearch(current_step,x,y+1)
if map[x+1][y] != -1 \
and (map[x+1][y]>next_step or map[x+1][y]==0) :
deepFirstSearch(current_step,x+1,y)
在这个地方是可以加一些东西以限制深度的,这样就不用无脑增加递归深度了。
比如:
1.限制对偏远地区的搜索,
我们可以大胆推测:最短路线是不会经过 【30-40】 【30-40】的,对于这一部分不需要加以搜索
2.在大片空白区域,
我们可以大胆推测:最短路线是沿着直线的,我们可以先搜索出一大块空白区域,然后直接一条直线连过去。
3.本质上,还是深度的思路不合适,要更换思路。但做一些这些的考量是有益的,因为暴力不是永远管用的。在求解魔方上帝之数的时候,数学家好像就是这样的吗?
4.有没有这样一种可能,正是因为我们的地图有大片的空白,所以无形中浪费了内存?我们在找地图的时候为什么不直接把有效地区给 剪切 出来呢?
5. ...... 自己分析
好了,在给所有的点标上数字后,你就能清楚得知道从任意一个点出发,到达这个点需要的最小步数了
array([[-1., -1., -1., -1., -1., -1., -1., -1., -1., -1., -1., -1., -1.,
-1., -1., -1., -1.],
[-1., 2., 1., 2., 3., -1., -1., -1., -1., 22., 23., 24., 25.,
26., 27., 28., -1.],
[-1., 1., -1., -1., -1., -1., -1., -1., 22., 21., 22., 23., 24.,
25., 26., 27., -1.],
[-1., 2., 3., 4., 5., 6., -1., 22., 21., 20., 21., 22., 23.,
24., 25., 26., -1.],
[-1., 3., -1., -1., -1., -1., -1., -1., -1., 19., 20., 21., 22.,
23., 24., 25., -1.],
[-1., 4., 5., 6., 7., 8., -1., 14., -1., 18., 19., 20., 21.,
22., 23., 24., -1.],
[-1., 5., 6., -1., 8., 9., -1., 13., -1., 17., 18., 19., 20.,
21., 22., 23., -1.],
[-1., 6., 7., -1., 9., 10., 11., 12., -1., 16., 17., 18., 19.,
20., 21., 22., -1.],
[-1., 7., 8., -1., 10., -1., 12., 13., 14., 15., 16., 17., 18.,
19., 20., 21., -1.],
[-1., 8., 9., 10., 11., -1., 13., 14., 15., 16., 17., 18., 19.,
20., 21., 22., -1.],
[-1., 9., 10., 11., 12., -1., 14., 15., 16., 17., 18., 19., 20.,
21., 22., 23., -1.],
[-1., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19., 20., 21.,
22., 23., 24., -1.],
[-1., 11., 12., 13., 14., 15., 16., 17., 18., 19., 20., 21., 22.,
23., 24., 25., -1.],
[-1., 12., 13., 14., 15., 16., 17., 18., 19., 20., 21., 22., 23.,
24., 25., 26., -1.],
[-1., 13., 14., 15., 16., 17., 18., 19., 20., 21., 22., 23., 24.,
25., 26., 27., -1.],
[-1., 14., 15., 16., 17., 18., 19., 20., 21., 22., 23., 24., 25.,
26., 27., 28., -1.],
[-1., -1., -1., -1., -1., -1., -1., -1., -1., -1., -1., -1., -1.,
-1., -1., -1., -1.]])
或者改变 end 和 start
当然,你可能已经发现了。最短路径往往不止一条。
当然,你可能会嘲笑楼主,为什么不在深度搜索时就保存好整个路径呢?
楼主觉得意义不大,这样只会增加内存。。。
其实是楼主不会。。
楼主的方法其实挺简单的,就是从头再搜索一遍,你说,那能不能给出所有的最短路线呢?嗯,问道楼主了,你可以自己推一推,写个码,分享在下面。。
def find_way(end):
global map
dirs = [(0,-1),(-1,0),(0,1),(1,0)]
track = [[end]]
for k in range(1,int(map[end[0]][end[1]])):
track0 = []
for i in track[-1]:
for j in range(4):
if (int(map[int(i[0]) + dirs[j][0]][int(i[1]) + dirs[j][1]]) == int(map[i[0]][i[1]]) - 1) &\
([i[0] + dirs[j][0],i[1] + dirs[j][1]] not in track0):
track0.append([i[0] + dirs[j][0],i[1] + dirs[j][1]])
track.append(track0)
return track
那么的话,在主函数部分的话,就只剩下地图可视化的部分了。回到开始去找吧。
游戏结束。
哦!
你还记得怎么避免递归深度吗?嗯
那当然是bfs广度搜索啦
上图:
上代码:
import numpy as np
import matplotlib.pyplot as plt
pre_route = []
q = []
dire = [(1,0),(-1,0),(0,1),(0,-1)]
visited = []
route = []
father = []
def bfs():
global xmax
global ymax
global start
global map
global end
global q
global dire
global father
global route
global pre_route
global visited
visited = np.zeros((xmax+2,ymax+2))
visited[start[0]][start[1]]=1
q.append([start[0],start[1]])
while q:
now = q[0]
q.pop(0)
for i in range(4):
point=[now[0] + dire[i][0],now[1] + dire[i][1]]
if map[point[0]][point[1]] == -1 or visited[point[0]][point[1]] == 1:
continue
father.append(now)
visited[point[0]][point[1]] = 1
q.append(point)
pre_route.append(point)
if point[0] == end[0] and point[1] == end[1]:
return True
print("there is no way to exit")
return False
def get_route():
global father
global route
global pre_route
route = [pre_route[-1],father[-1]]
for i in range(len(pre_route)-1,-1,-1):
if pre_route[i]==route[-1]:
route.append(father[i])
route.reverse()
return route
def show_map():
global map
global route
global end
global start
plt.scatter(list(np.where(map==-1)[0]),list(np.where(map==-1)[1]),s=3,c="black")
plt.scatter([i[0] for i in route],[i[1] for i in route],s=3,c="yellow")
plt.scatter(start[0],start[1],s=3,c="red")
plt.scatter(end[0],end[1],s=3,c="blue")
plt.show()
def init():
global xmax
global ymax
global map
map = np.zeros((xmax,ymax))
map[3][4:53] = -1
map[4][44:89] = -1
map[5][2:52] = -1
map[53][66:78] = -1
map[91][20:67] = -1
map[0][:] = -1
map[-1][:] = -1
for i in range(ymax):
map[i][0] = -1
map[i][-1] = -1
if __name__ =='__main__':
start = [1,1]
end = [25,40]
xmax = 100
ymax = 100
map = []
init()
if bfs():
route=get_route()
show_map()
嗯,那什么是广度搜索呢:
这玩意比深度搜索好一点的地方是,额,深度搜索需要把所有的地方都搜索一遍,这就很难应对大型地图。我们把广度搜索所搜索的地方表示出来:
你看。很明显。少分析了很多数据。
在解决 最短 最快 的问题时,应当有限使用广度搜索法。。。
很好,很有精神!!