Python - 从长度不等的列表中获取替换所有唯一组合

时间:2018-01-22 05:41:15

标签: python list combinations

注意:这不是一个重复的问题,因为标题可能会说

如果我有一个列表清单,我需要从中获取所有组合并进行替换。

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个问题:

  1. 对于大型列表,itertools.product会抛出MemoryError。当l有18个3长的子列表给予~400mil的组合时。

  2. 订单很重要,因此sorted对我的问题不起作用。这可能会让一些人感到困惑,因此可以通过下面的例子进行解释。

    l = [[1,2,3], [1], [1,2,3]]

  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

    希望这个例子有所帮助。

3 个答案:

答案 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)]

如果您不想排序,请考虑将frozensetcollections.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()作为第一种方法。

相关问题