【问题标题】:More efficient ways to find the number of matching values shared by two iterables?查找两个可迭代对象共享的匹配值数量的更有效方法?
【发布时间】:2020-02-24 03:09:39
【问题描述】:

编辑:寻找匹配的数量而不是匹配本身。无法用集合或[x for x in list1 if x in list2] 类型的方式解决。 list1.count(x) if x in list2 虽然有效。

假设您有两个列表,list1 和 list2,并且想要查找 list1 中的值与 list2 中的值匹配的次数。

我使用下面的代码成功地执行了这个任务 -

sum([x==y for x in list1 for y in list2])

问题是这段代码不能有效地处理更大的列表。有没有比“double for”循环更快、更高效、更pythonic的方式来解决这个问题?

【问题讨论】:

  • 查看设置交集
  • 只要list1list2 都没有重复元素,len(set(list1) & set(list2)) 就可以工作
  • list 有一个 count 方法,它比您的 0/1 循环更有效,但您的查询仍然是 O(n^2)。所以sum(list1.count(x) for x in list2) 会更快,但扩展性仍然很差。
  • @slurrr 你的预期结果是 num_dups([1,1], [1, 1]) 2 还是 4?
  • @AdamVanProoyen 4

标签: python arrays list sorting matching


【解决方案1】:

计数器支持与& 运算符的多集交集:

>>> from collections import Counter
>>> list1 = list("abba")   
>>> list2 = list("bbanana") 
>>> c1 = Counter(list1)
>>> c2 = Counter(list2)
>>> sum(c1[k]*c2[k] for k in c1 & c2)  # O(n)
10
>>> sum([x==y for x in list1 for y in list2])  # O(n**2)
10

【讨论】:

    【解决方案2】:

    我们可以使用 Python 标准库中的Counter

    计数器计算在迭代中找到项目的次数。从列表中构造它本质上会产生从列表中的每个项目到出现次数的映射。

    在两个计数器上执行集合交集将为我们提供在两个列表中找到的项目的计数。但是,我们不是查找重复项的数量,而是查找一个元素与另一个元素匹配的次数。这意味着我们需要使用乘法而不是 min 来设置交集。

    from collections import Counter
    
    def merge(d1, d2):
      return {k: (d1[k], d2[k]) for k in d1 if k in d2}
    
    def num_dups(l1, l2):
      c1, c2 = Counter(l1), Counter(l2)
      dups = merge(c1, c2)
      return sum(x * y for x, y in dups.values())
    

    【讨论】:

    • @wim 是的,你是对的。我会尝试用 slurrr 确认预期的行为是什么。这个方法的思路应该差不多(用乘法设置交集,而不是用min作为join算子)
    • 是的,这是一个很棒的功能,但不会多次计算 dups 来给你总数
    • @AdamVanProoyen 看到 wim 的回答
    【解决方案3】:

    这种方法与其他方法完全不同,可能对您的要求过于简单 - 但我想我会把它混在一起。

    它解决了这个请求:

    • 假设您有两个列表,列表 1 和列表 2,并且想要查找列表 1 中的值与列表 2 中的值匹配的次数。

    怎么样:

    a = ['a', 'b', 'c', 'd', 'e']
    b = ['a', 'a', 'c', 'c', 'c']
    
    [b.count(i) for i in a]
    

    输出:

    [2, 0, 3, 0, 0]
    

    【讨论】:

    • 这确实有效,并且是我让我的代码在更大的列表上工作的第一种方式。不知道为什么投反对票我希望有人能解释这段代码的糟糕之处,除了它不是最有效的。
    • @slurrr - 谢谢。我喜欢它,因为它简单且有效。我也想对反对票做出解释。不过还是谢谢。
    • 这仍然是 O(n**2)-ish - b.count(i) 完全针对 a 中的每个元素迭代 b。我不是你的反对者。
    • 您可能想要对此进行基准测试——它的可扩展性比 Counter 解决方案差,但对于小型列表可能要快得多。知道盈亏平衡点在哪里会很有用。
    猜你喜欢
    • 2021-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-02
    • 1970-01-01
    • 1970-01-01
    • 2016-08-08
    • 2022-10-19
    相关资源
    最近更新 更多