如何验证一个列表是否是另一个列表的子集?


184

我需要验证列表是否是另一个列表的子集-我想要的只是布尔返回值。

在相交之后在较小列表上测试相等性是最快的方法吗?鉴于需要比较的数据集数量,性能至关重要。

根据讨论添加更多事实:

  1. 在许多测试中,两个列表中的两个列表是否相同?它作为静态查找表之一来执行。

  2. 需要列表吗?事实并非如此-静态查找表可以是执行效果最好的任何内容。动态命令是一个字典,我们从中提取密钥以执行静态查找。

在这种情况下,最佳解决方案是什么?


您提到速度,根据您的使用,numpy可能会有用。
ninMonkey

2
列表项可以散列吗?
2013年

2
如果顺序很重要,那么这可能是一个好的开始

您需要适当的子集,还是可以相等?
törzsmókus

2
为什么不设置(list_a).issubset(set(list_b))?
SeF

Answers:


127

Python为此提供的性能函数是set.issubset。但是,它确实有一些限制,使得不清楚是否是您问题的答案。

列表可能包含多个项目并具有特定顺序。一套没有。此外,设置仅适用于可哈希对象。

您是在询问子集还是子序列(这意味着您需要一个字符串搜索算法)?在许多测试中,两个列表中的两个列表是否相同?列表中包含哪些数据类型?而且,这是否需要列出清单?

您的其他文章与字典和列表相交,使类型更清晰,并且确实推荐使用字典键视图来实现类似集合的功能。在那种情况下,之所以可以工作是因为字典键的行为就像一个集合(以至于在我们使用Python进行集合之前,我们都使用字典)。一个人想知道问题如何在三个小时内变得不那么具体。


我仅指一个子集,而issubset执行得很好-谢谢。但是我对这里的两个问题感到好奇。1.许多测试中的两个列表是否相同?它是静态查找表之一,它是其中之一。是否需要一个列表?事实并非如此-静态查找表可以是执行效果最好的任何内容。动态命令是一个字典,我们从中提取密钥以执行静态查找。这个事实会改变解决方案吗?
IUnknown

不多。字典的键类似于集合,并且已经排列在哈希表中,因此将集合用于静态部分不会引起其他复杂性。基本上,一个是dict这一事实意味着您可能不需要将静态部分转换为一个集合(您可以使用O(n)性能来检查所有(itertools.imap(dict.has_key,mylist)))。
Yann Vernier

我不理解这个(或任何其他依赖于集合的解决方案)如何在这里成为公认的答案。问题是关于列表的,我坦率地说,“验证一个列表是否是另一个列表的子集”中的子集不应该从字面上理解。转换为集合时,有关重复元素的任何信息都会丢失,但是,如果初始列表中可能包含这些信息,那么检查它们是否出现在第二个列表中以及真实地说可以找到一个列表中的所有元素可能很重要。在另一个。集不那样做!
inVader

上下文很重要;这是为帮助申请者而被接受的,并且确实解释了区别。我们被告知候选人将是可代表的集合,所以这是一项固定的任务。您的情况可能有所不同,您提到的差异将使用collections.Counter等多集解决。
Yann Vernier,

140
>>> a = [1, 3, 5]
>>> b = [1, 3, 5, 8]
>>> c = [3, 5, 9]
>>> set(a) <= set(b)
True
>>> set(c) <= set(b)
False

>>> a = ['yes', 'no', 'hmm']
>>> b = ['yes', 'no', 'hmm', 'well']
>>> c = ['sorry', 'no', 'hmm']
>>> 
>>> set(a) <= set(b)
True
>>> set(c) <= set(b)
False

21
这看起来最棒,编写起来也很简单,但是最快的应该是 set(a).issubset(b) 因为在这种情况下,您只转换a为set而不转换为set b,从而节省了时间。您可以timeit用来比较两个命令所消耗的时间。例如,timeit.repeat('set(a)<set(b)', 'a = [1,3,5]; b = [1,3,5,7]', number=1000) 以及 timeit.repeat('set(a).issubset(b)', 'a = [1,3,5]; b = [1,3,5,7]', number=1000)
Yulan Liu

