2012-06-12 4 views
7

지금까지 나는이 있고, 범위 목록에서 itertools과 목록을 만드는거야 : 이제itertools.product로 목록을 만드는 파이썬?

start_list = [xrange(0,201,1),xrange(0,201,2),xrange(0,201,5),xrange(0,201,10),xrange(0,201,20),xrange(0,201,50),xrange(0,201,100),xrange(0,201,200)] 

을, 나는이 다음 라인을 실행하려고한다면 내 파이썬 인터프리터를 죽일 것이라는 점을 알고있다 :

next_list = list(itertools.product(*start_list)) 

내가 그것 항목의 합에 대한 각 튜플을 확인하는 인수에 넣을 수있을 것입니다 궁금하네요과 동일한 경우에만 일정 금액에 next_list에 묻어 요?

아마 같은 것을 :

next_list = list(itertools.product(*start_list,sum(tuples)=200)) 

나는 이것이 옳지 않아 알고 나는 이것에 대해 갈거야 방법을 다시 생각하기 시작해야 할 수도 있습니다. 발전기의 start_list의 범위가 너무 많아서 다른 목록을 작성하지 못합니까?

+4

정수 200을 다른 집합에서 나온 8 개의 항으로 분할하는 방법을 알아 내려고한다면 next_list를 계산하는 더 쉬운 방법이 있습니다. 내가 직면하는 Cartesian 제품에 반복되는 5768123130 개의 별개 항목이 있습니다. 시간이 오래 걸릴 것입니다. – DSM

+0

안녕 DSM, 응답 해 주셔서 감사합니다. 나는보다 효율적인 방법을 모색 할 것이다. – tijko

+0

관련 : http://stackoverflow.com/questions/1106929/find-all-combinations-of-coins-when-given-some-dollar-value – jfs

답변

12

더 나은 단지 목록을 사용하는 이해

new_list = [item for item in itertools.product(*start_list) if sum(item) == 200] 
+0

당신의 해결책은 의도를 훨씬 명확하게 만듭니다. –

+0

안녕하세요. 응답 해 주셔서 감사합니다. 나는 이것을 list (itertools.product (* start_list))에 넣었다 : if sum (i) == 200 : new_list.append (i)'또한 인터프리터를 추락시켰다. 지금은이 문제를 해결하는 다른 방법을 찾아야하지만, 나는 당신의 의견 덕분에 배웠습니다! – tijko

1

사용이 :

next_list = 목록 (itertools.product (의 항목에 대한 항목 * START_LIST) 합 (항목 경우) == 200)

+0

@gnibbler : 변수 이름을 잘못 읽은 것 같아요.) –

+0

아 물론 이죠. 나는 충분한 커피를 마시지 않은 분이 십니다. –

+0

@gnibbler : 저는 제 12 잔 컵에 있습니다. 굴과 와인을 위해 : –

2
Solution  Runtime   Fn calls   Lines of Code 
-------- ---------- ------------------------ ------------- 
gnibbler 2942.627 s 1473155845 (1.5 billion)   1 
me_A   16.639 s  28770812 (29 million)   5 
me_B   0.452 s  774005 (.8 million)   43 

솔루션 me_A :

import itertools 

def good_combos(basis, addto): 
    good_sums = set(addto-b for b in basis[0]) 
    return ([addto-sum(items)]+list(items) for items in itertools.product(*basis[1:]) if sum(items) in good_sums) 

next_list = list(good_combos(start_list, 200)) 

당신이 그것을 먼저 긴 목록을 전달하면이훨씬 빠르게 할 수 있습니다 마십시오.

이 솔루션은 한 수준의 반복을 세트 조회로 바꿉니다. 가장 긴 목록에 200 개의 항목이 포함되어 있어도 이것이 거의 200 배 더 빠르다는 것은 놀라운 일이 아닙니다.


솔루션 me_B : 그러나 일반적인 기준으로 - - 각 목록은 어떤 활용의 그것을 박탈, 모두 0 addto을 포함

import itertools 
from bisect import bisect_left, bisect_right 

def good_combos(addto=0, *args): 
    """ 
    Generate all combinations of items from a list of lists, 
    taking one item from each list, such that sum(items) == addto. 

    Items will only be used if they are in 0..addto 

    For speed, try to arrange the lists in ascending order by length. 
    """ 
    if len(args) == 0:       # no lists passed? 
     return [] 

    args = [sorted(set(arg)) for arg in args] # remove duplicate items and sort lists in ascending order 
    args = do_min_max(args, addto)    # use minmax checking to further cull lists 

    if any(len(arg)==0 for arg in args):  # at least one list no longer has any valid items? 
     return [] 

    lastarg = set(args[-1]) 
    return gen_good_combos(args, lastarg, addto) 

def do_min_max(args, addto, no_negatives=True): 
    """ 
    Given 
     args   a list of sorted lists of integers 
     addto   target value to be found as the sum of one item from each list 
     no_negatives if True, restrict values to 0..addto 

    Successively apply min/max analysis to prune the possible values in each list 

    Returns the reduced lists 
    """ 
    minsum = sum(arg[0] for arg in args) 
    maxsum = sum(arg[-1] for arg in args) 

    dirty = True 
    while dirty: 
     dirty = False 
     for i,arg in enumerate(args): 
      # find lowest allowable value for this arg 
      minval = addto - maxsum + arg[-1] 
      if no_negatives and minval < 0: minval = 0 
      oldmin = arg[0] 
      # find highest allowable value for this arg 
      maxval = addto - minsum + arg[0] 
      if no_negatives and maxval > addto: maxval = addto 
      oldmax = arg[-1] 

      if minval > oldmin or maxval < oldmax: 
       # prune the arg 
       args[i] = arg = arg[bisect_left(arg,minval):bisect_right(arg,maxval)] 
       minsum += arg[0] - oldmin 
       maxsum += arg[-1] - oldmax 
       dirty = True 
    return args 

def gen_good_combos(args, lastarg, addto): 
    if len(args) > 1: 
     vals,args = args[0],args[1:] 
     minval = addto - sum(arg[-1] for arg in args) 
     maxval = addto - sum(arg[0] for arg in args) 
     for v in vals[bisect_left(vals,minval):bisect_right(vals,maxval)]: 
      for post in gen_good_combos(args, lastarg, addto-v): 
       yield [v]+post 
    else: 
     if addto in lastarg: 
      yield [addto] 

basis = reversed(start_list) # more efficient to put longer params at end 
new_list = list(good_combos(200, *basis)) 

do_min_max()는 실제로 데이터 세트에 아무것도 성취 할 수 없습니다 데이터를 사용하면 문제의 크기를 크게 줄일 수 있습니다.

여기서 절약은 반복의 각 레벨 (트리 가지 치기)에서 고려되는 항목 수를 연속적으로 줄이는 데 있습니다.

+0

코드 작성에 50 분이 걸리는 경우에도 여전히 승리 할 것입니다. :) 진심으로 내 대답은 단지 1 분 규칙이 아니라 원래의 문제를 해결했습니다. –

+1

@gnibbler : 짧은 버전이 나에게 발생하기 전에 긴 버전으로 놀기 3 시간 더 필요합니다. –

+0

두 가지 모두 매우 도움이됩니다. 나는 두 가지로부터 모두 배우게 될 것입니다 : D – tijko