最大路径三角形(Python)

时间:2013-11-30 17:45:18

标签: python backtracking brute-force

我有一个有两百行的三角形,我必须找到从三角形的顶部到底部的最大距离。

   5
  9 8
 5 4 6
9 7 3 4

这里,最短距离是5 + 8 + 4 + 3 = 20。最大距离为5 + 9 + 5 + 9 = 28。

我很清楚我想要实现的算法,但我很难将其转化为代码。

我的计划是:从第2行到最后一行开始,从底行添加最大可能路径,然后迭代到顶部。

例如,上面的三角形会变成:

   28
  23 19
 14 11 10
9 7 3 4

这比暴力迫害效率高得多,但我有两个一般性问题:

  1. 使用暴力,如何列出从顶部到的所有可能路径 底部(只能移动到相邻点)?我试过用这个 (三角形是包含三角形的列表列表):

    points=list(itertools.product(*triangle))  
    

    但是这包含了每行的所有可能组合,而不仅仅是 相邻成员。 Project Euler #18 - how to brute force all possible paths in tree-like structure using Python? 这有点解释了一种可能的方法,但我想使用 itertools和任何其他模块(尽可能pythonic)

  2. 我将如何重复添加每个最大值的策略 从前一行并迭代到顶部?我知道我必须这样做 实现一个嵌套循环:

    for x in triangle:
        for i in x:
            i+=? #<-Not sure if this would even increment it
    
    edit:
    what I was thinking was:
    triangle[y][x] = max([triangle[y+1][x],triangle[y+1][x+1]])
    

2 个答案:

答案 0 :(得分:2)

回答你的第一个问题(如何在所有路径上进行暴力迭代):如果你从三角形的顶部开始并沿着一些随机路径向下移动,你必须做出向左或向右移动的决定你失望的水平。因此,不同路径的数量为2^(nrows-1)。对于200行的问题,因此有8e59个不同的路径,这是以蛮力方式检查的方法。

对于一个小三角形,你仍然可以用暴力方式迭代所有可能的路径,例如:

In [10]: from itertools import product
In [11]: triangle = [[5], [9,8], [5,4,6], [9,7,3,4]]
In [12]: for decisions in product((0,1), repeat = len(triangle)-1):
    ...:     pos = 0
    ...:     path = [triangle[0][0]]
    ...:     for lr, row in zip(decisions, triangle[1:]):
    ...:         pos += lr # cumulative sum of left-right decisions
    ...:         path.append(row[pos])
    ...:     print path

[5, 9, 5, 9]
[5, 9, 5, 7]
[5, 9, 4, 7]
[5, 9, 4, 3]
[5, 8, 4, 7]
[5, 8, 4, 3]
[5, 8, 6, 3]
[5, 8, 6, 4]

这样做的方法是使用itertools.product迭代nrows-1左/右决定的所有可能组合,其中0表示向左走,1表示向右走(所以你更多或者少生成所有二进制数的位,直到2^(nrows-1))。如果将三角形存储为列表列表,则向左移动意味着在下一行中保持相同的索引,而向右移动意味着添加1.为了跟踪行中的位置,您只需计算所有的累积总和左/右决定。

回答你的第二个问题:首先,你的算法看起来非常好,你只需要在所有行上向后迭代一次,而你没有像蛮力解决方案那样检查指数数量的情况。我唯一要补充的是建立一个新的三角形,它在每一步都指示最大值是在左侧还是右侧。这对于重建之后的最佳路径很有用。所有这些都可以像这样实现:

mx = triangle[-1] # maximum distances so far, start with last row
directions = [] # upside down triangle with left/right direction towards max
for row in reversed(triangle[:-1]): # iterate from penultimate row backwards
    directions.append([l < r for l, r in zip(mx[:-1], mx[1:])])
    mx = [x + max(l, r) for x, l, r in zip(row, mx[:-1], mx[1:])]
    print 'Maximum so far:', mx
print 'The maximum distance is', mx[0]
directions.reverse()
pos = 0
path = [triangle[0][0]]
for direction, row in zip(directions, triangle[1:]):
    pos += direction[pos]
    path.append(row[pos])
print 'The optimal path is', path

和以前一样,我使用了False = 0True = 1来表示向左和向右的技巧。使用与以前相同的三角形,结果:

Maximum so far: [14, 11, 10]
Maximum so far: [23, 19]
Maximum so far: [28]
The maximum distance is 28
The optimal path is [5, 9, 5, 9]

答案 1 :(得分:2)

它不使用itertools,它是递归的,但我会记住结果,所以它仍然很快......

def memoize(function):
    memo = {}
    def wrapper(*args):
      if args in memo:
        return memo[args]
      else:
        rv = function(*args)
        memo[args] = rv
        return rv
    return wrapper



@memoize
def getmaxofsub(x, y):
    if y  == len(triangle) or x>y: return 0
    #print x, y
    return triangle[y][x] + max(getmaxofsub(x, y+1), getmaxofsub(x+1, y+1))


getmaxofsub(0,0)

我多次阅读你的算法建议,你的“累积三角形”存储在memoized装饰器的memo中,所以最后它非常相似。如果你想在通过三角形递归“向下调用”期间防止存在大堆栈,你可以通过调用getmaxofsub() bottom - &gt;来填充memoize的缓存。起来。

for i in reversed(range(len(triangle))):
    getmaxofsub(0, i), getmaxofsub(i//2, i), getmaxofsub(i, i)

print getmaxofsub(0,0)

修改

getmaxofsub:这个功能如何运作?首先你必须知道,你不能在子三角形中划分三角形。我以你的三角形为例:

   5
  9 8
 5 4 6
9 7 3 4

那是完整的。峰的“坐标”是x = 0,y = 0。

现在我提取峰x = 0,y = 1的子三角形:

  9
 5 4
9 7 3

或x = 1,y = 2

 4
7 3

这就是我的算法的工作原理:整个三角形的峰值(x = 0,y = 0)询问其子三角形(x = 0,y = 1)和(x = 1,y = 1), “你到地面的最大距离是多少?”他们每个人都会问他们的子三角形,等等...... 这将持续到函数到达地面/ y==len(triangle):地面条目想要询问它们的子三角形,但由于它们不是那些,它们得到答案0。 在每个三角形调用它们的子三角形之后,它决定哪一个是更大的三角形,添加它们自己的值并返回这个总和。

现在你看,这个算法的原理是什么。这些算法称为recursive algorithms。你看,一个调用自身的函数非常标准......它可以工作......

所以,如果你考虑整个算法,你会看到很多子三角被调用几次,他们会问他们的子三角形等等...但每次它们返回相同的值。这就是我使用memorize - 装饰器的原因:如果使用相同的参数xy调用函数,装饰器将返回这些参数的最后一个计算值,并防止时间 - 消耗计算......这是一个简单的缓存......

这就是为什么这个函数像递归算法一样容易实现,并且像迭代一样快......