更改`itertools.product`迭代顺序

时间:2018-04-23 21:16:28

标签: python itertools

给定一些迭代,itertools.product从后到前迭代,在推进倒数第二个迭代之前尝试最后一个迭代的所有选择,并在推进第三个迭代之前尝试最后两个迭代的所有选择-last iterable等。例如,

>>> list(itertools.product([2,1,0],['b','c','a']))
[(2, 'b'), (2, 'c'), (2, 'a'), (1, 'b'), (1, 'c'), (1, 'a'), (0, 'b'), (0, 'c'), (0, 'a')]

我想以不同的方式迭代产品:应该产生元组的顺序是它们包含的元素的索引的总和,即,在产生元素之前的元素之前。它们各自的迭代中的索引总和为k,产生其元素的所有元组。各自的迭代中的指数总和为k-1。例如,在生成包含每个iterable的第一个元素(索引0)的元组之后,生成的下一个元组应该包含来自单个iterable的第二个元素和来自其余的第一个元素。之后,生成的元组应包含来自一个元组的第三个元素或来自两个元组的第二个元素等。使用上面的例子,

>>> my_product([2,1,0],['b','c','a'])
[(2, 'b'),                     # element 0 from both iterables
 (2, 'c'), (1, 'b'),           # elements 0,1 and 1,0 (sums to 1)
 (2, 'a'), (1, 'c'), (0, 'b'), # elements 0,2 and 1,1 and 2,0 (sums to 2)
 (1, 'a'), (0, 'c'),           # elements 1,2 and 2,1 (sums to 3)
 (0, 'a')]                     # elements 2,2 (sums to 4)

3 个答案:

答案 0 :(得分:1)

通过排序解决了这个问题:

def my_product(*args):
    return [tuple(i[1] for i in p) for p in
        sorted(itertools.product(*map(enumerate, args)),
            key=lambda x: (sum(y[0] for y in x), x))]

测试:

>>> my_product([0,1,2],[3,4,5])
[(0, 3), 
 (0, 4), (1, 3), 
 (0, 5), (1, 4), (2, 3), 
 (1, 5), (2, 4), 
 (2, 5)]

也适用于未排序的非数字项:

>>> my_product(['s0','b1','k2'],['z3','a4','c5'])
[('s0', 'z3'), 
 ('s0', 'a4'), ('b1', 'z3'), 
 ('s0', 'c5'), ('b1', 'a4'), ('k2', 'z3'), 
 ('b1', 'c5'), ('k2', 'a4'), 
 ('k2', 'c5')]

>>> my_product([2,1,0],['b','c','a'])
[(2, 'b'), 
 (2, 'c'), (1, 'b'), 
 (2, 'a'), (1, 'c'), (0, 'b'), 
 (1, 'a'), (0, 'c'), 
 (0, 'a')]

并且有多个参数:

>>> my_product([2,1,0],['b','c','a'],['x','y','z'])
[(2, 'b', 'x'), 
 (2, 'b', 'y'), (2, 'c', 'x'), (1, 'b', 'x'), 
 (2, 'b', 'z'), (2, 'c', 'y'), (2, 'a', 'x'), (1, 'b', 'y'), (1, 'c', 'x'), (0, 'b', 'x'), 
 (2, 'c', 'z'), (2, 'a', 'y'), (1, 'b', 'z'), (1, 'c', 'y'), (1, 'a', 'x'), (0, 'b', 'y'), (0, 'c', 'x'), 
 (2, 'a', 'z'), (1, 'c', 'z'), (1, 'a', 'y'), (0, 'b', 'z'), (0, 'c', 'y'), (0, 'a', 'x'), 
 (1, 'a', 'z'), (0, 'c', 'z'), (0, 'a', 'y'),
 (0, 'a', 'z')]

答案 1 :(得分:0)

鉴于你需要检查这笔费用为什么不要'你只需使用sort:

def my_product(*args):
    return list(sorted(itertools.product(*args), key=lambda x: (sum(x), x)))

答案 2 :(得分:0)

作为排序的替代方法,此解决方案会对itertools.product()的结果进行多次传递。

请注意,它使用与其他答案相同的decorate-manipulate-undecorate模式。

import itertools

# TESTED on Python3
def my_product(*args):
    args = [list(enumerate(arg)) for arg in args]
    for sum_indexes in range(sum(len(item) for item in args)):
        for partial in itertools.product(*args):
            indexes, values = zip(*partial)
            if sum(indexes) == sum_indexes:
                yield values

assert list(my_product([2,1,0],['b','c','a'])) == [
    (2, 'b'),                     # element 0 from both iterables
    (2, 'c'), (1, 'b'),           # elements 0,1 and 1,0 (sums to 1)
    (2, 'a'), (1, 'c'), (0, 'b'), # elements 0,2 and 1,1 and 2,0 (sums to 2)
    (1, 'a'), (0, 'c'),           # elements 1,2 and 2,1 (sums to 3)
    (0, 'a')]