2012-06-12 10 views
7

私はこれまでのところ、私はこれ持って、範囲のリストからitertoolsでリストを作成しています:今itertools.productでリストを作成するPython?

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)] 

を、私はこの次の行を実行しようとした場合、それは私のPythonインタプリタを殺すことを知っています:私は思ったんだけど何

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を計算する簡単な方法があります。私が正しいと考えるならば、あなたのデカルト製品には、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

ねえ、お返事ありがとうございます。私はリストの中でこれを試していました(itertools.product(* start_list)):もし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:

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とADDTOの両方が含まれている - しかし、より一般的にデータを使用すると、問題のサイズを大幅に縮小できます。

ここでの節約は、反復の各レベル(ツリープルーニング)で考慮される項目の数を連続的に減らすことにあります。

+0

あなたのコードが書き込みに50分かかる場合、私はまだ勝つだろう:)。真剣に私の答えは、ちょうど1分のルールではなく、元の問題に対処していた –

+1

@gnibbler:私に短いバージョンが発生する前に長いバージョンで遊んでの3時間以上のような。 –

+0

両方とも非常に役に立ちます、私は両方から学びます:D – tijko

関連する問題