注意:这不是一个重复的问题,因为标题可能会说
如果我有一个列表清单,我需要从中获取所有组合并进行替换。
import itertools
l = [[1,2,3] ,[1,2,3], [1,2,3]]
n = []
for i in itertools.product(*l):
if sorted(i) not in n:
n.append(sorted(i))
for i in n:
print(i)
[1, 1, 1]
[1, 1, 2]
[1, 1, 3]
[1, 2, 2]
[1, 2, 3]
[1, 3, 3]
[2, 2, 2]
[2, 2, 3]
[2, 3, 3]
[3, 3, 3]
感谢@RoadRunner和@Idlehands。
以上代码完美有2个问题:
对于大型列表,itertools.product会抛出MemoryError。当l有18个3长的子列表给予~400mil的组合时。
订单很重要,因此sorted
对我的问题不起作用。这可能会让一些人感到困惑,因此可以通过下面的例子进行解释。
l = [[1,2,3], [1], [1,2,3]]
这里我有两个独特的群体:
Group1:元素0,2具有相同的值[1,2,3]
第2组:元素1,其值为[1]
因此,我需要的解决方案是:
[1,1,1]
[1,1,2]
[1,1,3]
[2,1,2]
[2,1,3]
[3,1,3]
因此,地理位置1
已修复为1
。
希望这个例子有所帮助。
答案 0 :(得分:4)
编辑答案:
根据新信息,为了处理过多的itertools.product()
组合,我们可以尝试小批量提取列表:
from itertools import product
l = [list(range(3))]*18
prods = product(*l)
uniques = set()
results = []
totals = 0
def run_batch(n=1000000):
for i in range(n):
try:
result = next(prods)
except StopIteration:
break
unique = tuple(sorted(result))
if unique not in uniques:
uniques.add(unique)
results.append(result)
global totals
totals += i
run_batch()
print('Total iteration this batch: {0}'.format(totals))
print('Number of unique tuples: {0}'.format(len(uniques)))
print('Number of wanted combos: {0}'.format(len(results)))
<强>输出:强>
Total iteration this batch: 999999
Number of unique tuples: 103
Number of wanted combos: 103
First 10 results:
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2)
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1)
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2)
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2)
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1)
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2)
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2)
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2)
在这里,我们可以通过您选择的范围调用next(prod)
来控制批量大小,并按您认为合适的方式继续。 uniques
将一组中的元组作为参考点排序,results
按照您想要的正确顺序排列。当我使用3 ^ 18的列表运行时,两个大小应该相同并且非常小。我不熟悉内存分配,但这样程序不应该将所有不需要的结果存储在内存中,因此您应该有更多的摆动空间。否则,您始终可以选择将results
导出到文件以腾出空间。显然,此示例仅显示列表的长度,但您可以根据自己的目的轻松显示/保存该列表。
我不能说这是最好的方法或最优化的方法,但它似乎对我有用。也许它会为你工作?该批次花费约10秒钟运行5次(每批平均约2次)。整套prods
花了我15分钟的时间来运行:
Total iteration: 387420102
Number of unique tuples: 190
Number of wanted combos: 190
原始答案:
@RoadRunner had a neat solution with sort()
and defaultdict
,但我觉得不需要后者。我利用他的sort()
建议并在此处实施了修改后的版本。
来自this answer:
l = [[1] ,[1,2,3], [1,2,3]]
n = []
for i in itertools.product(*l):
if sorted(i) not in n:
n.append(sorted(i))
for i in n:
print(i)
<强>输出:强>
[1, 1, 1]
[1, 1, 2]
[1, 1, 3]
[1, 2, 2]
[1, 2, 3]
[1, 3, 3]
答案 1 :(得分:4)
对于短输入序列,可以通过将itertools.product
的输出过滤为唯一值来完成。一种未经过优化的方式是set(tuple(sorted(t)) for t in itertools.product(*l))
,如果您愿意,可以转换为list
。
如果你有足够的笛卡尔产品粉丝,这太低效了,如果你的输入示例将子列表显示为已排序的是你可以依赖的东西,你可以借用文档对{{3}的讨论中的一个注释并过滤掉未排序的值:
permutations()的代码也可以表示为product()的子序列,经过筛选以排除具有重复元素的条目(来自输入池中相同位置的条目)
因此,您需要快速测试值是否已排序,类似于以下答案:
permutations
然后list(t for t in itertools.product(*l) if is_sorted(t))
除此之外,我认为你必须进入递归或l
的固定长度。
答案 2 :(得分:4)
如何使用collections.defaultdict
以不同顺序对具有相同元素的序列进行分组,然后从每个键中选择第一个元素:
from itertools import product
from collections import defaultdict
l = [[1] ,[1,2,3], [1,2,3]]
d = defaultdict(list)
for x in product(*l):
d[tuple(sorted(x))].append(x)
print([x[0] for x in d.values()])
给出了:
[(1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 2, 2), (1, 2, 3), (1, 3, 3)]
或者,这也可以通过保留一组已添加的内容来完成:
from itertools import product
l = [[1] ,[1,2,3], [1,2,3]]
seen = set()
combs = []
for x in product(*l):
curr = tuple(sorted(x))
if curr not in seen:
combs.append(x)
seen.add(curr)
print(combs)
# [(1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 2, 2), (1, 2, 3), (1, 3, 3)]
如果您不想排序,请考虑将frozenset
与collections.Counter()
一起使用:
from collections import Counter
from itertools import product
l = [[1] ,[1,2,3], [1,2,3]]
seen = set()
combs = []
for x in product(*l):
curr = frozenset(Counter(x).items())
if curr not in seen:
seen.add(curr)
combs.append(x)
print(combs)
# [(1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 2, 2), (1, 2, 3), (1, 3, 3)]
注意:如果您不想使用setdefault()
,也可以使用defaultdict()
作为第一种方法。