我想有效地找到两个列表的交集,保留两者的重复项,例如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中的重复项。任何建议?
答案 0 :(得分:3)
以下是您提出的问题的答案:
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()
文档here。 collections
是一个很棒的模块,我强烈建议您阅读整个模块的文档。
我意识到你实际上并不需要找到集合的交集,因为根据文档“缺少元素的数量是零”:
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 :(得分: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中的所有元素(如果它们出现在集合交集中)。
答案 2 :(得分:0)
我很难加速你的代码,因为我不知道你在运行什么。无论是在小型列表还是大型列表上运行它,以及有多少不同的元素,它都会产生很大的不同。无论如何,这里有一些建议:
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]')