Python 如何提高(速度、内存使用率)搜索唯一数量的路径以到达对角点的算法?

Python 如何提高(速度、内存使用率)搜索唯一数量的路径以到达对角点的算法?,python,algorithm,Python,Algorithm,我有mxn网格。m>=1;n>=1 我在左上角有一个项目,需要到达网格的右下角 项目只能向下或向右移动 我需要找到可能的独特路径来实现这一点 我为这个问题提出了两种解决方案:递归比下面的慢,下面的慢 问题是,当m和n都很大时,我的内存就用完了,例如m==20和n>=15使用了超过4GB的内存—我所有的可用内存 我如何改进我的解决方案,或者应该有完全其他的方法来解决问题 def唯一路径SM,n: 断言isinstancem,int,m应为整数 断言isinstancen,int,n应为整数 断言m

我有mxn网格。m>=1;n>=1

我在左上角有一个项目,需要到达网格的右下角

项目只能向下或向右移动

我需要找到可能的独特路径来实现这一点

我为这个问题提出了两种解决方案:递归比下面的慢,下面的慢

问题是,当m和n都很大时,我的内存就用完了,例如m==20和n>=15使用了超过4GB的内存—我所有的可用内存

我如何改进我的解决方案,或者应该有完全其他的方法来解决问题

def唯一路径SM,n: 断言isinstancem,int,m应为整数 断言isinstancen,int,n应为整数 断言m>=1,m应>=1 断言n>=1,n应>=1 如果m==1和n==1:边界情况 返回1 ch=[m,n,]表示首次启动 s=0唯一路径的数量 尽管如此: 新的_ch=[] 而ch: i=ch.pop我假设如果减少列表的len,它将减少内存使用 如果i[0]==1和i[1]==1:我们到达了对面的拐角处 s+=1 所有其他情况: elif i[0]!=1和我[1]!=1: 新附录i[0],i[1]-1, 新附录[0]-1,i[1] elif i[0]==1和i[1]!=1: 新附录i[0],i[1]-1, 其他: 新附录i[0]-1,i[1], 戴尔,我不再需要我了 如果不是新的: 返回s 德尔奇 ch=新的 新奥尔良 如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu': 打印唯一路径7,3=28-测试用例 编辑:

解决方案:带记忆的递归非常有效!非常感谢扎比尔·阿尔·纳粹主义

在python lru_缓存装饰器的帮助下:

@lru_缓存128 def路径编号,n: 如果m==1和n==1:边界情况 结果=1 伊里夫m!=1和n!=1: 结果=路径数SM-1,n+路径数SM,n-1 伊里夫m!=1和n==1: 结果=路径数SM-1,n elif m==1和n!=1: 结果=路径数,n-1 其他: 提出例外情况有些事情出错了! 返回结果 借助字典存储结果:

存储={} def路径数,n: 如果storage.getm,n,: 返回存储器[m,n] 如果m==1和n==1:边界情况 结果=1 伊里夫m!=1和n!=1: 结果=路径数,n+路径数,n-1 伊里夫m!=1和n==1: 结果=路径数\u no\u lrum-1,n elif m==1和n!=1: 结果=路径数,n-1 其他: 提出例外情况有些事情出错了! 存储[m,n,]=结果 返回结果 测试:


你的方法的问题是你重复地采取同样的步骤。这是有人应该尝试的第一种暴力手段

对于初学者,您可以尝试增加python的递归限制

import sys
sys.setrecursionlimit(1500)
但是如果你开始增加m,或者n,它就会失败。随着复杂性呈指数级增长

改进的一种方法是将问题分解为更小的部分,并针对更小的部分进行解决,然后将它们合并到最终解决方案中

想一想,你在绿色的位置,想去蓝色的位置。这是主要的解决办法。但是,让我们想象一下红色边界的较小子网格,红色网格的起点在橙色标记处,终点在蓝色,现在让我们以某种神奇的方式说,我们知道红色子网格的解决方案,我们不能合并从绿色到橙色+红色网格部分的解决方案吗

