微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在二维迷宫数组中找到最短路径,只靠撞墙

如何解决如何在二维迷宫数组中找到最短路径,只靠撞墙

我正在努力实现一种算法,该算法通过仅在碰到墙壁或其他障碍物时改变方向来解决 2D 迷宫阵列。

它需要做的是,给定以下数组(其中 x 是起点,1 是障碍物,g 是目标)找到最短路径,只需命中障碍物优先(除非到达障碍物/墙壁,否则不会改变移动方向)。

[[1,1,1]
 [1,x,g]
 [1,1]]

解决方案应该是:

[(1,1),(2,4)]

在上面的示例中,它仅在迷宫墙旁边移动,但这只是因为示例非常小。总而言之,它应该只在 4 个方向上移动,并且一旦开始一个方向直到遇到障碍物,它就不会改变它的路线。这是一个更直观的例子:

enter image description here

我设法找到了以下代码,它获取最短路径的长度但不显示路径本身。任何帮助获得这一点将不胜感激。

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。

算法和代码

普通墙跟随的关键算法修改

  • 继续朝同一个方向走,直到撞墙
  • 在当前方向撞墙时,在当前方向的左侧和右侧创建新的分支路径
  • 使用堆专注于以最小距离和方向变化次数扩展路径

代码

# 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 举报,一经查实,本站将立刻删除。