【问题标题】:Are nested for loops always slow?嵌套的 for 循环总是很慢吗?
【发布时间】:2020-09-01 17:29:43
【问题描述】:

似乎有很多与嵌套 for 循环的速度有关的问题和答案 - 我想我已经查看了其中的每一个!但不幸的是,我仍然不确定为什么我的代码很慢。我希望我能从你们这些优秀的人那里得到一些指导。

我每天下载一个包含约 116,000 个条目的 csv 文件。项目在文件中的不一致点被添加和删除,所以我每天都想看看添加了什么,删除了什么。

将条目从 csv 获取到列表完全不需要时间,对于旧列表和新列表,但我在代码的下一部分遇到了很大的速度下降,尽管最后,它做了我想要的并吐出差异 - 添加的项目和删除的项目。

列表中的 116,000 项中的每一项都是这样的字典:

old or new = [{'Date Stamped': '', 'Name': '', 'Registration Number': '', 'Type': '', "Form Name':  '', 'URL': "}]

当我到达这一点时:

added = [i for i in new if not i in old]
removed = [i for i in old if not i in new]

完成需要 25 分钟!我觉得这很长一段时间,但我可能并不完全了解我在做什么。

每个列表(旧的和新的)都有大约 116000 个项目。那是因为我必须迭代约 116,000 个项目 4 次吗?

它最终完成了我想要的,但它正在做的事情似乎非常缓慢;也就是说,这真的是我第一次使用包含这么多项目的数据集,所以也许这是理所当然的。

这是因为它是嵌套的 for 循环,所以速度很慢吗?是因为尺寸慢吗?我绝对是一个业余爱好者,非常感谢大家的帮助。非常感谢。

【问题讨论】:

  • 你的问题是什么?
  • 在字典上使用set
  • old or new = [{}]?
  • in 对于 100,000 多个项目的列表将非常慢。如果您需要进行重复的成员资格测试,您应该使用一个集合(尽管这需要可散列的对象,而 dicts 不是)。
  • 这很慢,因为在[x for x in a if x not in b] 中,代码必须为每个x 遍历b 以检查它是否存在。如果可以的话,最好将b 设为一个集合,因为查找的成本与集合的大小不成比例。最好还是让ab 设置和减去。

标签: python nested-for-loop


【解决方案1】:

实际上,是的,它很慢,因为它是一个嵌套的 for 循环,因为的大小。

Python 的element in list 操作只需逐个元素地搜索整个列表,找到它想要的那个。如果您必须为new 中的每个元素执行此操作,这意味着您可能正在整个old 中搜索new 中的每个元素。

列表不是用于搜索的良好数据结构。相反,如果您有这样的用例,您应该做的是首先将它们转换为set - 一个无序集合(但顺序可能无关紧要),它使用哈希表来确定元素是否存在于它。现在,不是逐个元素地搜索整个数据结构,它可以只对正在搜索的元素进行哈希处理,检查那里是否有元素,如果有,就说出来。

换句话说,element in set 的效率比element in list 高一个数量级。对于相对较小的开销成本(首先创建sets),这会减少for 循环的大量时间:

old_set = set(old)
new_set = set(new)
added = [i for i in new if not i in old_set]
removed = [i for i in old if not i in new]

此外,您甚至可以省去列表推导,因为set 支持集合论中的操作 - 获取两个集合之间的差异(一个集合中的元素不在另一个集合中)就像减去它们一样简单:

added = list(new_set - old_set)  # (new_set - old_set) is identical to new_set.difference(old_set)
removed = list(old_set - new_set)

这可能比列表推导更有效,因为它针对这个用例进行了优化。

【讨论】:

  • 我认为这不仅仅是“一个数量级”。这不是意味着快 10 倍吗?
  • @superbrain 它的复杂性比 n 差(从 O(n)O(1)),这就是我在这种情况下试图暗示的。
  • @GreenCloakGuy 这非常有帮助。我对套装没有太多经验,虽然我在研究这个项目的过程中看到了关于它们的信息,但这个答案也确实帮助我更多地了解了为什么它很慢以及我的选择是什么。非常感谢。
  • @GreenCloakGuy 哇!这非常快!因为 old 和 new 是字典列表,所以我终于弄清楚了如何通过以下方式获取集合中的值: old_set = set() for i in old: old_set.update(i.items()) (在 old_set 和 new_set 上)然后运行您在笔记中提到的设置操作。如此惊人的快!我想从输出中获得更多信息 - 从 dict 转换为 set 时是否可以保留 k:v 对,以便输出更全面?再次感谢。
  • @IrisD A dict 在结构上类似于set - 在内部,它们使用相同的数据结构:哈希表。如果您的数据已经采用dict(而不是list)的形式,那么您可以使用类似的字典理解added = {k:v for k,v in new_dict.items() if k in old_dict}。如果您的数据是一对一的键值对,您还可以反转字典rev_dict = {v:k for k,v in old_dict.items()}。不过,请记住 dicts 不支持 sets 所做的算术运算,因此您需要查看文档以确保您做的是正确的事情。
猜你喜欢
  • 1970-01-01
  • 2017-08-19
  • 1970-01-01
  • 1970-01-01
  • 2014-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多