现在,这个递归思想可以通过以下方式实现

def numberOfPaths(m, n): 
    if(m == 1 or n == 1): 
        return 1

    return numberOfPaths(m-1, n) + numberOfPaths(m, n-1)  # traversal in the two possible directions

m = 20
n = 20
print(numberOfPaths(m, n)) 
但复杂性仍然是指数级的,因为程序会反复尝试所有可能的组合来寻找解决方案。如果我们使用一个映射来保存所有的部分解呢?我们可以将解决方案保存为红色子网格,并从地图中使用它,而无需再次遍历它

这个概念被称为动态规划,这是众所周知的。所以,我不想谈任何细节

我们可以创建一个二维数组[m][n],它将被初始化为-1;如果我们从子网格m_1,n_1知道解,我们只返回答案,而不是遍历

这降低了Omxn的复杂性

这已经是一个重大改进

我们也可以把这个问题完全重新发明成一个组合问题

看,有m行,n列,如果你从左上角开始,你可以向右或向下移动,但是你的初始单元格和最终单元格是固定的。那么,你有多少种可能的选择来采取行动呢?M+N-2初始和最终细胞固定SO - 2现在,从所有这些可能的移动,你只能选择N-1,如果我们考虑列,或M-1,如果我们考虑行。因此,解决方案将是m+n- 2Cn-1或m+n-2Cm-1

现在,对于较小的整数,其中m!或者n!不要溢出幸运的是python整数可以轻松处理大值,这可以在线性时间Omaxm,n中完成。因为nCr只能根据阶乘计算


你的方法的问题是你重复地采取同样的步骤。这是有人应该尝试的第一种暴力手段

对于初学者,您可以尝试增加python的递归限制

import sys
sys.setrecursionlimit(1500)
但是如果你开始增加m,或者n,它就会失败。随着复杂性呈指数级增长

改进的一种方法是将问题分解为更小的部分,并针对更小的部分进行解决,然后将它们合并到最终解决方案中

想一想,你在绿色的位置,想去蓝色的位置。这是主要的解决办法。但是,让我们想象一下红色边界的较小子网格,红色网格的起点在橙色标记处,终点在蓝色,现在让我们以某种神奇的方式说,我们知道红色子网格的解决方案,我们不能合并从绿色到橙色+红色网格部分的解决方案吗

现在,这个递归思想可以通过以下方式实现

def numberOfPaths(m, n): 
    if(m == 1 or n == 1): 
        return 1

    return numberOfPaths(m-1, n) + numberOfPaths(m, n-1)  # traversal in the two possible directions

m = 20
n = 20
print(numberOfPaths(m, n)) 
但复杂性仍然是指数级的,因为程序会反复尝试所有可能的组合来寻找解决方案。如果我们使用一个映射来保存所有的部分解呢?我们可以将解决方案保存为红色子网格,并从地图中使用它,而无需再次遍历它

这个概念被称为动态规划,这是众所周知的。所以,我不想谈任何细节

我们可以创建一个二维数组[m][n],它将被初始化为-1;如果我们从子网格m_1,n_1知道解,我们只返回答案,而不是遍历

这降低了Omxn的复杂性

这已经是一个重大改进

我们也可以把这个问题完全重新发明成一个组合问题

看,有m行,n列,如果你从左上角开始,你可以向右或向下移动,但是你的初始单元格和最终单元格是固定的。那么,你有多少种可能的选择来采取行动呢?M+N-2初始和最终细胞固定SO - 2现在,从所有这些可能的移动,你只能选择N-1,如果我们考虑列,或M-1,如果我们考虑行。因此,溶液将为m+n-2Cn-1或m+n-2Cm-1

现在,对于较小的整数,其中m!或者n!不要溢出幸运的是python整数可以轻松处理大值,这可以在线性时间Omaxm,n中完成。因为nCr只能根据阶乘计算


非常感谢你,Zabir,我用递归回忆录解决了这个问题。非常感谢你,Zabir,我用递归回忆录解决了这个问题。