【问题标题】:Python: intersection of 2 lists keeping duplicates from both listsPython:2个列表的交集保留两个列表的重复项
【发布时间】:2015-04-24 03:27:25
【问题描述】:

我想有效地找到两个列表的交集,保留 both 的重复项,例如A=[1,1,2,3], B=[1,1,2,4] 应该返回 [1,1,1,1,2]

我知道之前有人问过类似的问题 (Python intersection of two lists keeping duplicates) 但这对我没有帮助,因为只保留一个列表中的重复项。

以下作品

def intersect(A,B):
    C=[]
    for a in A:
        for b in B:
            if a==b:
                C.append(a)
    return C

但是对于我正在做的事情来说效率不够!为了加快速度,我尝试对列表进行排序

def intersect(A,B):
    A.sort()
    B.sort()
    C=[]
    i=0
    j=0
    while i<len(A) and j<len(B):
        if A[i]<=B[j]:
            if A[i]==B[j]: 
                C.append(A[i])
            i+=1
        else:
            j=j+1
    return C

但是这只会保留列表 B 中的重复项。有什么建议吗?

【问题讨论】:

  • 只是为了澄清“列表的交集”在这里你的意思是“如果一个项目在列表 A 中出现 N 次,在列表 B 中出现 M 次,它应该在新列表中出现 N+M 次,但如果 N 或 M 为零,那么它应该在新列表中出现零次”? (这是“交叉点”一词的不寻常用法。)
  • 结果中有4个1,因为A中有2个,B中有2个,那么为什么结果没有两个2s?
  • 根据您的数据,我意识到我的上述解释是不正确的。为什么示例结果中只有一个 2,却有四个 1?
  • 之所以有四个 1,是因为列表 A 中的每个 1 都与列表 B 中的 1 匹配,而列表 A 中的 2 与列表 B 中的 2 唯一匹配。另一个例子是A=[1,1,2,3], B=[1,2,4] 应该返回 [1,1,2]
  • 您是在处理大型列表,还是在处理小型列表但有很多交叉点?你的列表中有很多重复的元素吗?请举一些当时的例子。

标签: python list duplicates intersection


【解决方案1】:

这是您所问问题的答案:

import collections
for A,B,expected_output in (
    ([1,1,2,3], [1,1,2,4], [1,1,1,1,2]),
    ([1,1,2,3], [1,2,4], [1,1,2])):
    cntA = collections.Counter(A)
    cntB = collections.Counter(B)
    output = [
        x for x in sorted(set(A) & set(B)) for i in range(cntA[x]*cntB[x])]
    assert output == expected_output

这是我和其他两个人最初解释的问题的答案:

import collections
A=[1,1,2,3]
B=[1,1,2,4]
expected_output = [1,1,1,1,2,2]
cntA = collections.Counter(A)
cntB = collections.Counter(B)
cnt_sum = collections.Counter(A) + collections.Counter(B)
output = [x for x in sorted(set(A) & set(B)) for i in range(cnt_sum[x])]
assert output == expected_output

您可以找到collections.Counter() 文档herecollections 是一个很棒的模块,我强烈建议阅读整个模块的文档。

我意识到您实际上并不需要找到集合的交集,因为根据文档,“缺失元素的计数为零”:

import collections
for A,B,expected_output in (
    ([1,1,2,3], [1,1,2,4], [1,1,1,1,2]),
    ([1,1,2,3], [1,2,4], [1,1,2])):
    cntA = collections.Counter(A)
    cntB = collections.Counter(B)
    output = [
        x for x in sorted(set(A)) for i in range(cntA[x]*cntB[x])]
    assert output == expected_output

【讨论】:

  • 我希望代码与问题中的第一段代码执行相同的工作,但更有效的是,预期输出为 [1,1,1,1,2]
  • 顶部的工作完全符合我的预期,并且大大加快了我的代码速度。谢谢!
  • cnt_sum 在第一个中是多余的
  • @EllaShar 是的,如果是 Python2,肯定会使用 xrange 而不是 range。我假设是 Python3,但问题只是标记了 Python。
  • @EllaShar:根据经验,最好提供适用于 Python 2 和 Python 3 的答案,尤其是在问题中未说明或标记版本时。
【解决方案2】:

这个怎么样:

a_set = set(A)
b_set = set(B)
intersect = [i for i in A if i in b_set] + [j for j in B if j in a_set]

两个列表推导连接在一起。一些额外的时间和内存用于创建 A 和 B 的集合,但这将被检查集合与列表中项目成员关系的效率所抵消。

你也可以稍微修饰一下:

set_intersect = set(A) & set(B)
list_intersect = [ele for ele in A+B if ele in set_intersect]

将两个列表强制为集合,获取它们的交集,然后使用列表推导添加列表 A 和 B 中的所有元素(如果它们出现在集合的交集中)。

【讨论】:

  • 这给出了我给出的示例 [1,1,1,1,2,2],而不是 [1,1,1,1,2]
【解决方案3】:

我很难加快您的代码速度,因为我不知道您运行它的目的是什么。无论您是在小型列表还是大型列表上运行它,以及那里有多少不同的元素,都会产生很大的不同。无论如何,这里有一些建议:

1.

def intersect(a, b):
    count_a = Counter(a)
    count_b = Counter(b)
    count_mul = []
    for i in count_a:
        count_mul.extend([i] * (count_a[i] * count_b[i]))
    return count_mul

2.

这会返回一个迭代器,你可以使用list(iterator)把它变成一个列表

def intersect(a, b):
    count_a = Counter(a)
    count_b = Counter(b)
    count_mul = Counter()
    for i in count_a:
        count_mul[i] += count_a[i] * count_b[i]
    return count_mul.elements()

3.

与您的方式非常相似,但不会更改列表的大小,这需要时间。

def intersect(A, B):
    return [a for a in A for b in B if a == b]

我不确定这对您的原始方式有什么改进,它确实取决于输入,但您的方式是 O(n*m) 而我的方式是 O(n+m)

您可以使用模块 timeit 来检查它在您的输入上的运行速度:

from timeit import timeit
timeit('test.intersect(A, B)', 'import test; A = [1,1,2,3]; B = [1,1,2,4]')

【讨论】:

  • 输入将是由大整数组成的长度为 3 的元组
猜你喜欢
  • 2014-12-27
  • 1970-01-01
  • 1970-01-01
  • 2020-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多