Python 在无回溯的情况下查找从电路板一角到另一角的多条唯一路径
我被一个算法优化所困扰 目标是找出在棋盘上从a点到B点可以使用多少种不同的方式,其中:Python 在无回溯的情况下查找从电路板一角到另一角的多条唯一路径,python,algorithm,optimization,Python,Algorithm,Optimization,我被一个算法优化所困扰 目标是找出在棋盘上从a点到B点可以使用多少种不同的方式,其中: A是左下角的正方形 B是右上角的正方形 每转一圈只能向上或向右转一次 下面是一个虚拟解决方案: # -*- conding: utf-8 -*- import time def solution(n, m, x, y): ret = 0 if x < n-1: ret += solution(n, m, x+1, y) if y < m-1: ret
- A是左下角的正方形
- B是右上角的正方形
- 每转一圈只能向上或向右转一次
# -*- conding: utf-8 -*-
import time
def solution(n, m, x, y):
ret = 0
if x < n-1:
ret += solution(n, m, x+1, y)
if y < m-1:
ret += solution(n, m, x, y+1)
if x == n-1 and y == m-1:
ret = 1
return ret
def wrapper(n, m):
start = time.time()
reponse = solution(n, m, 0, 0)
stop = time.time()
print "Response: %dx%d = %d\nTime : %f\n" % (n, m, reponse, stop-start)
if __name__ == "__main__":
for i in range(10):
wrapper(i+1,i+1)
#wrapper(7,7)
#wrapper(10,10)
#wrapper(100,100)
#wrapper(1000,1000)
#wrapper(10000,10000) <- way too slow
#-*-条件:utf-8-*-
导入时间
def溶液(n、m、x、y):
ret=0
如果x #wrapper(1000010000)这样想:由于A点和B点位于同一位置,您必须移动相同数量的UPs和权限,但顺序将不同。因此,您需要找到不同组合的数量。您不需要算法。只是数学。这里需要考虑的是:当你在右上方的方块上时,你没有任何不同的选择。让我们把它算作零。当你刚好在右上角的右边时,你唯一的选择就是向右走(一个选项),因为你不允许后退。当你刚好在右上角下方时,你唯一的选择就是往上走。让我们把它画出来
... 1 0
..... 1
从目标角向左/向下的角怎么办。从那里到拐角处有两条路(到邻居的各种选择的总和):你可以走到右边:
... 1 0
....2 1
扩展边我们总是使用“一”来扩展:一旦您位于顶部,只有一种方法可以到达右上角:
...1 1 1 0
...... 2 1
........ 1
........=1
但每个非边缘选择都是北部和东部邻国的数字总和:
...1 1 1 0
.....3 2 1
.......3 1
........ 1
等等。希望这能让你开始解决问题
对此也有不同的思考方式。给定一个NxN板,您必须进行2N次移动才能从一个角移动到另一个角。其中N个是北移,N个是东移。问题是:在一个2N长的字符串中,可以有多少不同的N个东移和N个北移组合。您正在寻找帕斯卡三角形。甚至还提到了您的确切问题动态规划:O(N)
它已经提到,问题有一个解决方案,使用a,以及它与的关系。另外,的条目为n×n的情况提供了一个很好的说明
n×n情形
通过使用上述资源,您可以得出结论,对于大小为n×n的网格,您需要计算C(2n-2,n-1)。通过将网格旋转45度并映射Pascal三角形,可以对其进行双重检查
实际上,直接计算这个数字需要以一种简单的方式计算最多3个不同的因子,这是一项非常昂贵的任务。如果你可以预先计算它们,那么这里就没有讨论,你可以说这个问题的复杂性是O(1)。如果你对预先计算的方法不感兴趣,那么你可以继续阅读
您可以使用动态规划(DP)计算这种不祥的数字。这里的诀窍是以较小的步骤执行该操作,这根本不需要计算大的阶乘数
也就是说,要计算C(n,k),你可以先把自己放在C(n,1)上,然后走到C(n,k)。让我们首先用C(n,k-1)来定义C(n,k)
基于此,您可以在Python中定义一个函数来计算C(n,k),如下所示:
def C(n, k):
"""
Calculate C(n, k) using Dynamic Programming.
C(n, k) = C(n, k - 1) * (n - k + 1) / k
"""
C = 1
for ki in range(1, k + 1):
C = C * (n - ki + 1) / ki
return C
它以线性时间O(N)运行
对于n×n的情况,需要计算C(2n-2,n-1)
n×m情形
对于一般的n×m情况,只需要计算C(n+m-2,m-1)
最后但并非最不重要
时间安排
我运行了以下网格大小的算法
N x N | Response's Length | Time
-----------------+-------------------+-----------
1 x 1 | 1 chars | 0.000001
10 x 10 | 5 chars | 0.000004
100 x 100 | 59 chars | 0.000068
1000 x 1000 | 600 chars | 0.002207
10000 x 10000 | 6018 chars | 0.163647
100000 x 100000 | 60203 chars | 40.853971
由于涉及的数据量非常大,因此,在10万x 10万网格大小以上的操作成本似乎高得离谱。不过没什么好惊讶的。作业?您尝试了什么?另外,请确保在记录时间之前,将计时代码的每个部分都运行一次。您需要让您的系统用该代码“预热”,否则您的第一次执行可能会有不正确的度量,而不是作业。我还尝试了一种非递归算法,该算法在第X步用所有可能的解填充数组,并在用B解填充数组时停止,但它消耗了太多的cpu和内存(即使它不会由于太多的递归而导致堆栈溢出)。难道没有O(1)解决方案吗?@BartlomiejLewandowski,不是。这里的阶乘是haskell的有效解决方案,但它会消耗大约15 Go的内存。
>> print "Response: %dx%d = %d" % (n, n, C(2 * n - 2, n - 1),)
Response: 10000x10000 = 5...
>> print "Response: %dx%d = %d" % (n, m, C(n + m - 2, m - 1),)
Response: 10000x10000 = 5...
N x N | Response's Length | Time
-----------------+-------------------+-----------
1 x 1 | 1 chars | 0.000001
10 x 10 | 5 chars | 0.000004
100 x 100 | 59 chars | 0.000068
1000 x 1000 | 600 chars | 0.002207
10000 x 10000 | 6018 chars | 0.163647
100000 x 100000 | 60203 chars | 40.853971