【问题标题】:How to find shortest path in X*Y grid如何在 X*Y 网格中找到最短路径
【发布时间】:2020-02-01 13:09:39
【问题描述】:

我在 Python 中有一个 N*M 网格。

在哪里

“X”代表边框,

“1”代表当前位置,

“2”代表完成,而

“3”代表禁区。

最后一件事是(假设你是一辆车)你只能直行或右行。

3x3 示例,边框除外:

[X,X,X,X,X]
[X,2,1,0,X]
[X,0,3,0,X]
[X,0,0,0,X]
[X,X,X,X,X]

另一个例子:

[X,X,X,X,X]
[X,2,0,0,X]
[X,3,3,1,X]
[X,X,X,X,X]

或者其他:

[X,X,X,X,X,X,X]
[X,0,2,0,0,0,X]
[X,0,3,0,3,0,X]
[X,0,0,0,0,0,X]
[X,0,3,0,3,0,X]
[X,0,3,1,3,0,X]
[X,X,X,X,X,X,X]

您对找到最快方法的具体脚本有什么建议吗?

如果没有, print("No solution")?

非常感谢!

为了帮助您了解这些情况:

pictures of examples 1 and 2

【问题讨论】:

  • 网格是列表列表还是其他类型?
  • @n1tr0xs 是的。

标签: python path grid shortest-path


【解决方案1】:

你可以使用这个基于图论的代码:

grid = [
['X','X','X','X','X','X','X'],
['X',2,0,0,0,0,'X'],
['X',0,3,0,3,0,'X'],
['X',0,0,0,0,0,'X'],
['X',0,3,0,3,0,'X'],
['X',0,3,1,3,0,'X'],
['X','X','X','X','X','X','X']
]


INF=1000000

def is_valid(grid:list):
    ends = 0
    starts = 0
    for line in grid:
        for el in line:
            if el==2:
                ends+=1
            if el==1:
                starts+=1
    return False if ends!=1 or starts!=1 else True

def redef_grid(grid:list):
    ''' Deleting from grid all 'X' '''
    g=grid.copy()
    if 'X' in g[0]: del g[0]
    if 'X' in g[-1]: del g[-1]
    for line in g:
        while 'X' in line:
            line.remove('X')
    return g

def uni_index(grid:list, pos:tuple):
    return len(grid[0])*pos[0]+pos[1]

def real_index(grid:list, index:int):
    row =index//len(grid[0])
    col=index-row*len(grid[0])
    return (row, col)