8
@YulanLiu:讨厌把它交给你,但第一件事issubset就是检查参数是否为set/ frozenset,如果不是,它将其转换为临时变量set进行比较,运行检查,然后丢弃该临时变量set时间差异(如果有的话)将是LEGB查找成本差异很小的一个因素(set第二次查找比现有属性的属性查找更昂贵set),但是对于足够大的输入来说,这主要是洗钱。
ShadowRanger

3
如果两个列表都包含相同的值,则此列表将返回false,而应将条件设置为(a)<= set(b)
ssi-anik

2
这个答案如何正确?他要求一份清单而不是一套。他们是完全不同的。如果a = [1、3、3、5、5]和b = [1、3、3、3、5],该怎么办。集合论不适用于重复项。
伊曼·肯尼

1
我还要指出,如果a = [1,3,5]和b = [1,3,5],则set(a)<set(b)将返回False。您可以添加equals运算符来处理以下情况:即set(a)<= set(b)。
乔恩(Jon)

37
one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

all(x in two for x in one)

说明:生成器通过遍历列表one检查该项是否在list中来创建布尔值two。 如果每个项目都是真实的,则返回,否则all()返回。TrueFalse

还有一个优点是all,在缺少元素的第一个实例上返回False,而不必处理每个项目。


我认为对于可读性和明确说明您要实现的目标,这set(one).issubset(set(two))是一个很好的解决方案。使用我发布的解决方案,如果定义了正确的比较运算符,则应该可以将其与任何对象一起使用。
voidnologo '16

4
使用生成器表达式,而不是列表推导;前者将允许all正确短路,而后者将执行所有检查,即使从第一次检查中可以清楚得知测试将失败。只需放下方括号即可all(x in two for x in one)
ShadowRanger

我是错的,还是您不能在本地人身上使用这种方法?
坎珀

22

假设项目是可哈希的

>>> from collections import Counter
>>> not Counter([1, 2]) - Counter([1])
False
>>> not Counter([1, 2]) - Counter([1, 2])
True
>>> not Counter([1, 2, 2]) - Counter([1, 2])
False

如果您不在乎重复的项目,例如 [1, 2, 2]并且[1, 2]然后只需使用:

>>> set([1, 2, 2]).issubset([1, 2])
True

在相交之后在较小列表上测试相等性是最快的方法吗?

.issubset将是最快的方法。在测试之前检查长度issubset不会提高速度,因为您仍然有O(N + M)个项目要进行迭代和检查。


6

另一种解决方案是使用intersection

one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

set(one).intersection(set(two)) == set(one)

集的交集将包含 set one

(要么)

one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

set(one) & (set(two)) == set(one)

2
one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

set(x in two for x in one) == set([True])

如果list1在列表2中:

  • (x in two for x in one)生成的列表True

  • 当我们做一个set(x in two for x in one)只有一个元素(真)。


2

集合论不适用于列表,因为重复使用集合论会导致错误的答案。

例如:

a = [1, 3, 3, 3, 5]
b = [1, 3, 3, 4, 5]
set(b) > set(a)

没有任何意义。是的,它给出了错误的答案,但这是不正确的,因为集合论只是在进行比较:1,3,5与1,3,4,5。您必须包括所有重复项。

取而代之的是,您必须计算每个项目的每次出现次数,并进行大于等于的检查。这不是很昂贵,因为它不使用O(N ^ 2)运算,并且不需要快速排序。

#!/usr/bin/env python

from collections import Counter

def containedInFirst(a, b):
  a_count = Counter(a)
  b_count = Counter(b)
  for key in b_count:
    if a_count.has_key(key) == False:
      return False
    if b_count[key] > a_count[key]:
      return False
  return True


