在 python 中的矩阵上使用统一成本搜索

Using uniform cost search on a matrix in python

美好的一天,我有一个 11x11 矩阵(如下所示),其中 0 代表开放空间,1 代表墙壁。水平和垂直运动的权重为 1,对角线运动的权重为 sqrt(2) 矩阵如下所示:

 `board = [[0,0,0,0,0,0,0,0,0,0,0,0],
           [0,0,0,0,0,1,1,1,0,1,1,0],
           [0,1,0,0,0,0,1,1,0,1,1,0],
           [0,1,1,0,0,0,0,0,0,1,1,0],
           [0,1,1,1,0,0,0,0,0,1,1,0],
           [0,1,1,1,1,0,0,0,0,1,1,0],
           [0,1,1,1,1,1,1,1,1,1,1,0],
           [1,1,1,1,1,1,1,1,1,1,1,0],
           [0,0,0,0,0,0,0,0,0,0,1,0],
           [0,0,0,0,0,0,0,0,0,0,1,0],
           [0,0,1,1,1,1,1,1,1,1,1,0],
           [0,0,0,0,0,0,0,0,0,0,0,0]]`

我的目标是在 python 中编写统一成本搜索代码,以找到从起点(例如 [1,1])到终点(例如 [5,1] 的最具成本效益的路径]).我遇到的大多数代码都适用于图形而不是矩阵。我需要有关使用矩阵解决此问题的帮助。

我是 python 的新手,非常感谢所有帮助和建议。我正在使用 python 3.

由于似乎没有人知道这个问题的简单答案,所以我 post 我的(希望是正确的)答案。使用的方法不是很有效,并且基于像算法一样的洪水填充。

首先我们定义一个包含所有可能方向的列表。这些由 lambda 函数表示,其中 return 新索引(xy)、当前权重和当前路径:

from math import sqrt

dirs = [
    lambda x, y, z, p: (x, y - 1, z + 1, p + [(x, y)]),  # up
    lambda x, y, z, p: (x, y + 1, z + 1, p + [(x, y)]),  # down
    lambda x, y, z, p: (x - 1, y, z + 1, p + [(x, y)]),  # left
    lambda x, y, z, p: (x + 1, y, z + 1, p + [(x, y)]),  # right
    lambda x, y, z, p: (x - 1, y - 1, z + sqrt(2), p + [(x, y)]),  # up left
    lambda x, y, z, p: (x + 1, y - 1, z + sqrt(2), p + [(x, y)]),  # up right
    lambda x, y, z, p: (x - 1, y + 1, z + sqrt(2), p + [(x, y)]),  # down left
    lambda x, y, z, p: (x + 1, y + 1, z + sqrt(2), p + [(x, y)])  # down right
]

然后我们创建一些函数。第一个检查方向计算的索引是否是矩阵的有效索引,并且没有墙。

def valid(grid, x, y):
    return 0 <= x < len(grid) and 0 <= y < len(grid[0]) and grid[x][y] == 0

adjacent 函数为 frontier 处的每个单元格生成每个方向(想象它像一个波浪),flood 函数将波浪向前移动一步并替换旧的踩墙 (1).

def adjacent(grid, frontier):
    for (x, y, z, p) in frontier:
        for d in dirs:
            nx, ny, nz, np = d(x, y, z, p)
            if valid(grid, nx, ny):
                yield (nx, ny, nz, np)

def flood(grid, lst):
    res = list(adjacent(grid, frontier))
    for (x, y, z, p) in frontier:
        grid[x][y] = 1
    return res

在下面的函数中,我们将定义的函数和return称为最短路径权重和最短路径的元组。

def shortest(grid, start, end):
    start, end = tuple(start), tuple(end)
    frontier = [(start[0], start[1], 0, [])]
    res = []
    while frontier and grid[end[0]][end[1]] == 0:
        frontier = flood(grid, frontier)
        for (x, y, z, p) in frontier:
            if (x, y) == end:
                res.append((z, p + [(x, y)]))
    if not res:
        return ()
    return sorted(res)[0]

