在 Python 中测试列表是否共享任何项目

2025-02-07 08:44:00
admin
原创
90
摘要:问题描述:我想检查一个列表中的任何项目是否存在于另一个列表中。我可以用下面的代码简单地做到这一点,但我怀疑可能有一个库函数可以做到这一点。如果没有,是否有更符合 Python 风格的方法来达到相同的结果。In [78]: a = [1, 2, 3, 4, 5] In [79]: b = [8, 7, 6] ...

问题描述:

我想检查一个列表中的任何项目是否存在于另一个列表中。我可以用下面的代码简单地做到这一点,但我怀疑可能有一个库函数可以做到这一点。如果没有,是否有更符合 Python 风格的方法来达到相同的结果。

In [78]: a = [1, 2, 3, 4, 5]

In [79]: b = [8, 7, 6]

In [80]: c = [8, 7, 6, 5]

In [81]: def lists_overlap(a, b):
   ....:     for i in a:
   ....:         if i in b:
   ....:             return True
   ....:     return False
   ....: 

In [82]: lists_overlap(a, b)
Out[82]: False

In [83]: lists_overlap(a, c)
Out[83]: True

In [84]: def lists_overlap2(a, b):
   ....:     return len(set(a).intersection(set(b))) > 0
   ....: 

解决方案 1:

简短的回答:使用not set(a).isdisjoint(b),它通常是最快的。

有四种常用方法来测试两个列表是否共享任何项目。第一种选择是将两者转换为集合ab检查它们的交集,如下所示:

bool(set(a) & set(b))

因为集合在 Python 中是使用哈希表存储的,所以搜索它们是O(1)(有关 Python 中运算符复杂性的更多信息,请参阅此处)。从理论上讲,对于列表和中的和对象,这是O(n+m)平均水平。但是n`mab`

  1. 它必须首先从列表中创建集合,这可能需要花费大量时间,并且

  2. 它假设您的数据中的散列冲突是稀疏的。

第二种方法是使用生成器表达式对列表执行迭代,例如:

any(i in a for i in b)

这允许就地搜索,因此不会为中间变量分配新内存。它还会在第一次找到时退出。in运算符始终O(n)在列表中(参见此处)。

另一个建议的选项是混合迭代其中一个列表,将另一个列表转换为集合并测试该集合的成员身份,如下所示:

a = set(a); any(i in a for i in b)

第四种方法是利用isdisjoint()(冻结)集合的方法(参见此处),例如:

not set(a).isdisjoint(b)

如果搜索的元素靠近数组的开头(例如,它已排序),则生成器表达式是首选,因为集合交集方法必须为中间变量分配新内存:

from timeit import timeit
>>> timeit('bool(set(a) & set(b))', setup="a=list(range(1000));b=list(range(1000))", number=100000)
26.077727576019242
>>> timeit('any(i in a for i in b)', setup="a=list(range(1000));b=list(range(1000))", number=100000)
0.16220548999262974

以下是此示例的执行时间与列表大小的关系图:

开始时共享元素测试执行时间

请注意,两个轴都是对数的。这代表了生成器表达式的最佳情况。可以看出,该isdisjoint()方法对于非常小的列表大小更好,而生成器表达式对于较大的列表大小更好。

另一方面,由于搜索从混合和生成器表达式的开头开始,如果共享元素系统地位于数组的末尾(或两个列表不共享任何值),则不相交和集合交集方法比生成器表达式和混合方法快得多。

>>> timeit('any(i in a for i in b)', setup="a=list(range(1000));b=[x+998 for x in range(999,0,-1)]", number=1000))
13.739536046981812
>>> timeit('bool(set(a) & set(b))', setup="a=list(range(1000));b=[x+998 for x in range(999,0,-1)]", number=1000))
0.08102107048034668

最后共享时的元素共享测试执行时间

值得注意的是,对于较大的列表大小,生成器表达式的速度要慢得多。这仅适用于 1000 次重复,而不是上图中的 100000 次。当没有共享元素时,此设置也近似得很好,并且是分离和集合交集方法的最佳情况。

以下是使用随机数的两项分析(而不是操纵设置以支持一种或另一种技术):

具有高共享概率的随机生成数据的元素共享测试执行时间
具有高共享概率的随机生成数据的元素共享测试执行时间

共享概率高:元素从 中随机获取[1, 2*len(a)]。 共享概率低:元素从 中随机获取[1, 1000*len(a)]

到目前为止,此分析假设两个列表的大小相同。如果两个列表的大小不同,例如a,一个列表小得多,isdisjoint()则总是更快:

在开始时共享元素时,两个不同大小的列表上的元素共享测试执行时间
在最后共享时,两个不同大小的列表上的元素共享测试执行时间

确保a列表较小,否则性能会下降。在本实验中,a列表大小设置为常数5

