11

範囲のリストから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の範囲が多すぎて、別のリストを作成できませんか?

4

3 に答える 3

19

リスト内包表記を使用する方が良い

new_list = [item for item in itertools.product(*start_list) if sum(item) == 200]
于 2012-06-12T02:00:05.060 に答える
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))

最初に最長のリストを渡すと、これがはるかに高速になる可能性があることに注意してください。

このソリューションは、1レベルの反復をセットルックアップに置き換えます。最長のリストに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の両方が含まれているため、レバレッジが失われますが、より一般的なデータでは、問題のサイズを大幅に減らすことができます。

ここでの節約は、反復の各レベル(ツリーの剪定)で考慮されるアイテムの数を連続的に減らすことにあります。

于 2012-06-12T17:53:39.160 に答える
1

これを使って:

next_list = list(item for item in itertools.product(* start_list)if sum(item)== 200)

于 2012-06-12T01:45:29.763 に答える