【问题标题】:for/in/if List Comprehension Becomes Very Slow With Large Number of Matchesfor/in/if 列表理解因大量匹配而变得非常缓慢
【发布时间】:2016-09-19 12:22:20
【问题描述】:

我的 Python 2.7 代码中有以下列表理解,它返回行号(索引)和一长串行中的行:

results = [[lines.index(line), line] for line in lines
            if search_item in line.lower()]

如果结果数量很少,这会很快:

The search item is: [ 1330 ]
Before string pre-processing, the time is: 0.0000
The number of lines is: 1,028,952
After string pre-processing, the time is: 0.2500
The number of results is: 249

“字符串预处理”就是我所说的结果=上面的操作。

这里是相同的操作,但搜索项是“1330”而不是“1330”。这会产生 6,049 个匹配项而不是 249 个:

The search item is: [1330]
Before string pre-processing, the time is: 0.0000
The number of lines is: 1,028,952
After string pre-processing, the time is: 10.3180
The number of results is: 6,049

如您所见,10 秒与 1/4 秒...此外,“1330”和“1330”搜索使用 for 循环分别在 2.4 秒和 3.2 秒内运行:

for lineNum, line in enumerate(lines):
    if search_item in line.lower():
        return lineNum, line

因此,列表理解在 249 个结果的情况下将性能提高了 10 倍,但在 6,049 个结果的情况下却慢了 3+x...

显然,问题不在于列表理解的 if/in 部分(两个搜索都扫描所有 1M+ 行并接受或拒绝每一行),而在于构建第二个“长”的结果列表案子。换句话说,瓶颈似乎在

results = [lines.index(line), line]

理解的一部分。

我想我很惊讶列表理解对于大型结果集变得如此缓慢(而且 6K 真的没有那么大)。我错过了什么?有没有我应该使用的其他方法可以始终胜过 for 循环?

【问题讨论】:

  • 您似乎已经知道enumerate()。为什么不在列表推导中使用它?
  • 不明白 list.index() 的开销。另外,对于 Python 来说还是很新的,尤其是列表理解。但是,现在我在我的应用程序中有一个超快速的搜索。非常兴奋!

标签: python performance list list-comprehension


【解决方案1】:

list.index() 调用必须搜索所有行才能找到匹配项。对于 N 行,您执行 O(N^2) 步骤; 1000 行变为 100 万步,以此类推。对于 6k 行,即 3600 万步 *

如果您只需要一个行号,请使用enumerate() function 生成一个:

results = [[index, line] for index, line in enumerate(lines)
            if search_item in line.lower()]

enumerate() 会在执行过程中添加一个运行计数器,让您的算法只执行 O(N) 步。您已经在完整的 for 循环语句中使用了它,但不在您的列表理解中

如果您有重复行,输出会有所不同; lines.index() 找到 第一个 匹配项,而 enumerate() 产生唯一的行号。


*Big-O notation 为我们提供算法的渐近行为。由于list.index() 对于给定的行 x 只需要扫描(最多)x 行来查找索引,如果你对迭代的每一行都这样做,您总共只需要 1 + 2 + 3 + ... x 步,即triangle number。所以总共“只”采取了 ((N * (N + 1)) / 2) 步,大约是 1/2 N^2 步。但是当 N 趋于无穷大时,乘数不再重要,最终得到 O(N^2)。

【讨论】:

  • 是的,但我们什么时候才能真正使用 big-O 教授?
  • @JeremyWest:一直都是。因为这里很重要。
  • 同意。那是个笑话。
  • Martijn,谢谢!现在在 0.3 秒内运行 6K 结果!非常感谢您的帮助!
  • @JeremyWest:很抱歉杀了它!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-22
  • 2015-03-24
  • 2018-01-22
  • 1970-01-01
  • 2022-06-10
相关资源
最近更新 更多