def get_neibs(grid:list, index:int):
    def get_vertical(grid:list, index:int):
        return [index+k for k in [len(grid[0]), -len(grid[0])] if index+k>=0 and index+k<uni_index(grid, (len(grid)-1, len(grid[0])-1))]
    def get_horizontal(grid:list, index:int):
        return [index+k for k in [1, -1] if index+k>=(index//len(grid[0]))*(len(grid[0])) and index+k<=uni_index(grid, (index//len(grid[0]), len(grid[0])-1))]
    return get_vertical(grid, index)+get_horizontal(grid, index)

def get_matrix(grid:list):
    last_el = uni_index(grid, (len(grid)-1, len(grid[0])-1))
    elements=len(grid[0])*len(grid)
    matrix=[[INF for _ in range(elements)] for _ in range(elements)]
    for index in range(last_el):
        neibs=get_neibs(grid, index)
        neibs=[neib for neib in neibs if grid[real_index(grid, neib)[0]][real_index(grid, neib)[1]]!=3]
        for neib in neibs:
            matrix[index][neib]=1
    return matrix

def get_start(grid:list):
    for i in range(len(grid)):
        try:
            return (i, grid[i].index(1))
        except ValueError:
            pass

def get_end(grid:list):
    for i in range(len(grid)):
        try:
            return (i, grid[i].index(2))
        except ValueError:
            pass

def Dijkstra(N, S, matrix):
    valid = [True]*N
    weight = [1000000]*N
    weight[S] = 0
    for i in range(N):
        min_weight = 1000001
        ID_min_weight = -1
        for i in range(N):
            if valid[i] and weight[i] < min_weight:
                min_weight = weight[i]
                ID_min_weight = i
        for i in range(N):
            if weight[ID_min_weight] + matrix[ID_min_weight][i] < weight[i]:
                weight[i] = weight[ID_min_weight] + matrix[ID_min_weight][i]
        valid[ID_min_weight] = False
    return weight

grid=redef_grid(grid)
if is_valid(grid):
    matrix=get_matrix(grid)
    paths = Dijkstra(len(grid)*len(grid[0]), uni_index(grid, get_start(grid)), matrix)
    shortest_path = paths[uni_index(grid, get_end(grid))]
    if shortest_path==INF: shortest_path='No solution'
    print(shortest_path)
else: print('Invalid grid')

【讨论】:

  • 这太棒了!除了只有一件事,那就是你只能直行或右转,就像在任务中一样。 To help you understand what I mean
  • @CrispyCosmo 从图片上看,你的意思是你不能斜着走,不是吗?
  • 是的,但你不能左转。但是没关系。你已经完成了一项了不起的工作!谢谢!
  • @CrispyCosmo 谢谢。我不知道是否可以将这个细节添加到代码中
【解决方案2】:

这很有趣。蛮力方法计算通过系统的所有可能路径,最后找到以 2 结尾的路径的最小路径长度。请注意,init 只返回一个有效的矩阵来处理。如果没有找到矩阵的解,solve函数返回None。

BLOCK=3
FINAL=2
TERMINATE=None
INITIAL=1
PATH=0

def init(n,m):
  # returns array with essential components, but not necessarily with solution
  import numpy
  import random
  while True:
    try:
      a=numpy.zeros((n,m),dtype=int)
      for i in range(n):
        for j in range(m):
          t=INITIAL
          # initialize to anything but INITIAL
          while t==INITIAL: t=numpy.random.randint(PATH,BLOCK)
          a[i,j]=t
      c=random.choice(list(zip(*(a==PATH).nonzero())))
      a[c]=INITIAL
      finals=list(zip(*(a==FINAL).nonzero()))
      c=random.choice(finals)
      for i in finals:
        if i!=c: a[i]=BLOCK
      break
    except:
      continue
  return a

def possible_moves(a,pos):
  n,m=a.shape
  test_moves=[(0,1),(1,0),(-1,0),(0,-1)]
  #print('from pos={}'.format(pos))
  x,y=pos[0:2]
  valid_moves=[]
  for t in test_moves:
    new=(t[0]+x,t[1]+y)
    if t[0]+x>-1 and \
       t[0]+x<n and \
       t[1]+y>-1 and \
       t[1]+y<m:
      if a[new] not in [BLOCK]:
        valid_moves.append((t[0]+x,t[1]+y))
  return valid_moves

def allpaths(a,paths):
  for path in paths:
    if path[-1] in [TERMINATE,FINAL]: continue
    pos=path[-1]
    moves=possible_moves(a,pos)
    if not moves:
      path.append(TERMINATE) # if no moves left, path is terminated
      continue
    base=path.copy()
    for im,move in enumerate(moves):
      b=a.copy()
      # set previous position to BLOCK so can't move into previous positions
      b[pos]=BLOCK
      if im==0:
        if a[move]==FINAL: path.append(FINAL) # if position to move to is FINAL, indicate
        else: path.append(move)
      else:
        if a[move]==FINAL: paths.append(base+[FINAL])
        else: paths.append(base+[move])
    paths=allpaths(b,paths)
  return paths

def solve(a):
  ipos=list(zip(*(a==INITIAL).nonzero()))
  paths=[ipos]
  paths=allpaths(a,paths)
  M=a.shape[0]*a.shape[1]
  minlen=M
  for path in paths:
    if path[-1] in [FINAL]:
      minlen=min(minlen,len(path)-1)
  if minlen==M: return None
  else: return minlen

n,m=5,6
a=init(n,m)
print(n,m)
print(a)

minlen=solve(a)
print(minlen)

要捕获“正确”,您可以简单地通过 possible_moves 函数跟踪先前的位置,并确保您只返回“直线”和“正确”方向(“向后”在编码时已经被防范)。

【讨论】:

  • 如何编辑代码以便根据输入而不是随机设置块? (输入只是块的数量,然后是每个块的 Xs 和 Ys。)
  • @CrispyCosmo 您可以将 a=init(n,m) 替换为您想要的任何内容,但有几个条件:(1)数组必须是 numpy 数组,因为代码使用 numpy模块为方便起见,(2) 数组必须使用 3 表示阻塞的路径/位置,单个 1 用于初始位置,单个 2 用于最终位置,其他位置为零。我没有像您那样在边缘使用块,因为这些位置被隐式阻止。但是,如果您愿意,您可以使用 3 作为外围设备。例如,将 a=init(n,m) 替换为 "import numpy",后跟 a=numpy.array([[1,3,3],[0,2,3]])
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-06
  • 1970-01-01
  • 2020-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多