代码之家  ›  专栏  ›  技术社区  ›  Tak

在Python中获取二维数组中单元格的最短路径

  •  7
  • Tak  · 技术社区  · 7 年前

    我有一个2D阵列, arr ,例如,其中每个单元格都有一个值1、2或3, arr[0][0] = 3, arr[2][1] = 2, and arr[0][4] = 1 .

    我想知道从给定的某个单元格出发的最短路径,例如, arr[5][5] 到最近的值为2的单元格,其中路径不应包含任何值为1的单元格。我该怎么做?

    下面是 BFS ,但我如何才能使它接受2D数组作为图形,并将起点作为数组中的某个单元格位置,然后转到距此单元格最近的两个单元格,避免使用带1的单元格,使其看起来像 bfs(2darray, starting location, 2) ?

    def bfs(graph, start, end):
        # Maintain a queue of paths
        queue = []
    
        # Push the first path into the queue
        queue.append([start])
        while queue:
    
            # Get the first path from the queue
            path = queue.pop(0)
    
            # Get the last node from the path
            node = path[-1]
    
            # Path found
            if node == end:
                return path
    
            # Enumerate all adjacent nodes, construct a new path and push it into the queue
            for adjacent in graph.get(node, []):
                new_path = list(path)
                new_path.append(adjacent)
                queue.append(new_path)
    
    print bfs(graph, '1', '11')
    

    Enter image description here

    2 回复  |  直到 4 年前
        1
  •  28
  •   tobias_k    5 年前

    您可以使用 breadth first search 为了这个。基本上,网格中的每个单元格都对应于图中的一个节点,相邻单元格之间有边。从起始位置开始,不断扩展可通过的单元格,直到找到目标单元格。

    def bfs(grid, start):
        queue = collections.deque([[start]])
        seen = set([start])
        while queue:
            path = queue.popleft()
            x, y = path[-1]
            if grid[y][x] == goal:
                return path
            for x2, y2 in ((x+1,y), (x-1,y), (x,y+1), (x,y-1)):
                if 0 <= x2 < width and 0 <= y2 < height and grid[y2][x2] != wall and (x2, y2) not in seen:
                    queue.append(path + [(x2, y2)])
                    seen.add((x2, y2))
    

    网格设置和结果:(请注意,我使用的是符号而不是数字,只是因为这样更容易直观地解析网格并验证解决方案。)

    wall, clear, goal = "#", ".", "*"
    width, height = 10, 5
    grid = ["..........",
            "..*#...##.",
            "..##...#*.",
            ".....###..",
            "......*..."]
    path = bfs(grid, (5, 2))
    # [(5, 2), (4, 2), (4, 3), (4, 4), (5, 4), (6, 4)]
    
        2
  •  3
  •   Peter Mortensen Len Greski    4 年前

    如果列表不太大,我发现最简单的解决方案是使用 where NumPy库的函数,用于查找具有所需值的单元格。因此,您需要将列表转换为NumPy数组。

    下面的代码可能会被简化,以使其更短、更高效,但通过这种方式,它会更清晰。顺便说一下,您可以计算两种距离:典型的欧几里德距离和 Manhattan .

    如果在与原始单元格相同的距离处有多个目标单元格, 最小坐标 对应于找到的第一个单元格(先按行,然后按列)。

    import numpy as np
    
    # The list needs to be transformed into an array in order to use the np.where method
    # arr = np.random.randint(5, size=(6, 6))
    arr = np.array([[0, 0, 0, 1, 1, 3],
                    [0, 0, 2, 1, 1, 0],
                    [0, 0, 1, 1, 1, 1],
                    [3, 0, 3, 1, 1, 1], ])
    
    # Origin cell to make the search
    x0, y0 = (1, 1)
    targetValue = 3
    
    # This is the keypoint of the problem: find the positions of the cells containing the searched value
    positions = np.where(arr == targetValue)
    x, y = positions
    
    dx = abs(x0 - x)  # Horizontal distance
    dy = abs(y0 - y)  # Vertical distance
    
    # There are different criteria to compute distances
    euclidean_distance = np.sqrt(dx ** 2 + dy ** 2)
    manhattan_distance = abs(dx + dy)
    my_distance = euclidean_distance  # Criterion choice
    min_dist = min(my_distance)
    print(min_dist)
    
    min_pos = np.argmin(my_distance)  # This method will only return the first occurrence (!)
    min_coords = x[min_pos], y[min_pos]
    print(min_coords)