我坚持使用算法优化。
目标是找出在国际象棋棋盘中从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
当我留在小棋盘时,它工作正常,结果是相关的。但我的目标是为10000x10000电路板找到解决方案。
是否有任何想法?
答案 0 :(得分:4)
这样想:由于你的A点和B点在同一个地方,你必须移动相同数量的UP和RIGHT,但顺序会有所不同。所以你需要找到不同组合的数量。
答案 1 :(得分:2)
已经提到使用Triangle of Pascal及其与Binomial coefficient的关系来解决问题。此外,Catalan number的条目对 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)。
C(n, k) = n! / k! * ( n - k )! ; k! = (k - 1)! * k
= n! / (k - 1)! * k * (n - k)! ; (n - k)! = (n - k + 1)! / (n - k + 1)
= n! * (n - k + 1) / (k - 1)! * k * (n - k + 1)! ; C(n, k - 1) = n! / (k - 1)! * ( n - k + 1 )!
= C(n, k - 1) * (n - k + 1) / 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)。
>> print "Response: %dx%d = %d" % (n, n, C(2 * n - 2, n - 1),)
Response: 10000x10000 = 5...
对于一般的 n × m 情况,你只需要计算C(n + m - 2,m - 1)。
>> print "Response: %dx%d = %d" % (n, m, C(n + m - 2, m - 1),)
Response: 10000x10000 = 5...
最后但并非最不重要的是,you can see a live example at Ideone here.
我运行了以下网格尺寸的算法。
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
由于所涉及的数量非常大,看起来网格尺寸为100 000 x 100 000的操作变得非常昂贵。没什么可惊讶的。
答案 2 :(得分:1)
您不需要算法。只是数学。这里有一些需要考虑的事项:当你在右上方时,你没有任何不同的选择。让我们把它算作零。如果您就在右上角的右侧,您唯一的选择是向右(一个选项),因为您不允许回溯。当你在右上角的下方时,你唯一的选择是上升。让我们把它映射出来
... 1 0
..... 1
从目标角落向左/向下的角落怎么样?从那里,有两条通往拐角的路径(到达邻居的选项总和):你可以向右走:
... 1 0
....2 1
扩展边缘我们总是使用1扩展:一旦你位于顶部,只有一种方法可以到达右上角:
...1 1 1 0
...... 2 1
........ 1
........=1
但是每个非边缘选择是北方和东方邻居中数字的总和:
...1 1 1 0
.....3 2 1
.......3 1
........ 1
等等。希望这可以帮助您开始解决方案。
对此也有不同的思考方式。给定NxN板,您必须进行2N移动才能从一个角落到另一个角落。这些移动中的N个是北移,N是东移。问题是:N个东移多少个不同的组合,N个北移可以有一个2N长的字符串。
答案 3 :(得分:0)
您正在寻找Pascal的三角形。 wikipedia link甚至提到了您的确切问题