IT

주요에서 목록이 항목을 공유하는지 테스트

lottoking 2020. 7. 30. 09:37
반응형

주요에서 목록이 항목을 공유하는지 테스트


있는지 확인하려면 나는 어느 하나 개의 목록에있는 항목의 다른 목록에 존재한다. 아래 코드를 사용하여 간단하게 수행 할 수있는 기능이 작업을 수행하는 라이브러리를 수행합니다. 어떤 경우에 있습니까?

In [78]: a = [1, 2, 3, 4, 5]

In [79]: b = [8, 7, 6]

In [80]: c = [8, 7, 6, 5]

In [81]: def lists_overlap(a, b):
   ....:     for i in a:
   ....:         if i in b:
   ....:             return True
   ....:     return False
   ....: 

In [82]: lists_overlap(a, b)
Out[82]: False

In [83]: lists_overlap(a, c)
Out[83]: True

In [84]: def lists_overlap2(a, b):
   ....:     return len(set(a).intersection(set(b))) > 0
   ....: 

짧은 대답 : use not set(a).isdisjoint(b), 일반적으로 가장 빠 사용.

두 개의 목록 ab공유하고 항목을 공유 하는지 테스트하는 일반적인 네 가지 방법이 있습니다. 첫 번째 옵션은 다음과 같이 둘 다 세트로 변환하고 교차점을 확인하는 것입니다.

bool(set(a) & set(b))

세트는 명세에서 해시 테이블을 사용하여 저장O(1) 되기 때문에 세트를 검색합니다 ( 그리고 연산자의 세트에 대한 자세한 내용 여기 참조 ). 이론적으로, 이은 O(n+m)에 대한 평균 nm목록에 ab. 1) 먼저 목록에서 집합을 무시하고 무시할 수없는 시간이 걸릴 수 있습니다. 2) 해시 충돌이 데이터 사이에 드문이 아니라 가정합니다.

두 번째 방법은 다음과 같이 목록에서 반복을 수행하는 생성기를 사용하는 것입니다.

any(i in a for i in b)

이를 통해 내부 검색이 가능하지 않은 중간 변수에 새 메모리가 할당되지 않습니다. 그것은 또한 첫 번째 발견에서 구제됩니다. 그러나 in연산자는 항상 O(n)목록에 있습니다 ( 여기 참조 ).

제안 된 또 다른 옵션은 목록 중 하나를 반복하고 세트의 다른 하나를 변환 하고이 세트의 멤버십을 테스트하는 하이브리드입니다.

a = set(a); any(i in a for i in b)

네 번째 방법은 isdisjoint()(냉동 된) 세트 방법을 활용하는 것입니다 ( 여기 참조 ).

not set(a).isdisjoint(b)

검색하는 요소가 배열의 시작 부분 근처에있는 경우 (예 : 정렬 된 경우), 집합 교차 방법이 중개 변수에 새 메모리를 할당해야 할 때 생성기 선호합니다.

from timeit import timeit
>>> timeit('bool(set(a) & set(b))', setup="a=list(range(1000));b=list(range(1000))", number=100000)
26.077727576019242
>>> timeit('any(i in a for i in b)', setup="a=list(range(1000));b=list(range(1000))", number=100000)
0.16220548999262974

리스트 크기에 따른 예제의 실행 시간 그래프는 다음과 가변됩니다.

처음에 공유 할 때 요소 공유 테스트 실행 시간

두 축은 ​​모두 로그입니다. 이는 생성기 대부분에 가장 경우를 나타냅니다. 보다시피,이 isdisjoint()방법은 매우 작은 크기에 대해 더 나은 반면, 생성기 좀 더 큰 목록 크기에 대해 더 좋습니다.

반면에 검색이 사용하고 생성기 사용의 시작으로 하이브리드 시작에 따라 공유 요소가 배열의 끊임없는 체계적으로 공유 요소가 배열의 공유하지 않는 경우 분리 및 설정 교차 접근 방식은 훨씬 빠너 레이터 표현과 하이브리드 방식보다 훨씬 빠릅니다.

>>> timeit('any(i in a for i in b)', setup="a=list(range(1000));b=[x+998 for x in range(999,0,-1)]", number=1000))
13.739536046981812
>>> timeit('bool(set(a) & set(b))', setup="a=list(range(1000));b=[x+998 for x in range(999,0,-1)]", number=1000))
0.08102107048034668

마지막에 공유 할 때 요소 공유 테스트 실행 시간

더 느리다는 점에 주목하는 것이 흥미 롭습니다. 이것은 이전 그림의 100000이 아니라 1000 회 반복입니다. 이 설정은 공유 된 요소가 없을 때에도 근사치이며, 분리 및 교차로 접근에 가장 많은 경우입니다.

다음은 임의의 숫자를 사용하는 두 가지 분석입니다 (하나의 기술을 선호하는 설정을 조작하는 대신).

공유 가능성이 높은 임의로 생성 된 데이터의 요소 공유 테스트 실행 시간 공유 가능성이 높은 임의로 생성 된 데이터의 요소 공유 테스트 실행 시간

높은 공유 가능성 : 요소는 임의로 가져옵니다 [1, 2*len(a)]. 낮은 공유 가능성 : 요소는 임의로 가져옵니다 [1, 1000*len(a)].

지금 까지이 분석에서는 두 목록의 크기가 모두 같다고 가정했습니다. 크기가 다른 두 개의 경우, 예를 들어이 a훨씬 작다 isdisjoint()항상 빠른 :

