在Python中,我如何检查一个数组是否包含另一个数组/列表的所有元素,包括重复元素?

3
似乎有几种方法可以确定一个集合是否是另一个集合的子集,但我没能找到一种简洁的方法来确定一个列表(或数组)中的所有元素,包括重复值,在另一个列表(或数组)中是否出现。例如,对于假设函数contains_all(A, B),它检查是否所有B的元素都包含在A中,这些是一些预期输出: contains_all([11, 4, 11, 6], [6, 11]) 返回 True (顺序无关紧要) contains_all([11, 4, 11, 6], [11, 9]) 返回 False (没有9) contains_all([11, 4, 11, 6], [11, 11]) 返回 True contains_all([11, 4, 11, 6], [11, 11, 11]) 返回 False (只有两个11) contains_all([11, 4, 11, 6], [6, 6]) 返回 False (只有一个6) contains_all([11, 4, 11, 6], [11, 4, 6, 11]) 返回 True contains_all([11, 4, 11, 6], [11, 4, 11, 6, 5]) 返回 False (没有5)
特别是上面的第四和第五个例子,我在实现时遇到了麻烦。set(B).issubset(A)或列表理解可以处理其他情况,但不包括这些情况,因为集合没有重复元素。有没有一种简洁的方法来做到这一点?如果没有,写一个能够实现此功能的函数最佳方式是什么?似乎使用collections.Counter对象或多重集可能是可能的,但我不确定如何去做。
3个回答

4
在Python 3.10中,collections.Counter已经内置了这个功能:
def contains_all(A, B):
    return Counter(A) >= Counter(B)

然而,由于支持负计数,>= 需要迭代两个计数器中的所有键,当你知道所有计数都是非负数时,这是低效的。手动检查B计数器中的键(如Guy's answer所示)应该更快。 (我手头没有Python 3.10,因此无法提供时间记录。)

这个错误信息是版本特定的吗?在 Python 3.9 上我遇到了 TypeError: '>=' not supported between instances of 'Counter' and 'Counter' - Guy
1
@Guy:显然是这样!我以为它很老了,但文档说它是在3.10中新的。 - user2357112

2

你说得对,collections.Counter是一个不错的选择。你只需要遍历B计数器并检查值是否小于或等于。在all()中执行此操作以检查所有键值对。

def contains_all(a, b):
    counter_a = Counter(a)
    counter_b = Counter(b)
    return all(v <= counter_a[k] for k, v in counter_b.items())

编辑

user2357112的回答更好,但适用于Pyton3.10或更新版本。对于旧版本,您可以使用此答案。


collections.Counter 报告没有与之关联的计数的元素计数为0,因此您不需要进行 k in counter_a 检查。 - user2357112
谢谢,我猜我对Counter不是很熟悉。 - Guy

1

试试这个

def contains(A, B):
contains_all = []
data_missing = []
for i in B:
    if i in A and B.count(i) <= A.count(i):
        contains_all.append(i)
    else:
        data_missing.append(i)
if data_missing:
    return False
else:
    return True

>>> print(contains([11, 4, 11, 6], [11, 4, 11, 6, 5]))
>>> False

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接