如何解决如何在二维迷宫数组中找到最短路径,只靠撞墙
我正在努力实现一种算法,该算法通过仅在碰到墙壁或其他障碍物时改变方向来解决 2D 迷宫阵列。
它需要做的是,给定以下数组(其中 x
是起点,1
是障碍物,g
是目标)找到最短路径,只需命中障碍物优先(除非到达障碍物/墙壁,否则不会改变移动方向)。
[[1,1,1]
[1,x,g]
[1,1]]
解决方案应该是:
[(1,1),(2,4)]
在上面的示例中,它仅在迷宫墙旁边移动,但这只是因为示例非常小。总而言之,它应该只在 4 个方向上移动,并且一旦开始一个方向直到遇到障碍物,它就不会改变它的路线。这是一个更直观的例子:
我设法找到了以下代码,它获取最短路径的长度但不显示路径本身。任何帮助获得这一点将不胜感激。
def shortestdistance(self,maze: List[List[int]],start: List[int],destination: List[int]):
start,destination = tuple(start),tuple(destination)
row,col = len(maze),len(maze[0])
def neighbors(maze,node):
temp = []
used = set()
used.add(node)
for dx,dy in [(-1,0),(0,-1),(1,0)]:
(x,y),dist = node,0
while 0 <= x + dx < row and 0 <= y + dy < col and maze[x + dx][y + dy] == 0:
x += dx
y += dy
dist += 1
if (x,y) not in used:
temp.append((dist,(x,y)))
return temp
heap = [(0,start)]
visited = set()
while heap:
dist,node = heapq.heappop(heap)
if node in visited: continue
if node == destination:
return dist
visited.add(node)
for neighbor_dist,neighbor in neighbors(maze,node):
heapq.heappush(heap,(dist + neighbor_dist,neighbor))
return -1
解决方法
问题:
- 通常的广度优先搜索速度太慢,无法解决迷宫海报的尺寸要求,即 24 x 24。
算法和代码
- 算法基于 wall following algorithm 的修改
- 代码基于 nerijus-st / Maze-Solving 的修改版本
普通墙跟随的关键算法修改
- 继续朝同一个方向走,直到撞墙
- 在当前方向撞墙时,在当前方向的左侧和右侧创建新的分支路径
- 使用堆专注于以最小距离和方向变化次数扩展路径
代码
# Modification of https://github.com/nerijus-st/Maze-Solving/blob/master/left%20hand%20rule.py
import numpy as np
import heapq
class Maze():
# Movement directions
directions = ["N","E","S","W"]
def __init__(self,maze):
assert isinstance(maze,np.ndarray) # maze should be 2D numpy array
self.maze = maze
self.m,self.n = maze.shape
def solve_maze(self,start,goal):
"""
N
W E
S
UP (N) - get_neighbours()['N']
RIGHT (E) - get_neighbours()['E']
DOWN (S) - get_neighbours()['S']
LEFT (W) - get_neighbours()['W']
maze 2D Numpy array
start tuple for stating position
goal tuple for destination
Strategy: Keeps going in same direction until a wall is reached. Then try
form branches for both left and right directions (i.e. search both)
"""
# Try all starting directions
heap = [(0,facing,[start]) for facing in Maze.directions]
heapq.heapify(heap)
while heap:
dist,changes,path = heapq.heappop(heap)
changes = -changes # Negative to make earlier since using min heap
if path[-1] == goal:
return self.compress_path(path)
self.x,self.y = path[-1] # Set current position in maze
front_wall = self.get_front_wall(facing) # Coordinates next position in front
if front_wall and not self.maze[front_wall] and not front_wall in path: # if Clear in front
# Front direction clear
heapq.heappush(heap,(dist+1,-changes,path + [front_wall]))
elif len(path) > 1:
# Try to left
left_wall = self.get_left_wall(facing) # Coordinates to the left
if left_wall and not self.maze[left_wall] and not left_wall in path: # if clear to the left
left_facing = self.rotate_facing(facing,"CCW") # Rotate left (i.e. counter clockwise)
heapq.heappush(heap,-(changes+1),left_facing,path + [left_wall]))
# Try to the right
right_wall = self.get_right_wall(facing) # Coordinates to the right
if right_wall and not self.maze[right_wall] and not right_wall in path: # if Clear to the right
right_facing = self.rotate_facing(facing,"CW") # Rotate right (i.e. clockwise)
heapq.heappush(heap,right_facing,path + [right_wall]))
def compress_path(self,path):
if not path:
return
if len(path) < 3:
return path
result = [path[0]]
for i,p in enumerate(zip(path,path[1:])):
direction = self.get_direction(*p)
if i == 0:
prev_direction = direction
result.append(p[1][:])
continue
if len(result) > 2:
if prev_direction == direction:
result[-1] = p[1][:]
else:
result.append(p[1][:])
else:
result.append(p[1][:])
prev_direction = direction
return result
def get_neighbours(self):
' Neighbors of current position '
x,y = self.x,self.y
result = {}
result['N'] = (x-1,y) if x + 1 < self.m else None
result['S'] = (x+1,y) if x-1 >= 0 else None
result['E'] = (x,y+1) if y + 1 < self.n else None
result['W'] = (x,y-1) if y -1 >= 0 else None
return result
def get_direction(self,point1,point2):
x1,y1 = point1
x2,y2 = point2
if y1 == y2 and x1 - 1 == x2:
return 'N'
if y1 == y2 and x1 + 1 == x2:
return 'S'
if x1 == x2 and y1 + 1 == y2:
return 'E'
if x1 == x2 and y1 - 1 == y2:
return 'W'
def get_left_wall(self,facing):
if facing == "N":
return self.get_neighbours()['W'] # cell to the West
elif facing == "E":
return self.get_neighbours()['N']
elif facing == "S":
return self.get_neighbours()['E']
elif facing == "W":
return self.get_neighbours()['S']
def get_right_wall(self,facing):
if facing == "N":
return self.get_neighbours()['E'] # cell to the East
elif facing == "E":
return self.get_neighbours()['S']
elif facing == "S":
return self.get_neighbours()['W']
elif facing == "W":
return self.get_neighbours()['N']
def get_front_wall(self,facing):
return self.get_neighbours()[facing]
def rotate_facing(self,rotation):
facindex = Maze.directions.index(facing)
if rotation == "CW":
if facindex == len(Maze.directions) - 1:
return Maze.directions[0]
else:
return Maze.directions[facindex + 1]
elif rotation == "CCW":
if facindex == 0:
return Maze.directions[-1]
else:
return Maze.directions[facindex - 1]
测试
测试 1
maze = [[1,1,1],[1,0],1]]
M = Maze(np.array(maze))
p = M.solve_maze((1,1),(2,3))
print('Answer:',p)
# Output: Answer: [(1,3)]
测试 2
maze = [[1,(23,23))
print('Answer:',p)
# Output: Answer: [(1,(1,2),9),(3,12),(5,10),(7,(17,23)]
,
您可以将其解释为图形问题。 生成一个包含所有可能运动的图,这些运动是边缘,可能的位置是节点。边缘应标有其长度(或重量)(例如,在您的情况下可能是 6 个块)。 我猜球从某个地方开始,因此问题是(在考虑图表时):到节点的最短路径是什么,你形成节点 v。
因此您可以使用 Bellman–Ford algorithm。 我不会解释它,因为维基百科在这方面做得更好。 如果你不喜欢那个算法,还有更多的算法可以解决你的问题,比如 Dijkstra 算法。
,您可以使用递归生成器函数,该函数在每一步要么继续沿同一方向前进,要么在撞墙时通过寻找其他可能的方向来改变路线:
def int_21(x):
if x == '2021-01-07 00:00:00':
return '1'
else:
return '0'
输出:
graph = [[1,'x','g'],1]]
d_f = [lambda x,y:(x+1,y),lambda x,y+1),y:(x,y:(x-1,y-1),y-1)]
def navigate(coord,f = None,path = [],s = []):
if graph[coord[0]][coord[-1]] == 'g':
yield path+[coord]
elif f is None:
for i,_f in enumerate(d_f):
if graph[(j:=_f(*coord))[0]][j[-1]] != 1 and j not in s:
yield from navigate(j,f = _f,path=path+[coord],s = s+[coord])
else:
if graph[(j:=f(*coord))[0]][j[-1]] == 1:
yield from navigate(coord,path=path,s = s)
else:
yield from navigate(j,f = f,s = s+[coord])
start = [(j,k) for j,a in enumerate(graph) for k,b in enumerate(a) if b == 'x']
r = min(navigate(start[0]),key=len)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。