2017-05-10 3 views
1

발전기의 일부 하위 집합을 필터링하는 가장 좋은 방법은 무엇입니까? 예를 들어, 문자열 "1023"을 가지고 있으며 각 숫자의 가능한 모든 조합을 생성하려고합니다. 모든 조합은 다음과 같습니다발전기 필터링

['1', '0', '2', '3'] 
['1', '0', '23'] 
['1', '02', '3'] 
['1', '023'] 
['10', '2', '3'] 
['10', '23'] 
['102', '3'] 
['1023'] 

나는 항목 중 하나에 선도적 인 0을 포함하는 부분 집합에 관심이 아니에요, 그래서 올바른 사람은 다음과 같습니다

['1', '0', '2', '3'] 
['1', '0', '23'] 
['10', '2', '3'] 
['10', '23'] 
['102', '3'] 
['1023'] 

나는 두 가지 질문이 있습니다.

1) 발전기를 사용하는 경우 앞에 오는 0으로 필터링하는 것이 가장 좋습니다. 현재는 모든 조합을 생성 한 다음 나중에 반복하고 하위 집합이 유효한 경우에만 계속합니다. 간단하게 샘플 코드에서 서브 세트 만 인쇄하고 있습니다. 생성 된 생성기가 매우 길거나 유효하지 않은 부분 집합이 많은 경우 전체 생성기를 순환하는 것이 거의 낭비입니다. 잘못된 항목 (선행 0이있는 항목)을 발견했을 때 발전기를 멈추고 'allCombinations'를 걸러 낼 방법이 있습니까?

2) 위와 같은 것이 없으면 이러한 조합을 생성하는 더 좋은 방법은 무엇입니까? 선행 제로와의 조합은 무시한다). 발전기를 사용하여

코드 :

import itertools 

def isValid(subset):   ## DIGITS WITH LEADING 0 IS NOT VALID 
    valid = True 
    for num in subset: 
     if num[0] == '0' and len(num) > 1: 
      valid = False 
      break 

    return valid 

def get_combinations(source, comb): 
    res = "" 
    for x, action in zip(source, comb + (0,)): 
     res += x 
     if action == 0: 
      yield res 
      res = "" 

digits = "1023" 
allCombinations = [list(get_combinations(digits, c)) for c in itertools.product((0, 1), repeat=len(digits) - 1)] 


for subset in allCombinations: ## LOOPS THROUGH THE ENTIRE GENERATOR 
    if isValid(subset): 
     print(subset) 
+1

'필터 '내장 함수를 보았습니까? –

+0

@aryamccarthy : 파이썬 2에서'filter'는 열렬한 것이지, 생성기가 아닌리스트를 리턴합니다. – 9000

+3

Python 2에는 지연 평가를위한 itertools.ifilter가 있습니다. Python 2 태그가 표시되지 않습니다. –

답변

2

필터링, 그것을 할 수있는 복합 건물 수준에서보다 효율적으로 수행 할 수 있습니다.

def generate_pieces(input_string, predicate): 
    if input_string: 
     if predicate(input_string): 
      yield [input_string] 
     for item_size in range(1, len(input_string)+1): 
      item = input_string[:item_size] 
      if not predicate(item): 
       continue 
      rest = input_string[item_size:] 
      for rest_piece in generate_pieces(rest, predicate): 
       yield [item] + rest_piece 

너무 오래 그것도 재미 없어, 상처의 모든 조합을 생성 :

>>> list(generate_pieces('10002', lambda x: True)) 
[['10002'], ['1', '0002'], ['1', '0', '002'], ['1', '0', '0', '02'], ['1', '0', '0', '0', '2'], ['1', '0', '00', '2'], ['1', '00', '02'], ['1', '00', '0', '2'], ['1', '000', '2'], ['10', '002'], ['10', '0', '02'], ['10', '0', '0', '2'], ['10', '00', '2'], ['100', '02'], ['100', '0', '2'], ['1000', '2']] 

을 만 그 어디 단편 없습니다 앞에 0이 : 제로이었다 시작

>>> list(generate_pieces('10002', lambda x: not x.startswith('0'))) 
[['10002'], ['1000', '2']] 

하위 문자열을 재귀 적 단계로 고려되지 않았습니다.

+0

예 건물 프로세스 중에 필터링하는 것이 더 효율적입니다.샘플 코드 – user1179317

+0

을 주셔서 감사합니다. 실제로 '0'이 유효합니다. 0을 선두로하는 항목, len이 적어도 2 이상은 '0'을 선두로하는 항목 만 무효입니다. – user1179317

+0

'lambda x : x == '0'또는 x.startswith ('0')'이 작동하지 않습니다. – 9000

0

하나의 일반적인 솔루션은 yield를 사용하기 전에 필터링을 시도하는 것입니다. 난 그냥 항복 전에 당신에게 필터링의 예를 준 :

import itertools 

def my_gen(my_string): 

    # Create combinations 
    for length in range(len(my_string)): 
     for my_tuple in itertools.combinations(my_string, length+1): 

      # This is the string you would like to output 
      output_string = "".join(my_tuple) 

      # filter here: 
      if output_string[0] != '0': 
       yield output_string 


my_string = '1023' 
print(list(my_gen(my_string))) 

편집 : 다른 발전기의 이해에 추가

"앞에 0이없는"와 같은 간단하고 명백한 조건에 대한
import itertools 

my_string = '1023' 
my_gen = ("".join(my_tuple)[0] for length in range(len(my_string)) 
         for my_tuple in itertools.combinations(my_string, length+1) 
         if "".join(my_tuple)[0] != '0') 
+0

당신의 솔루션은 내가 원하는 대답을주지 못하지만 당신은 단지 'yield'이전에 저에게 필터를 보여 주려한다고 생각합니다. 나는 그것을 생각하고 있었지만 나는 'allCombinations'에 대한리스트를 가지고 있기 때문에 그것을 할 수 있다고 생각하지 않는다. 속도에 대한 목록 이해력을 고수하고 싶지만 어쩌면 캔트가 아닐 수도 있습니다. – user1179317

+0

C/C++에서 목록에 대한 공간을 미리 할당하므로 목록 내장이 빠릅니다. 나는 생성자의 내포가 생성자 함수보다 빠르다고 믿지 않는다. 왜냐하면 채울 목록이 없기 때문이다. - 항목은 한 번에 하나씩 계산된다. –