总之:

  • 如果列表非常小(<10 个元素),not set(a).isdisjoint(b)则总是最快的。

  • 如果列表中的元素已排序或具有您可以利用的规则结构,则生成器表达式any(i in a for i in b)在大型列表大小上是最快的;

  • 用 测试集合的交集not set(a).isdisjoint(b),它总是比 快bool(set(a) & set(b))

  • 混合“遍历列表,在集合上测试”a = set(a); any(i in a for i in b)通常比其他方法慢。

  • 当涉及没有共享元素的列表时,生成器表达式和混合比其他两种方法慢得多。

在大多数情况下,使用isdisjoint()方法是最好的方法,因为生成器表达式将需要更长的时间来执行,因为当没有共享元素时它非常低效。

解决方案 2:

def lists_overlap3(a, b):
    return bool(set(a) & set(b))

注意:以上假设您想要一个布尔值作为答案。如果您只需要一个在语句中使用的表达式if,只需使用if set(a) & set(b):

解决方案 3:

def lists_overlap(a, b):
  sb = set(b)
  return any(el in sb for el in a)

any这是渐近最优的(最坏情况 O(n + m)),并且由于的短路,可能比交叉方法更好。

例如:

lists_overlap([3,4,5], [1,2,3])

一旦到达就会返回 True3 in sb

编辑:另一种变化(感谢 Dave Kirby):

def lists_overlap(a, b):
  sb = set(b)
  return any(itertools.imap(sb.__contains__, a))

这依赖于imap的迭代器,它是用 C 实现的,而不是生成器理解。它还用作sb.__contains__映射函数。我不知道这会带来多大的性能差异。它仍然会短路。

解决方案 4:

您还可以使用any列表理解:

any([item in a for item in b])

解决方案 5:

在 Python 2.6 或更高版本中你可以执行以下操作:

return not frozenset(a).isdisjoint(frozenset(b))

解决方案 6:

您可以使用任何内置函数 /wa 生成器表达式:

def list_overlap(a,b): 
     return any(i for i in a if i in b)

正如 John 和 Lie 指出的那样,当两个列表共享的每个 i bool(i) == False 时,这会产生不正确的结果。应该是:

return any(i in b for i in a)

解决方案 7:

这个问题相当老了,但我注意到,当人们争论集合与列表时,没有人想到将它们一起使用。按照 Soravux 的例子,

列表的最坏情况:

>>> timeit('bool(set(a) & set(b))',  setup="a=list(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
100.91506409645081
>>> timeit('any(i in a for i in b)', setup="a=list(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
19.746716022491455
>>> timeit('any(i in a for i in b)', setup="a= set(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
0.092626094818115234

列表的最佳情况是:

>>> timeit('bool(set(a) & set(b))',  setup="a=list(range(10000)); b=list(range(10000))", number=100000)
154.69790101051331
>>> timeit('any(i in a for i in b)', setup="a=list(range(10000)); b=list(range(10000))", number=100000)
0.082653045654296875
>>> timeit('any(i in a for i in b)', setup="a= set(range(10000)); b=list(range(10000))", number=100000)
0.08434605598449707

因此,比遍历两个列表更快的是通过遍历一个列表来查看它是否在一个集合中,这是有道理的,因为检查一个数字是否在一个集合中需要恒定的时间,而通过遍历列表来检查所需的时间与列表的长度成比例。

因此,我的结论是遍历列表,并检查它是否在集合中

解决方案 8:

如果您不关心重叠元素是什么,您可以简单地检查len组合列表与组合为一个集合的列表。如果有重叠元素,则集合会更短:

len(set(a+b+c))==len(a+b+c) 如果没有重叠,则返回 True。

解决方案 9:

我将再举一个具有函数式编程风格的事例:

any(map(lambda x: x in a, b))

解释:

map(lambda x: x in a, b)

b返回在中找到元素的布尔列表a。然后将该列表传递给,如果有任何元素 ,any则简单地返回。 True`True`

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2560  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1552  
  IPD(Integrated Product Development)流程作为一种先进的产品开发管理模式,在众多企业中得到了广泛应用。其中,技术评审与决策评审是IPD流程中至关重要的环节,它们既有明显的区别,又存在紧密的协同关系。深入理解这两者的区别与协同,对于企业有效实施IPD流程,提升产品开发效率与质量具有重要意义...
IPD管理流程   1  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、ClickUp、Freshdesk、GanttPRO、Planview、Smartsheet、Asana、Nifty、HubPlanner、Teamwork。在当今快速变化的商业环境中,项目管理软件已成为企业提升效率、优化资源分配和确保项目按时交付的关键工具。然而...
项目管理系统   2  
  建设工程项目质量关乎社会公众的生命财产安全,也影响着企业的声誉和可持续发展。高质量的建设工程不仅能为使用者提供舒适、安全的环境,还能提升城市形象,推动经济的健康发展。在实际的项目操作中,诸多因素会对工程质量产生影响,从规划设计到施工建设,再到后期的验收维护,每一个环节都至关重要。因此,探寻并运用有效的方法来提升建设工程...
工程项目管理制度   3  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用