2011-08-14 5 views
2

하나의 반복 가능하고 다른 반복 가능하지 않은 모든 항목을 찾으려면 몇 가지 코드를 작성했습니다. 나는 원래 세트 차이를 사용했지만, 각 세트에 수백만 개의 아이템이 저장되어 있기 때문에 계산이 다소 느렸다.Pythonic iterable difference

def differences(a_iter, b_iter): 
    a_items, b_items = set(), set() 

    def remove_or_add_if_none(a_item, b_item, a_set, b_set): 
     if a_item is None: 
      if b_item in a_set: 
       a_set.remove(b_item) 
      else: 
       b_set.add(b) 

    def remove_or_add(a_item, b_item, a_set, b_set): 
     if a in b_set: 
      b_set.remove(a) 
      if b in a_set: 
       a_set.remove(b) 
      else: 
       b_set.add(b) 
      return True 
     return False 

    for a, b in itertools.izip_longest(a_iter, b_iter): 
     if a is None or b is None: 
      remove_or_add_if_none(a, b, a_items, b_items) 
      remove_or_add_if_none(b, a, b_items, a_items) 
      continue 

     if a != b: 
      if remove_or_add(a, b, a_items, b_items) or \ 
       remove_or_add(b, a, b_items, a_items): 
       continue 
      a_items.add(a) 
      b_items.add(b) 

    return a_items, b_items 

그러나, 위의 코드 그래서 난 개선을위한 대안이나 제안을 찾고 있어요 매우 파이썬하지 않는 것 : 나는 천 대부분의 몇 가지 차이점에있을 것입니다 알고 있기 때문에 나는 아래 버전을 썼다.

+5

내장 된 세트의 차이보다 얼마나 더 빠릅니까? –

답변

0

코드가 손상된 것 같습니다. [1,1][1,2]으로 시도하면 1은 하나의 세트에 있지만 다른 세트에는 없게됩니다.

> print differences([1,1],[1,2])             
(set([1]), set([2])) 

당신이 if a != b 시험의 영향이 다시 추적 할 수 있습니다 (즉 주문에 대해 뭔가를 가정 한 것입니다 간단한 설정 차이에 존재하지 않습니다).

아마도 많은 값을 버리는 테스트가 없기 때문에 귀하의 방법이 기본 제공 세트보다 빠르다고 생각하지 않습니다. 인수는 다음과 같이됩니다. 모든 데이터를 보유하기 위해 메모리에 하나의 세트를 작성해야합니다 (버그는 그렇게하지 않았 음). 순진한 접근법은 두 세트를 만듭니다. 그래서 당신이 할 수있는 최선의 방법은 시간의 절반을 절약하는 것입니다. 그리고 파이썬에서 효율적인 C 코드가 무엇인지를 연구해야합니다.

0

나는 파이썬 세트 작업이 표준 라이브러리에서 벗어날 수있는 최상의 성능이라고 생각했을 것이다.

아마도 데이터 구조 및 수행 조작 자체보다는 문제가 귀하가 선택한 특정 구현 일 것입니다. 더 나은 성능을 제공해야하는 대체 구현이 있습니다.

시퀀스가 ​​큰 시퀀스 비교 작업의 경우 가능하면 시퀀스를 구성하는 개체를 비교에 사용되는 컨테이너에 넣지 마십시오. 인덱스 대신 작업하는 것이 좋습니다. 시퀀스의 객체가 정렬되지 않은 경우 정렬합니다.

그래서 예를 들어, 내가 NumPy 사용, 숫자 파이썬 라이브러리, 작업의 이러한 종류를 위해 :

여기
# a, b are 'fake' index arrays of type boolean 
import numpy as NP 
a, b = NP.random.randint(0, 2, 10), NP.random.randint(0, 2, 10) 
a, b = NP.array(a, dtype=bool), NP.array(b, dtype=bool) 

# items a and b have in common: 
NP.sum(NP.logical_and(a, b)) 

# the converse (the differences) 
NP.sum(NP.logical_or(a, b)) 
2

이 더 파이썬 솔루션입니다 :

a, b = set(a_iter), set(b_iter) 

return a - b, b - a 

파이썬이 빠른 것을 의미하지 않는다 , 오히려 우아하고 읽기 쉽습니다. 여기

가 빠를 수있는 솔루션입니다 : 이제

a, b = set(a_iter), set(b_iter) 

# Get all the candidate return values 
symdif = a.symmetric_difference(b) 

# Since symdif has much fewer elements, these might be faster 
return symdif - b, symdif - a 

, 파이썬에서 "빠른"알고리즘을 사용자 정의를 작성하는 대신에 내장 된 작업을 사용하는 방법에 대한 : 그것은 아주 나쁜 생각입니다.

집합 연산자는 많이 최적화되어 있으며 C로 작성되어 일반적으로 Python보다 훨씬 빠릅니다. C (또는 Cython)로 알고리즘을 작성할 수 있지만, 파이썬의 세트 알고리즘은 세계 정상급의 천재에 의해 작성되고 최적화되었다는 것을 명심하십시오. 최적화 작업에 능숙하지 않으면 노력할 가치가 없을 것입니다. 반면에, 당신이 일을 크게 속도를 낼 수 있다면, 코드를 공유하십시오; 나는 파이썬 자체에 들어갈 기회가있을 것이라고 확신한다.

좀 더 현실적인 접근 방법으로 파이썬 코드 호출을 제거해보십시오. 예를 들어, 개체에 사용자 지정 같음 연산자가있는 경우이를 제거하는 방법을 찾아야합니다.

그러나 희망을 얻지 마십시오. 수백만 개의 데이터 작업은 항상 오랜 시간이 걸립니다. 나는 이것을 어디서 사용하는지 모르지만, 설정된 알고리즘을 최적화하는 데 보내는 것보다 1 분 동안 컴퓨터를 바쁘게 보내는 것이 더 나을 것입니다.

관련 문제