我有一些排序/评分的参数列表。我想生成可能的参数组合(笛卡尔积)。但是,如果参数的数量很大,这很快(很快!!)变成一个非常大的数字。基本上,我想做笛卡尔产品,但要提前停止。
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左右?取决于结果)。因此,如果少于截止值,则最终应生成所有截止值。最好每次只产生一次。
答案 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
这将以随机顺序为您提供产品列表。