a = [1, 3, 3, 3, 5]
b = [1, 3, 3, 4, 5]
print "b in a: ", containedInFirst(a, b)

a = [1, 3, 3, 3, 4, 4, 5]
b = [1, 3, 3, 4, 5]
print "b in a: ", containedInFirst(a, b)

然后运行它,您将得到:

$ python contained.py 
b in a:  False
b in a:  True

0

如果我迟到聚会请原谅。;)

要检查一个set A是否是set BPython具有A.issubset(B)和的子集A <= B。它set只能工作,而且效果很好,但是内部实现的复杂性是未知的。参考:https : //docs.python.org/2/library/sets.html#set-objects

我想出了一种算法来检查是否list Alist B以下注释的子集。

  • 为了降低查找子集的复杂性,我发现 sort先比较两个列表,然后再比较要符合子集条件的元素。
  • 它帮助我breakloop当第二列表元素的值B[j]比第一个列表元素的值A[i]
  • last_index_j用于启动looplist B其最后一次离开。它有助于避免从的开头开始进行比较 list B(您可能会不必要地list Bindex 0随后的开始iterations)进行比较。
  • O(n ln n)排序列表和O(n)检查子集都将很复杂。
    O(n ln n) + O(n ln n) + O(n) = O(n ln n)

  • 代码中有很多print语句,以查看每个处iteration发生了什么loop。这些仅用于理解。

检查一个列表是否是另一个列表的子集

is_subset = True;

A = [9, 3, 11, 1, 7, 2];
B = [11, 4, 6, 2, 15, 1, 9, 8, 5, 3];

print(A, B);

# skip checking if list A has elements more than list B
if len(A) > len(B):
    is_subset = False;
else:
    # complexity of sorting using quicksort or merge sort: O(n ln n)
    # use best sorting algorithm available to minimize complexity
    A.sort();
    B.sort();

    print(A, B);

    # complexity: O(n^2)
    # for a in A:
    #   if a not in B:
    #       is_subset = False;
    #       break;

    # complexity: O(n)
    is_found = False;
    last_index_j = 0;

    for i in range(len(A)):
        for j in range(last_index_j, len(B)):
            is_found = False;

            print("i=" + str(i) + ", j=" + str(j) + ", " + str(A[i]) + "==" + str(B[j]) + "?");

            if B[j] <= A[i]:
                if A[i] == B[j]:
                    is_found = True;
                last_index_j = j;
            else:
                is_found = False;
                break;

            if is_found:
                print("Found: " + str(A[i]));
                last_index_j = last_index_j + 1;
                break;
            else:
                print("Not found: " + str(A[i]));

        if is_found == False:
            is_subset = False;
            break;

print("subset") if is_subset else print("not subset");

输出量

[9, 3, 11, 1, 7, 2] [11, 4, 6, 2, 15, 1, 9, 8, 5, 3]
[1, 2, 3, 7, 9, 11] [1, 2, 3, 4, 5, 6, 8, 9, 11, 15]
i=0, j=0, 1==1?
Found: 1
i=1, j=1, 2==1?
Not found: 2
i=1, j=2, 2==2?
Found: 2
i=2, j=3, 3==3?
Found: 3
i=3, j=4, 7==4?
Not found: 7
i=3, j=5, 7==5?
Not found: 7
i=3, j=6, 7==6?
Not found: 7
i=3, j=7, 7==8?
not subset

如果对它们进行排序,则不再有任何理由使用列表而不是列表…
LtWorf

0

下面的代码检查给定集合是否为另一个集合的“适当子集”

 def is_proper_subset(set, superset):
     return all(x in superset for x in set) and len(set)<len(superset)


感谢@YannVernier,我已修改为对子集和超集都包括空检查,因此当两者均为空时它返回false。
Leo Bastin

但是,为什么要这样做呢?要使A成为B的子集,仅意味着A不包含不在B中的项目,或者等效地,A中的所有项目也都在B中。因此,空集合是所有集合(包括其自身)的子集。您的额外检查断言它不是,并且您断言这是理想的,但它与既定术语相反。有什么好处?
Yann Vernier

