【问题标题】:Analyzing the complexity matrix path-finding分析复杂度矩阵寻路
【发布时间】:2021-04-18 09:24:21
【问题描述】:

最近在我的作业中,我被分配解决以下问题:

给定一个由 0 和 1 组成的 nxn 阶矩阵,找出从 [0,0] 到 [n-1,n-1] 仅经过零(它们不一定不相交)的路径数只能向下或向右走,从不向上或向左。返回一个同阶矩阵,其中 [i,j] 项是原始矩阵中经过 [i,j] 的路径数,解必须是递归的。

我在 python 中的解决方案:

def find_zero_paths(M):
    n,m = len(M),len(M[0])
    dict = {}
    for i in range(n):
        for j in range(m):
            M_top,M_bot = blocks(M,i,j)
            X,Y = find_num_paths(M_top),find_num_paths(M_bot)
            dict[(i,j)] = X*Y
    L = [[dict[(i,j)] for j in range(m)] for i in range(n)]
    return L[0][0],L

def blocks(M,k,l):
    n,m = len(M),len(M[0])
    assert k<n and l<m
    M_top = [[M[i][j] for i in range(k+1)] for j in range(l+1)]
    M_bot = [[M[i][j] for i in range(k,n)] for j in range(l,m)]
    return [M_top,M_bot]

def find_num_paths(M):
    dict = {(1, 1): 1}
    X = find_num_mem(M, dict)
    return X

def find_num_mem(M,dict):
    n, m = len(M), len(M[0])
    if M[n-1][m-1] != 0:
        return 0
    elif (n,m) in dict:
        return dict[(n,m)]
    elif n == 1 and m > 1:
        new_M = [M[0][:m-1]]
        X = find_num_mem(new_M,dict)
        dict[(n,m-1)] = X
        return X
    elif m == 1 and n>1:
        new_M = M[:n-1]
        X = find_num_mem(new_M, dict)
        dict[(n-1,m)] = X
        return X
    new_M1 = M[:n-1]
    new_M2 = [M[i][:m-1] for i in range(n)]
    X,Y = find_num_mem(new_M1, dict),find_num_mem(new_M2, dict)
    dict[(n-1,m)],dict[(n,m-1)] = X,Y
    return X+Y

我的代码基于这样的想法,即在原始矩阵中通过 [i,j] 的路径数等于从 [0,0] 到 [i,j] 的路径数的乘积,并且从 [i,j] 到 [n-1,n-1] 的路径数。另一个想法是从 [0,0] 到 [i,j] 的路径数是从 [0,0] 到 [i-1,j] 和从 [0,0] 到[i,j-1]。因此我决定使用一个字典,其键是 [[M[i][j] for j in range(k)] for i in range(l)] 或 [[M[i][j] for j in range(k+1,n)] for i in range(l+1,n)] for some 0

现在,我的导师说这段代码是指数型的(对于 find_zero_paths),但我不同意。 递归树(对于 find_num_paths)的大小受上述形式的子矩阵数量的限制,即 O(n^2)。此外,每次我们在字典中添加一个新矩阵时,我们都会在多项式时间内完成(仅切片列表),所以......总复杂度是多项式(poly*poly = poly)。此外,函数“blocks”在多项式时间内运行,因此“find_zero_paths”在多项式时间内运行(2 个多项式大小列表乘以在多项式时间内运行的函数),因此所有代码都在多项式时间内运行。

我的问题:代码多项式和我的 O(n^6) 界限是错误的还是指数级的,我错过了什么?

【问题讨论】:

  • 如果有一个零循环,路径的数量将是无限的。请问这有什么办法吗? (例如,通过指定没有循环或仅寻找最短路径)此外,听说您需要使用递归有点痛苦。这有什么原因吗?
  • 我忘了说你只能往下走或往右走,不能往回走或往上走,因此没有循环,修复它。另外,递归的原因只是因为作业的主题是递归。

标签: python algorithm time-complexity path-finding


【解决方案1】:

很遗憾,你的导师是对的。

这里有很多东西要解压:

在开始之前,请注意。请不要使用 dict 作为变量名。好痛^^。 Dict 是 python 中字典构造函数的保留关键字。用您的变量覆盖它是一种不好的做法。

首先,如果您只计算矩阵中的一个单元格,那么您计算 M_top * M_bottom 的方法很好。在你做这件事的方式上,你一遍又一遍地不必要地计算一些块——这就是我思考递归的原因,我会为此使用动态编程。从头到尾一次,从头到尾一次,然后我会去计算产品并完成它。不需要 O(n^6) 的单独计算。既然你必须使用递归,我建议缓存部分结果并尽可能重用它们。

第二,问题的根源和隐形指数的原因。它隐藏在 find_num_mem 函数中。假设您计算矩阵中的最后一个元素 - result[N][N] 字段,让我们考虑最简单的情况,矩阵全为零,因此存在所有可能的路径。

  1. 在第一步中,您的递归创建分支 [N][N-1] 和 [N-1][N]。
  2. 第二步,[N-1][N-1],[N][N-2],[N-2][N],[N-1][N-1]
  3. 在第三步中,您再次从上一步创建两个分支 - 一个指数爆炸的漂亮示例。

现在该怎么做:你会很快注意到一些分支被一遍又一遍地复制。 缓存结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-18
    • 1970-01-01
    • 1970-01-01
    • 2021-02-16
    • 1970-01-01
    • 2015-08-29
    相关资源
    最近更新 更多