【问题标题】:How to efficiently check if a list is in another list of lists python如何有效地检查一个列表是否在另一个列表列表中python
【发布时间】:2023-03-22 06:02:01
【问题描述】:

我有两个列表(listA,listB),每个列表由许多元组列表组成。

例如

listA = [ [(0,1), (1,2) ... ] , [(5,6), (6,10)] , ... ] # can have 5000 lists, each with 100+ tuples
listB = [...] # about the same structure

我想遍历listA中的每个列表,如果它不在listB中,我将它附加到listB。

所以它是这样的:

for lst in listA:
    if lst not in listB: # membership checking
        listB.append(lst)

我有数十万个这样的任务要执行,当 listA 和 listB 变大时,它似乎真的很慢。成员资格检查似乎是这里的瓶颈。我尝试使用字符串 '0-1' 而不是整数元组,但它并没有变得更快。有谁知道如何优化代码?列表成员检查真的很慢吗?

非常感谢任何帮助。谢谢!

------------- 编辑:这就是我最终使用的 -------------

谢谢各位。将嵌套列表转换为元组并使用集合!但是在遍历 listA 时必须小心,每个嵌套列表也必须转换为元组(但仅用于成员资格检查!)。我仍然需要将嵌套列表作为列表附加到 listB。那就是:

# first convert listB to a set of tuples
listB_as_set = set([tuple(x) for x in listB]) # O(N)

for lst in listA:
    # convert the nested list to tuple
    lst_tuple = tuple(lst)
    # membership checking
    if lst_tuple in listB_as_set: # now O(1), originally O(N)
        listB.append(lst) # still appending as a list to listB

假设两个列表的长度为 N,并且忽略将 lst 转换为 lst_tuple 并将 lst 附加到 listB 的时间,如果我没记错的话,我们得到了从 O(N2)O(N) 的改进。

【问题讨论】:

  • 列表成员为O(n),如果您不关心排序,则考虑将嵌套列表转换为tuple 并使用sets。集合是O(1) 用于成员资格检查。
  • @AChampion listA/listB 中列表的顺序无关紧要,但是对于每个嵌套列表,它必须是 (0,1), (1,2), ... I看。所以我将尝试将嵌套列表转换为元组并查看结果。谢谢!
  • 您只需要转换listB
  • 这是真的!但在循环中,我仍然必须将嵌套列表转换为元组以进行成员资格检查。

标签: python list membership


【解决方案1】:

如果您想存储值以检查它们的存在,sets 会明显更快。 所以你可以试试这个,然后用for循环,会比list快。

listA,listB = set(listA),set(listB)

那是因为set 使用哈希函数映射到存储桶。由于 Python 实现会自动调整该哈希表的大小,因此速度可以保持不变O(1)

Sets 在确定对象 i 是否在集合中时明显更快,但在 迭代 其内容时比 lists 慢。


如果你使用的是嵌套列表,你可以试试

listA = [[(0, 1), (1, 2)], [(5, 6), (6, 10)]]
listA = { tuple(i) for i in listA}

或者

listA = {frozenset(i) for i in listA} 

frozenset 类型是不可变和可散列的,所以

frozenset([(0, 1), (1, 2)]) = frozenset([(1,2),(0,1)])

希望这会有所帮助。

【讨论】:

  • 由于嵌套列表,您不会收到不可哈希的类型错误。
  • @McGrady 谢谢!但是当你做列表理解时,它不是 [] 而不是 {} 吗?此外,如果我只将 listA 中的内容转换为元组,同时循环遍历它,那可能会更好,正如我在帖子中添加的那样。
  • {...} 是一个集合理解。您需要它是 set 才能获得 O(1) 性能。需要对listB 执行此操作。这样做可以让您使用set 操作,例如union.
  • @AChampion 啊,我明白了。我的错。 union 会比使用 for 循环更快吗?
【解决方案2】:

由于列表的性质,您现在的操作方式是 O(N^2) 操作。但是如果你使用集合,那是因为近似 O(n+m) 详见这里:https://wiki.python.org/moin/TimeComplexity

所以方法是

a = set(lista)
b = set(listb)

b.union(lista)

只需三行代码,速度也快得多。 AChampion 提出的关于 uhashable 列表的一个很好的观点。那样的话

a = set([ tuple(x) for x in listA ])

会起作用的。

【讨论】:

  • 由于嵌套列表,您不会收到不可哈希的类型错误。
  • 这个简单明了!在此步骤之前,我还将嵌套列表更改为元组,因此现在速度更快。
  • 很高兴知道。 stackoverflow 的常用形式是接受其中一个答案,而不是编辑您的问题以包含答案中的内容:-)
猜你喜欢
  • 2021-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-24
  • 2023-02-22
  • 2021-01-04
  • 2012-10-08
  • 1970-01-01
相关资源
最近更新 更多