한 목록이 다른 목록의 하위 집합인지 어떻게 확인할 수 있습니까?


185

목록이 다른 목록의 하위 집합인지 확인해야합니다. 부울 리턴 만 있으면됩니다.

교차 후 작은 목록에서 동등성을 테스트하는 것이 가장 빠른 방법입니까? 비교해야 할 데이터 세트의 수를 고려할 때 성능이 가장 중요합니다.

토론을 기반으로 추가 사실 추가 :

  1. 많은 테스트에서 목록 중 하나가 동일합니까? 정적 조회 테이블 중 하나이므로 수행합니다.

  2. 목록이어야합니까? 정적 조회 테이블은 성능이 가장 좋은 것이 될 수 있습니다. 동적은 정적 조회를 수행하기 위해 키를 추출하는 명령입니다.

시나리오를 고려할 때 최적의 솔루션은 무엇입니까?


사용에 따라 속도, 아마도 numpy가 유용 할 것이라고 언급했습니다.
ninMonkey

2
목록 항목은 해시 가능합니까?
wim


적절한 부분 집합이 필요합니까, 아니면 동등 할 수 있습니까?
törzsmókus

2
왜 set (list_a) .issubset (set (list_b))입니까?
SeF

답변:


127

파이썬이 제공하는 퍼포먼스 함수는 set.issubset입니다. 그러나 귀하의 질문에 대한 답변인지 확실하지 않은 몇 가지 제한 사항이 있습니다.

목록에는 항목이 여러 번 포함될 수 있으며 특정 주문이 있습니다. 세트는하지 않습니다. 또한 세트는 해시 가능한 객체 에서만 작동 합니다.

서브 세트 또는 서브 시퀀스에 대해 질문하고 있습니까 (즉, 문자열 검색 알고리즘이 필요함)? 많은 테스트에서 목록 중 하나가 동일합니까? 목록에 포함 된 데이터 유형은 무엇입니까? 그리고 그 문제에 대해 목록이 필요합니까?

다른 게시물 은 dict과 교차하고 목록 이 유형을 더 명확하게 만들고 세트와 같은 기능을 위해 사전 키보기를 사용하도록 권장했습니다. 이 경우 사전 키가 집합처럼 동작하기 때문에 작동하는 것으로 알려져 있습니다 (파이썬에서 집합을 사용하기 전에 사전을 사용 했음). 세 시간 동안 문제가 어떻게 구체적이지 않은지 궁금합니다.


나는 부분 집합만을 언급하고 있으며 issubset은 잘 작동합니다-감사합니다. 그러나 나는 여기에 2 가지 질문이 궁금합니다. 1. 많은 테스트에서 목록 중 하나가 동일합니까? 정적 조회 테이블 중 하나이므로 목록이 필요합니까? 정적 조회 테이블은 성능이 가장 좋은 것이 될 수 있습니다. 동적은 정적 조회를 수행하기 위해 키를 추출하는 명령입니다. 이 사실이 해결책을 바꿀 것인가?
IUnknown

별로. 사전의 키는 설정과 유사하며 이미 해시 테이블에 배열되어 있으므로 정적 부분에 대한 세트를 사용하면 추가 합병증이 발생하지 않습니다. 기본적으로 하나가 dict이라는 사실은 정적 부분을 세트로 변환 할 필요가 없다는 것을 의미합니다 (O (n) 성능으로 모든 (itertools.imap (dict.has_key, mylist)을 확인할 수 있음).
Yann Vernier

나는 이것이 (또는 세트에 의존하는 다른 솔루션) 어떻게 받아 들여질 수 있는지 이해하지 못한다. 문제는 목록에 관한 것이며 솔직히 "한 목록이 다른 목록의 하위 집합인지 확인"의 하위 집합을 문자 그대로 사용해서는 안된다고 생각합니다. 세트로 변환하면 중복 요소에 대한 정보가 손실되지만 초기 목록에 해당 정보가 포함될 수 있으면 두 번째 목록에 해당 정보가 있는지 확인하고 한 목록의 모든 요소를 ​​찾을 수 있는지 확인하는 것이 중요합니다. 다른 안에. 세트는하지 않습니다!
inVader

상황이 중요합니다. 이것은 구도자를 돕는 것으로 받아 들여졌으며 그 차이점을 설명했다. 우리는 후보자들이 세트로 표현 될 수 있다고 들었으므로 세트 작업이었습니다. 귀하의 사례는 다를 수 있으며 언급 한 차이는 collections.Counter와 같은 다중 세트를 사용하여 해결됩니다.
Yann Vernier

141
>>> 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으로 만 변환 하고 변환 하지 않기 때문에 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)
율란 리우

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]이면 어떻게됩니까? 집합 이론은 중복에 부적합합니다.
Eamonn Kenny

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()반환 True합니다 False.

all모든 항목을 처리하지 않고 누락 된 요소의 첫 번째 인스턴스에서 False 를 반환 하는 이점도 있습니다.


가독성을 높이고 달성하려는 내용을 명시하는 것이 set(one).issubset(set(two))훌륭한 솔루션 이라고 생각합니다 . 내가 게시 한 솔루션을 사용하면 적절한 비교 연산자가 정의되어 있으면 객체와 함께 사용할 수 있습니다.
voidnologo