我对它进行了 (0, 0)(8, 8) 的测试,输出似乎是合理的。如果两个水平/垂直步骤的成本低于相等对角线步骤的成本,它可能会失败。

编辑:(0, 0)(8, 8) 的结果,P 作为路径:

[[P,P,P,P,P,P,P,P,P,P,P,0],
 [0,0,0,0,0,1,1,1,0,1,1,P],
 [0,1,0,0,0,0,1,1,0,1,1,P],
 [0,1,1,0,0,0,0,0,0,1,1,P],
 [0,1,1,1,0,0,0,0,0,1,1,P],
 [0,1,1,1,1,0,0,0,0,1,1,P],
 [0,1,1,1,1,1,1,1,1,1,1,P],
 [1,1,1,1,1,1,1,1,1,1,1,P],
 [0,0,0,P,P,P,P,P,P,P,1,P],
 [0,0,P,0,0,0,0,0,0,0,1,P],
 [0,P,1,1,1,1,1,1,1,1,1,P],
 [0,0,P,P,P,P,P,P,P,P,P,0]]

体重:39.071067811865476

编辑 2:添加复制粘贴版本。

from math import sqrt

board = [[0,0,0,0,0,0,0,0,0,0,0,0],
         [0,0,0,0,0,1,1,1,0,1,1,0],
         [0,1,0,0,0,0,1,1,0,1,1,0],
         [0,1,1,0,0,0,0,0,0,1,1,0],
         [0,1,1,1,0,0,0,0,0,1,1,0],
         [0,1,1,1,1,0,0,0,0,1,1,0],
         [0,1,1,1,1,1,1,1,1,1,1,0],
         [1,1,1,1,1,1,1,1,1,1,1,0],
         [0,0,0,0,0,0,0,0,0,0,1,0],
         [0,0,0,0,0,0,0,0,0,0,1,0],
         [0,0,1,1,1,1,1,1,1,1,1,0],
         [0,0,0,0,0,0,0,0,0,0,0,0]]

dirs = [
    lambda x, y, z, p: (x, y - 1, z + 1, p + [(x, y)]),  # up
    lambda x, y, z, p: (x, y + 1, z + 1, p + [(x, y)]),  # down
    lambda x, y, z, p: (x - 1, y, z + 1, p + [(x, y)]),  # left
    lambda x, y, z, p: (x + 1, y, z + 1, p + [(x, y)]),  # right
    lambda x, y, z, p: (x - 1, y - 1, z + sqrt(2), p + [(x, y)]),  # up left
    lambda x, y, z, p: (x + 1, y - 1, z + sqrt(2), p + [(x, y)]),  # up right
    lambda x, y, z, p: (x - 1, y + 1, z + sqrt(2), p + [(x, y)]),  # down left
    lambda x, y, z, p: (x + 1, y + 1, z + sqrt(2), p + [(x, y)])  # down right
]

def valid(grid, x, y):
    return 0 <= x < len(grid) and 0 <= y < len(grid[0]) and grid[x][y] == 0

def adjacent(grid, frontier):
    for (x, y, z, p) in frontier:
        for d in dirs:
            nx, ny, nz, np = d(x, y, z, p)
            if valid(grid, nx, ny):
                yield (nx, ny, nz, np)

def flood(grid, frontier):
    res = list(adjacent(grid, frontier))
    for (x, y, z, p) in frontier:
        grid[x][y] = 1
    return res

def shortest(grid, start, end):
    start, end = tuple(start), tuple(end)
    frontier = [(start[0], start[1], 0, [])]
    res = []
    while frontier and grid[end[0]][end[1]] == 0:
        frontier = flood(grid, frontier)
        for (x, y, z, p) in frontier:
            if (x, y) == end:
                res.append((z, p + [(x, y)]))
    if not res:
        return ()
    return sorted(res)[0]

print(shortest(board, (0, 0), (8, 8)))