【问题标题】:Trying to understand how this code I found for triangle max path sum in python works试图了解我在 python 中为三角形最大路径总和找到的这段代码是如何工作的
【发布时间】:2021-02-14 15:13:02
【问题描述】:

所以采用一个嵌套列表格式的三角形。 例如

t =  [[5],[3, 6],[8, 14, 7],[4, 9, 2, 0],[9, 11, 5, 2, 9],[1, 3, 8, 5, 3, 2]]

并将路径定义为三角形每一行的元素之和, 向下行时向左或向右移动 1。或者在python中 第二个索引要么保持不变,要么我们给它加 1。

a_path = [t[0][0],[t[1][1]],t[2][1],t[3][1],t[4][2],t[5][3]] = [5, 6, 14, 9, 5,5] is valid
not_a_path = [t[0][0],[t[1][0]],t[2][2],t[3][1],t[4][0],t[5][4]] = [5, 3, 7, 9, 9, 3] is not valid

对于像这个例子这么小的三角形,这显然可以通过蛮力来完成。 我写了一个这样的函数,对于一个 20 行的三角形,它需要大约 1 分钟。 我需要一个可以为 100 行三角形执行此操作的函数。 我在https://rosettacode.org/wiki/Maximum_triangle_path_sum#zkl 上找到了这段代码,它与我尝试过的小三角形的可怕函数输出的所有结果一致,并且在控制台中使用 %time 它可以在 0 ns 内相对较快地完成 100 线三角形。

def maxPathSum(rows):
    return reduce(
        lambda xs, ys: [
            a + max(b, c) for (a, b, c) in zip(ys, xs, xs[1:])
        ],
        reversed(rows[:-1]), rows[-1]
    )

所以我开始对此进行分析,并使用打印语句和控制台来确定它在做什么。我知道reversed(rows[:-1]), rows[-1] 正在反转三​​角形,以便我们可以从最后一行的所有可能的最终值通过它们可能的路径的总和进行迭代以获得该值,并且作为 a,b,c 迭代:a 是底行的数字,b 是底行的第二个,c 是底行的第三个。当他们迭代时,我认为a + max(b,c) 似乎将 a 与 b 或 c 上的最大数相加,但是当我尝试在控制台中找到两个列表或嵌套列表的最大值时,返回的列表似乎完全是任意的。

ys = t[-1]
xs = list(reversed(t[:-1]))
for (a, b, c) in zip(ys, xs, xs[1:]):
    print(b)
    print(c)
    print(max(b,c))
    print("")

打印

[9, 11, 5, 2, 9]
[4, 9, 2, 0]
[9, 11, 5, 2, 9]

[4, 9, 2, 0]
[8, 14, 7]
[8, 14, 7]

[8, 14, 7]
[3, 6]
[8, 14, 7]

[3, 6]
[5]
[5]

如果 max(b,c) 返回包含 max(max(b),max(c)) 的列表,则 b = [3, 6], c = [5] 将返回 b,所以不是这样。如果 max(b,c) 返回总和最大的列表,max(sum(b),sum(c)),那么同一个示例与之矛盾。它不会返回包含最小值或均值最大的列表,所以我唯一的猜测是我设置 xs = list(reversed(t[:-1])) 的事实是问题所在,如果它是 lambda 函数内的迭代器但不是在控制台中。

还试图找到 a + max (b,c) 给我这个错误,这是有道理的。

TypeError: unsupported operand type(s) for +: 'int' and 'list'

我最好的猜测再次是 xs 作为列表的不同定义是问题所在。如果是真的,我想知道这一切在 lambda 函数中作为迭代器的上下文中是如何工作的。我想我明白 reduce() 和 zip() 正在做什么,所以大多数情况下只是 lambda 函数让我感到困惑。

提前感谢您的帮助

【问题讨论】:

  • 您可能应该先了解这是手动完成的方式,然后再尝试了解这个不太可读的代码是如何工作的。这是一个解释它的视频:youtube.com/watch?app=desktop&v=N4TYj0Dn0ZQ
  • @Thierry Lathuille 我很确定我理解这是要做什么的想法,但是我观看了视频,它确实使我的想法更加清晰,主要是看到了视觉示例。但我仍然不明白代码是如何在最挥手的细节上遵循相同的算法的。谢谢你的视频

标签: python lambda


【解决方案1】:

我们可以通过在第二个参数中包含所有行来简化表达式以减少 - 没有理由将最后一行作为 reduce 的第三个参数(起始值)传递。

然后,给你的变量起有意义的名字真的很有帮助,而原来的代码很糟糕。

所以,这就变成了:

from functools import reduce

def maxPathSum(rows):
    return reduce(
        lambda sums, upper_row: [cell + max(sum_left, sum_right) 
                                 for (cell, sum_left, sum_right) 
                                 in zip(upper_row, sums, sums[1:])],
        reversed(rows)
    )

在第一次迭代中,sums 将是最后一行,upper_row 在它上面。

lambda 将通过将具有最大值 sums 的上一行的每个值添加到其左侧或右侧来计算可能的最佳总和。

它用总和压缩上一行(最后一个总和不会被使用,因为有一个太多),总和移动一个值。因此, zip 将为我们提供一个三元组(上排的值 (cell),左下方的总和 (sum_left),右下方的总和 (sum_right)。此时最好的总和是我们当前的单元格 + 最大的这些总和。

lambda 返回这一新行和,它将在下一次迭代中用作reduce (sums) 的第一个参数,而upper_row 成为reversed(rows) 中的下一行。

最后,reduce 返回最后一行总和,其中仅包含一个值,即我们最好的总和:

[53]

【讨论】:

  • 有道理,谢谢
【解决方案2】:

您可以拼出 lambda 函数,以便它可以打印。这有助于你理解吗?

t =  [[5],[3, 6],[8, 14, 7],[4, 9, 2, 0],[9, 11, 5, 2, 9],[1, 3, 8, 5, 3, 2]]
def g( xs, ys):
    ans=[a + max(b, c) for (a, b, c) in zip(ys, xs, xs[1:])]
    print(ans)
    return ans
def maxPathSum(rows):
    return reduce(
        g,
        reversed(rows[:-1]), rows[-1]
    )
maxPathSum(t)

【讨论】:

  • 我可以看到算法在 g 的 print 语句中运行,所以我现在可以清楚地看到代码在做什么。但我不明白a + max(b,c) 是如何做到的。 a 是底行的整数,这似乎考虑了第 2 行和第 3 行的最大值与 a 的总和。该算法的工作原理是从底部的第二行确定其下方两个选项的最大总和,减少行直到找到最大总和。我看不出a + max(b,c) 是如何进行这种迭代的。不过,使用另一个函数替换 lambda 很有帮助,谢谢。
猜你喜欢
  • 1970-01-01
  • 2021-07-03
  • 2016-05-01
  • 2019-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多