처음에 공유 할 때 서로 다른 크기의 두 목록에서 요소 공유 테스트 실행 시간 마지막에 공유 할 때 서로 다른 크기의 두 목록에서 요소 공유 테스트 실행 시간

존재 확인하십시오 a, 성능이 감소 목록은 작다. 이 실험에서 a크기는 일정하게 설정되었습니다 5.

요약해서 기재 :

  • 목록이 매우 작은 경우 (<10 개 요소) not set(a).isdisjoint(b)항상 가장 사용하기 빠릅니다.
  • 사용하는 경우에는 사용되는 규칙적인 구조를 사용하고 있습니다 any(i in a for i in b).
  • not set(a).isdisjoint(b)항상보다 빠른와 세트 교차점을 테스트합니다 bool(set(a) & set(b)).
  • 하이브리드 "목록을 반복하고 세트에서 테스트" a = set(a); any(i in a for i in b)는 일반적으로 다른 방법보다 느립니다.
  • 생성기 표현식과 하이브리드는 요소를 공유하지 않는 목록과 관련하여 다른 두 가지 접근 방식보다 훨씬 느립니다.

대부분의 경우 isdisjoint()메서드를 사용하는 것이 가장 좋은 방법입니다. 생성기 표현식을 실행하는 데 훨씬 오래 걸리기 때문입니다. 요소가 공유되지 않으면 매우 비효율적입니다.


def lists_overlap3(a, b):
    return bool(set(a) & set(b))

참고 : 위의 내용은 부울을 답으로 원한다고 가정합니다. if에서 사용할 표현식 만 있으면됩니다.if set(a) & set(b):


def lists_overlap(a, b):
  sb = set(b)
  return any(el in sb for el in a)

이것은 점근 적으로 최적이며 (최악의 경우 O (n + m)) any의 단락 으로 인해 교차로 접근 방식보다 나을 수 있습니다 .

예 :

lists_overlap([3,4,5], [1,2,3])

도착하자마자 True를 반환합니다. 3 in sb

편집 : 또 다른 변형 (Dave Kirby 덕분에) :

def lists_overlap(a, b):
  sb = set(b)
  return any(itertools.imap(sb.__contains__, a))

이것은 imap생성기 이해가 아닌 C로 구현 된의 반복기 에 의존합니다 . sb.__contains__매핑 기능으로 도 사용 됩니다. 이것이 성능 차이가 얼마나 큰지 모르겠습니다. 여전히 단락됩니다.


any목록 이해력과 함께 사용할 수도 있습니다 .

any([item in a for item in b])

파이썬 2.6 이상에서는 다음을 수행 할 수 있습니다.

return not frozenset(a).isdisjoint(frozenset(b))

내장 함수 / wa 생성기 표현식을 사용할 수 있습니다.

def list_overlap(a,b): 
     return any(i for i in a if i in b)

John과 Lie가 지적했듯이 두 목록 bool (i) == False가 공유 할 때마다 잘못된 결과를 제공합니다. 그것은해야한다:

return any(i in b for i in a)

이 질문은 꽤 오래되었지만 사람들이 세트 대 목록을 논쟁하는 동안 아무도 함께 사용하는 것을 생각하지 않았다는 것을 알았습니다. Soravux의 예에 따라

목록의 최악의 경우 :

>>> timeit('bool(set(a) & set(b))',  setup="a=list(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
100.91506409645081
>>> timeit('any(i in a for i in b)', setup="a=list(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
19.746716022491455
>>> timeit('any(i in a for i in b)', setup="a= set(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
0.092626094818115234

그리고 목록의 가장 좋은 경우 :

>>> timeit('bool(set(a) & set(b))',  setup="a=list(range(10000)); b=list(range(10000))", number=100000)
154.69790101051331
>>> timeit('any(i in a for i in b)', setup="a=list(range(10000)); b=list(range(10000))", number=100000)
0.082653045654296875
>>> timeit('any(i in a for i in b)', setup="a= set(range(10000)); b=list(range(10000))", number=100000)
0.08434605598449707

따라서 두 개의 목록을 반복하는 것보다 더 빠른 것은 목록이 집합에 있는지 확인하기 위해 목록을 반복하는 것입니다. 이는 숫자가 집합에 있는지 확인하는 데 일정한 시간이 걸리고 목록을 반복하여 확인하는 데는 길이에 비례하는 시간이 걸리기 때문에 의미가 있습니다. 목록.

따라서 내 결론은 목록반복하고 그것이 세트에 있는지 확인하는 것입니다 .


겹치는 요소가 무엇인지 신경 쓰지 않는다면 len결합 된 목록과 집합으로 결합 된 목록을 간단히 확인할 수 있습니다 . 겹치는 요소가 있으면 세트가 더 짧아집니다.

len(set(a+b+c))==len(a+b+c) 겹치는 부분이 없으면 True를 반환합니다.


함수형 프로그래밍 스타일로 다른 것을 던질 것입니다.

any(map(lambda x: x in a, b))

설명:

map(lambda x: x in a, b)

의 요소가 b있는 부울 목록을 반환합니다 a. 다음 해당 목록 그런이로 전달되어 요소가있는 경우 any간단히 반환 True됩니다 True.

참고 URL : https://stackoverflow.com/questions/3170055/test-if-lists-share-any-items-in-python

반응형