【问题标题】:Python creating a list with itertools.product?Python 用 itertools.product 创建一个列表?
【发布时间】:2012-06-14 22:01:31
【问题描述】:

我正在使用 itertools 从范围列表中创建一个列表,到目前为止我有这个:

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 的范围是否太多而无法构建另一个列表?

【问题讨论】:

  • 如果您试图弄清楚如何将整数 200 划分为从不同集合中抽取的 8 个项,则有更简单的方法来计算 next_list。如果我数正确,您的笛卡尔积有 5768123130 个不同的项目需要迭代,这将需要相当长的时间。
  • 您好帝斯曼,感谢您的回复。我将研究创建一种更有效的方法。
  • J.F.塞巴斯蒂安,谢谢我看到了讨论这个问题和其他类似问题的链接(我已经阅读了关于背包的维基)。我不想先查看其他解决方案,然后再看看我自己是否能找到一个有效的解决方案。现在,我运行的代码与分钟规则相去甚远,而且非常粗暴,我得到了正确的结果,但在找到解决此问题的最佳方法之前,我仍然犹豫不决。
  • @tijko:对不起,我迟到了;请考虑我的回答!

标签: python itertools


【解决方案1】:

最好只使用列表理解

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

【讨论】:

  • 你好小食者,感谢您的回复。我试过这个for i in list(itertools.product(*start_list)): if sum(i) == 200: new_list.append(i) 这也使解释器崩溃。现在即使我需要找到一种不同的方法来解决这个问题,我还是从你的评论中学到了谢谢!
【解决方案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

解决方案我_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 倍也就不足为奇了。


解决方案我_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,从而剥夺了它的任何影响力 - 但是在更一般的数据基础上,它可以大大减少问题的规模。

这里的节省在于连续减少在每个迭代级别(树修剪)考虑的项目数。

【讨论】:

  • 如果您的代码花了 50 分钟编写,我仍然会赢 :)。说真的,我的回答只是解决最初的问题,而不是 1 分钟规则
  • @gnibbler:在我想到短版之前,更像是玩了 3 个小时的长版。
【解决方案3】:

使用这个:

next_list = list(item for item in itertools.product(*start_list) if 总和(项目)== 200)

【讨论】:

  • 哦,当然,我是没有喝够咖啡的人
  • @gnibbler:我自己在第 12 杯。关牡蛎和葡萄酒:)
猜你喜欢
  • 2021-05-13
  • 1970-01-01
  • 1970-01-01
  • 2020-01-01
  • 1970-01-01
  • 2011-02-27
  • 2021-12-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多