如何改善此递归功能?

时间:2020-05-04 13:17:30

标签: python recursion

如何改进此递归函数,因为它太慢。 此问题来自project euler

问题:

从2×2网格的左上角开始,只能向右和向下移动,到右下角有6条路线。

enter image description here

通过20×20网格有多少条这样的路线?

MAX = 2
paths = 0

def a(x=0,y=0):
    if x==MAX and y==MAX:
        global paths
        paths+=1
        return
    if x>MAX or y>MAX:
        return
    a(x+1,y)
    a(x,y+1)
a()
print(paths)

它一直向下测试直到到达网格中的最后一个单元格。如果它与网格重叠,它将停止并移至调用堆栈中的下一个函数

3 个答案:

答案 0 :(得分:3)

此问题是应用基于备忘录的动态编程原理的理想选择,因为它包含重叠子问题最优子结构。您的递归解决方案需要花费很长时间因为它花了大部分时间一次又一次地解决相同的问题。

使用:

def find_paths(start, end, memo):
    if start == end:
        return 1
    elif start[0] > end[0] or start[1] > end[1]:
        return 0

    r_point, b_point = (start[0] + 1, start[1]), (start[0], start[1] + 1) 
    if not r_point in memo:
        memo[r_point] = find_paths(r_point, end, memo)

    if not b_point in memo:
        memo[b_point] = find_paths(b_point, end, memo)

    return memo[r_point] + memo[b_point]

调用函数:

print(find_paths((0, 0), (2, 2), {}))
print(find_paths((0, 0), (20, 20), {}))
print(find_paths((0, 0), (100, 100), {}))

此打印:

6
137846528820
90548514656103281165404177077484163874504589675413336841320

答案 1 :(得分:1)

这类似于Pascal的三角形。到达网格上的每个点都需要将位置的路径总和从左上至主对角线(帕斯卡的级数),然后向下至目的地。

2x2

Pascal's   Rest
*--1--1    *--1--1
|  |  |    |  |  |
1--2--+    1--2--3
|  |  |    |  |  | 
1--+--+    1--3--6  ==> 6 paths

3x3

Pascal's      Rest
*--1--1--1    *--1--1--1 
|  |  |  |    |  |  |  |
1--2--3--+    1--2--3--4
|  |  |  |    |  |  |  |
1--3--+--+    1--3--6--10
|  |  |  |    |  |  |  |
1--+--+--+    1--4--10-20 ==> 20 paths

4x4

Pascal's       rest        
*--1--1--1--1  *--1--1--1--1
|  |  |  |  |  |  |  |  |  |
1--2--3--4--+  1--2--3--4--5
|  |  |  |  |  |  |  |  |  |
1--3--6--+--+  1--3--6--10-15 
|  |  |  |  |  |  |  |  |  |
1--4--+--+--+  1--4--10-20-35 
|  |  |  |  |  |  |  |  |  |
1--+--+--+--+  1--5--15-35-70 ==> 70 paths

在这一点上,您可以做更多的数学运算,或者可以实现一种有效的算法来计算结果:

N = 4
paths = [1]
for _ in range(N):
    paths = [ a+b for a,b in zip(paths,[0]+paths) ]+[1] # Pascal's
for _ in range(N):
    paths = [ a+b for a,b in zip(paths,paths[1:]) ]     # Rest
result = paths[0]

更多数学:如果将平方扩展到2N,您还将注意到结果恰好是主对角线中间的点。这是Pascal三角形的第2N行的Nth值。

*--1--1--1--1··1··1··1··1  
|  |  |  |  |  :  :  :
1--2--3--4--5··+··+··8·· 
|  |  |  |  |  :  :
1--3--6--10-15·+··28··   
|  |  |  |  |  :
1--4--10-20-35·56·· 
|  |  |  |  |  
1--5--15-35-70··   <-- 70 is combinations of 4 in 8 
:  :  :  :    
1··+··+··56··
:  :  :    
1··+··28··
:  :    
1··8··
:   
1··

根据Pascal三角形的性质,这等于2N集合中N个值的组合数量。

可以用(2N)计算! / N!^ 2:factorial(2*N)//factorial(N)**2

N=2 --> 4!/2!^2 --> 24/4 --> 6

N=3 --> 6!/3!^2 --> 720/36 --> 20

N=4 --> 8!/4!^2 --> 40320/576 --> 70

...

N=20 --> you do the math :)

答案 2 :(得分:-1)

有一个简单的数学解决方案。

我们需要在我们拥有的40个位置中的任意一个位置上精确地向下移动20个位置(因为它总是向下20个位置,向右20个位置)

因此,答案是40选择20 = 137846528820

有关问题here的更多信息