谢谢@YannVernier现在,代码检查给定集合是否为另一个集合的“适当子集”。
Leo Bastin

这与依赖集合的答案一样糟糕。从数学上讲,集合是不同元素的集合,在检查一个列表是否为另一个列表的一部分时,我们可以也不应该依赖该假设。如果初始列表包含重复项,则即使该元素在第二个列表中仅出现一次,您的函数仍可以返回True。我不认为这是尝试比较列表时的正确行为。
inVader

0

在python 3.5中,您可以执行[*set()][index]来获取元素。这是比其他方法慢得多的解决方案。

one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

result = set(x in two for x in one)

[*result][0] == True

或仅与len和set

len(set(a+b)) == len(set(a))

0

这是我如何知道一个列表是否是另一个列表的子集,对于我来说,顺序对我很重要。

def is_subset(list_long,list_short):
    short_length = len(list_short)
    subset_list = []
    for i in range(len(list_long)-short_length+1):
        subset_list.append(list_long[i:i+short_length])
    if list_short in subset_list:
        return True
    else: return False

0

大多数解决方案都认为列表没有重复项。如果您的列表确实有重复,可以尝试以下操作:

def isSubList(subList,mlist):
    uniqueElements=set(subList)
    for e in uniqueElements:
        if subList.count(e) > mlist.count(e):
            return False     
    # It is sublist
    return True

这样可以确保子列表中的元素绝不会与列表中的元素不同,也不会包含更多的公共元素。

lst=[1,2,2,3,4]
sl1=[2,2,3]
sl2=[2,2,2]
sl3=[2,5]

print(isSubList(sl1,lst)) # True
print(isSubList(sl2,lst)) # False
print(isSubList(sl3,lst)) # False

0

由于没有人考虑过将字符串与字符串进行比较,因此这是我的建议。

当然,您可能想检查管道(“ |”)是否不在两个列表中,并且可能自动选择另一个字符,但是您明白了。

使用空字符串作为分隔符不是解决方案,因为数字可以有几个数字([12,3]!= [1,23])

def issublist(l1,l2):
    return '|'.join([str(i) for i in l1]) in '|'.join([str(i) for i in l2])

-1

如果您要问一个列表是否“包含”在另一个列表中,则:

>>>if listA in listB: return True

如果您要询问listA中的每个元素在listB中是否具有相等数量的匹配元素,请尝试:

all(True if listA.count(item) <= listB.count(item) else False for item in listA)

这对我不起作用。返回FALSE即使== listA的数组listB
中国社会科学院

@cass我只测试过字符串。在您的机器上尝试一下。pastebin.com/9whnDYq4
DevPlayer 2015年

我指的是“如果listB中的listA:返回True”部分,而不是第二部分。
cass 2015年

@cass考虑:['one','two']中的['one','two']产生False。['one','two','3']中的['one','two']产生False。[['one','two'],'three']中的['one','two']产生True。所以是的,如果listA == ListB,则listB中的listA将始终返回False,因为listA必须是listB中的列表元素。也许您在想:listB中的listA表示“列表A中的项目是否被列为listB中的项目。这不是listB中listA的含义
DevPlayer 2015年

@cass啊,我知道我的帖子令人困惑了。原始帖子要求测试listA是listB的子集。从技术上讲,基于原始帖子的问题,我的帖子是错误的。为正确起见,该问题必须询问“是否[item0,item2,listA,item3,listA,]中的listA”。不是“ [[d”,“ c”,“ f”,“ a”,“ b”,“ a”]中的['a”,“ b”,“ c”]中的项目”。
DevPlayer 2015年

-2

如果a2 is subset of a1,那么Length of set(a1 + a2) == Length of set(a1)

a1 = [1, 2, 3, 4, 5]
a2 = [1, 2, 3]

len(set(a1)) == len(set(a1 + a2))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.