以不同的顺序生成itertools.product

时间:2017-02-17 02:10:42

标签: python algorithm itertools

我有一些排序/评分的参数列表。我想生成可能的参数组合(笛卡尔积)。但是,如果参数的数量很大,这很快(很快!!)变成一个非常大的数字。基本上,我想做笛卡尔产品,但要提前停止。

import itertools
parameter_options = ['1234',
                     '123',
                     '1234']
for parameter_set in itertools.product(*parameter_options):
    print ''.join(parameter_set)

产生

111
112
113
114
121
122
123
124
131
132
133
134
...

我想生成(或类似的东西):

111
112
121
211
122
212
221
222
...

因此,如果我提早停止,我至少会得到一些好的"一组参数,其中一组好的参数主要来自列表的早期。这个特殊的顺序会很好,但我对任何改变"下一个排列"选择顺序。我希望从列表前面生成大部分项目所产生的早期结果,但并不关心解决方案是先生成113或122,还是211或112先生成。

我的计划是在产生一些排列后停止(可能是10K左右?取决于结果)。因此,如果少于截止值,则最终应生成所有截止值。最好每次只产生一次。

3 个答案:

答案 0 :(得分:2)

如果您根据输出空间的图形遍历来考虑输出,我认为您可以按照您想要的顺序获得结果。您需要最近的第一次遍历,而itertools.product函数是深度优先遍历。

尝试这样的事情:

import heapq

def nearest_first_product(*sequences):
    start = (0,)*len(sequences)
    queue = [(0, start)]
    seen = set([start])
    while queue:
        priority, indexes = heapq.heappop(queue)
        yield tuple(seq[index] for seq, index in zip(sequences, indexes))
        for i in range(len(sequences)):
            if indexes[i] < len(sequences[i]) - 1:
                lst = list(indexes)
                lst[i] += 1
                new_indexes = tuple(lst)
                if new_indexes not in seen:
                    new_priority = sum(index * index for index in new_indexes)
                    heapq.heappush(queue, (new_priority, new_indexes))
                    seen.add(new_indexes)

示例输出:

for tup in nearest_first_product(range(1, 5), range(1, 4), range(1, 5)):
    print(tup)

(1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(2, 1, 1)
(1, 2, 2)
(2, 1, 2)
(2, 2, 1)
(2, 2, 2)
(1, 1, 3)
(1, 3, 1)
(3, 1, 1)
(1, 2, 3)
(1, 3, 2)
(2, 1, 3)
(2, 3, 1)
(3, 1, 2)
(3, 2, 1)
(2, 2, 3)
(2, 3, 2)
(3, 2, 2)
(1, 3, 3)
(3, 1, 3)
(3, 3, 1)
(1, 1, 4)
(2, 3, 3)
(3, 2, 3)
(3, 3, 2)
(4, 1, 1)
(1, 2, 4)
(2, 1, 4)
(4, 1, 2)
(4, 2, 1)
(2, 2, 4)
(4, 2, 2)
(3, 3, 3)
(1, 3, 4)
(3, 1, 4)
(4, 1, 3)
(4, 3, 1)
(2, 3, 4)
(3, 2, 4)
(4, 2, 3)
(4, 3, 2)
(3, 3, 4)
(4, 3, 3)
(4, 1, 4)
(4, 2, 4)
(4, 3, 4)

通过更改代码中new_priority的计算,您可以获得一堆略有不同的订单。当前版本使用平方笛卡尔距离作为优先级,但如果您愿意,可以使用其他值(例如,包含序列中的值,而不仅仅是索引)。

如果你不关心(1, 1, 3)是否在(1, 2, 2)之前出现(只要它们都来自(1, 1, 2)(1, 2, 1)(2, 1, 1) ),你可以做一个广度优先的遍历而不是最近的遍历。这会更简单一些,因为您可以使用常规队列(如collections.deque)而不是优先级队列。

此类遍历所使用的队列意味着此代码使用了一定量的内存。但是,内存量远远少于在将它们按顺序排列之前必须预先生成结果的情况。使用的最大内存与结果空间的表面积成比例,而不是与其体积成比例。

答案 1 :(得分:2)

你的问题有点暧昧,但是阅读你的评论和其他答案,似乎你想要一个笛卡尔产品实现广泛搜索而不是深度搜索。

最近我有同样的需求,但也要求它不会将中间结果存储在内存中。这对我来说非常重要,因为我正在处理大量参数(因此是一个非常大的笛卡尔积),任何存储值或执行递归调用的实现都是不可行的。正如你在问题中所述,这似乎也是你的情况。

由于我没有找到满足此要求的答案,我找到了这个解决方案:

def product(*sequences):
    '''Breadth First Search Cartesian Product'''
    # sequences = tuple(tuple(seq) for seqin sequences)

    def partitions(n, k):
        for c in combinations(range(n+k-1), k-1):
            yield (b-a-1 for a, b in zip((-1,)+c, c+(n+k-1,)))

    max_position = [len(i)-1 for i in sequences]
    for i in range(sum(max_position)):
        for positions in partitions(i, len(sequences)):
            try:
                yield tuple(map(lambda seq, pos: seq[pos], sequences, positions))
            except IndexError:
                continue
    yield tuple(map(lambda seq, pos: seq[pos], sequences, max_position))

就速度而言,这台发电机在开始时运行良好,但在最新结果中开始变慢。因此,虽然这个实现有点慢,但它可以作为不使用内存但不提供重复值的生成器。

正如我在@Blckknght回答中提到的,这里的参数也必须是序列(可订阅和长度定义的迭代)。但是你也可以通过取消注释第一行来绕过这个限制(牺牲一点内存)。如果您使用generator / iterators作为参数,这可能很有用。

我希望我能帮助你,让我知道这是否有助于解决你的问题。

答案 2 :(得分:0)

这个解决方案可能并不是最好的,因为它会简单地将每个组合强制进入内存,但它确实有效。对于大型数据集,可能需要一些时间。

del

这将以随机顺序为您提供产品列表。