2012-06-12 66 views
7

我创建与范围的列表itertools名单,到目前为止,我有这样的:Python创建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)] 

现在,我知道,如果我尝试运行此下一行会杀了我的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。如果我计算正确,你的Cartesian产品有5768123130个不同的项目需要迭代,这需要很长时间。 – DSM

+0

嗨帝斯曼,谢谢你的回应。我会考虑创建一个更有效的方法。 – 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

嘿gnibbler,感谢您的回应。我已经尝试过这个'对于列表中的我(itertools.product(* start_list)):如果sum(i)== 200:new_list.append(i)'这也会导致解释器崩溃。现在,尽管我需要找到一种解决这个问题的不同方式,但我从您的评论中学到了很多谢意! – tijko

1

使用此:

next_list =列表(itertools.product(对于逐项* START_LIST)如果 总和(项目)== 200)

+0

@gnibbler:我认为你误读了变量名称;) –

+0

哦,当然,_I_m_没有喝够咖啡的人 –

+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