4
목록 이해가 아닌 생성기 표현식을 사용하십시오. 전자는 all올바르게 단락 될 수 있고, 후자는 첫 번째 검사에서 테스트가 실패한 것이 확실하더라도 모든 검사를 수행합니다. 대괄호를 떨어 뜨리면됩니다 all(x in two for x in one).
ShadowRanger

내가 틀렸습니까, 아니면이 방법을 현지인과 함께 사용할 수 없습니까?
Homper

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)하나의 요소 (True) 만 있습니다.


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

1

아무도 두 문자열을 비교하는 것을 고려하지 않았으므로 여기 내 제안이 있습니다.

물론 파이프 ( "|")가 목록의 일부가 아니고 다른 문자를 자동으로 선택했는지 확인하고 싶을 수도 있습니다.

숫자가 여러 자릿수를 가질 수 있으므로 빈 문자열을 구분 기호로 사용하는 것은 해결책이 아닙니다 ([12,3]! = [1,23])

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

0

파티에 늦었다면 용서해주세요 ;)

하나가 있는지 확인하려면 set A의 하위 집합입니다 set B, Python가지고 A.issubset(B)A <= B. 그것은에 작동 set만 잘 작동 하지만 내부 구현의 복잡성을 알 수 없습니다. 참조 : https://docs.python.org/2/library/sets.html#set-objects

나는 다음과 같은 말 list A의 하위 집합 인지 확인하는 알고리즘을 생각해 냈습니다 list B.

  • 하위 집합 찾기의 복잡성을 줄이기 위해 sort요소를 하위 집합에 적합하게 비교하기 전에 먼저 두 목록 모두에 적합한 것으로 나타 났습니다.
  • 이 날 도움 제리스트의 요소의 값이 될 때 제리스트의 요소의 값보다 더 크다 .breakloopB[j]A[i]
  • last_index_j마지막으로 중단 된 지점 부터 다시 시작 loop하는 데 사용 list B됩니다. 그것은 시작부터 비교를 시작 피할 수 있습니다 list B(당신이 필요 짐작 시작으로, 어떤 list B에서 index 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를 반환합니다.
레오 바 스틴

근데 이러는거야? A가 B의 부분 집합이되는 것은 단순히 A가 B에없는 항목을 포함하지 않거나 A의 모든 항목도 B에 있음을 의미합니다. 따라서 빈 세트는 자체를 포함한 모든 세트 의 서브 세트입니다 . 귀하의 추가 수표는 그것이 아니라고 주장하고, 이것이 이상적이라고 주장하지만, 확립 된 용어와 반대입니다. 장점은 무엇입니까?
얀 베르니에

감사합니다 @YannVernier 이제 코드는 주어진 집합이 다른 집합의 "적절한 부분 집합"인지 확인합니다.
레오 바 스틴

이것은 set 의 사용에 의존하는 대답만큼 나쁘다 . 수학적으로 말하면, 집합은 고유 한 요소의 모음이지만 하나의 목록이 다른 목록의 일부인지 확인할 때 해당 가정에 의존 할 수 있습니다. 초기 목록에 중복이 포함되어 있으면 문제의 요소가 두 번째 목록에 한 번만 있어도 함수는 여전히 True를 반환 할 수 있습니다 . 목록을 비교할 때 이것이 올바른 동작이라고 생각하지 않습니다.
inVader

0

파이썬 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

-1

한 목록이 다른 목록에 "포함"되어 있는지 묻는 경우 :

>>>if listA in listB: return True

listA의 각 요소에 listB의 동일한 수의 일치하는 요소가 있는지 묻는 경우 다음을 시도하십시오.

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

이것은 나를 위해 작동하지 않습니다. listA == listB
cass

@cass 나는 문자열로만 테스트했습니다. 컴퓨터에서 시도하십시오. pastebin.com/9whnDYq4
DevPlayer

두 번째 부분이 아니라 "listB의 listA : return True"부분을 참조했습니다.
cass

@cass 고려 : [ 'one', 'two']에서 [ 'one', 'two']는 False를 산출합니다. [ 'one', 'two', 'three']에서 [ 'one', 'two']는 False입니다. [[ 'one', 'two'], 'three']에서 [ 'one', 'two']는 True입니다. 따라서 listA == ListB 인 경우 listA는 listB 내의 목록 요소 여야하므로 listB의 listA는 항상 False를 반환합니다. 아마도 당신은 생각하고있을 것입니다 : listB의 listA는 "listA의 항목이 listB의 항목으로 나열되었습니다. 이것은 listB의 listA의 의미가 아닙니다
DevPlayer

@cass 아, 내 게시물이 어떻게 혼동되는지 봅니다. 원래 게시물은 listA를 listB의 하위 집합으로 테스트하도록 요청했습니다. 기술적으로 내 게시물은 원래 게시물의 질문에 따라 잘못되었습니다. 그것이 옳다면, 질문은 "[item0, item2, listA, item3, listA,]의 listA 인 경우"를 요구했을 것입니다. "[ 'd', 'c', 'f', 'a', 'b', 'a']의 [ 'a', 'b', 'c']에있는 항목"이 아닙니다.
DevPlayer

-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))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.