如何生成不同列表元素的组合?

How to generate combinations of elements of different lists?

[Python]

我有一个列表列表,如下所示

[[1,2,3],[3,4,5],[5,6,7,8]]

我想创建一个 program/function 来创建类似

的组合
1,3,5
1,3,6
1,3,7
1,3,8
1,4,5
1,4,6
.
.
.

我尝试在互联网上查找,我找不到任何东西。如果有一种方法可以在不使用 itertools 模块的情况下做到这一点,那将更加感激。谢谢。

你可以使用itertools.product

import itertools
a = [[1,2,3],[3,4,5],[5,6,7,8]]
list(itertools.product(*a))
#output
[(1, 3, 5),
 (1, 3, 6),
 (1, 3, 7),
 (1, 3, 8),
 (1, 4, 5),
 (1, 4, 6),
 (1, 4, 7),
 (1, 4, 8),
 (1, 5, 5),
 (1, 5, 6),
 (1, 5, 7),
 (1, 5, 8),
 (2, 3, 5),
 (2, 3, 6),
 (2, 3, 7),
 (2, 3, 8),
 (2, 4, 5),
 (2, 4, 6),
 (2, 4, 7),
 (2, 4, 8),
 (2, 5, 5),
 (2, 5, 6),
 (2, 5, 7),
 (2, 5, 8),
 (3, 3, 5),
 (3, 3, 6),
 (3, 3, 7),
 (3, 3, 8),
 (3, 4, 5),
 (3, 4, 6),
 (3, 4, 7),
 (3, 4, 8),
 (3, 5, 5),
 (3, 5, 6),
 (3, 5, 7),
 (3, 5, 8)]

正如您要求解决方案没有 itertools,这是一个递归函数,它接受任意长度的列表并进行您需要的组合:

def combine(elems):
    if len(elems) == 0:
        return [[]]
    result = []    
    subcombinations =  combine(elems[1:])
    for x in elems[0]:
        for y in subcombinations:
            result.append([x, *y])
    return result

或更短的版本

def combine(elems):
    if len(elems) == 0:
        return [[]]
    return [[x, *y] for x in elems[0] for y in combine(elems[1:])]

这是 documentationitertools.product() 的大致等效实现,以防您 want/need 在不使用库的情况下构建函数。

def product(*args, repeat=1):
    pools = [tuple(pool) for pool in args] * repeat
    result = [[]]
    for pool in pools:
        result = [x+[y] for x in result for y in pool]
    for prod in result:
        yield tuple(prod)

a = [[1,2,3],[3,4,5],[5,6,7,8]]

print(list(product(*a)))

输出:

[(1, 3, 5), (1, 3, 6), (1, 3, 7), (1, 3, 8), (1, 4, 5), (1, 4, 6), (1, 4, 7), (1, 4, 8), (1, 5, 5), (1, 5, 6), (1, 5, 7), (1, 5, 8), (2, 3, 5), (2, 3, 6), (2, 3, 7), (2, 3, 8), (2, 4, 5), (2, 4, 6), (2, 4, 7), (2, 4, 8), (2, 5, 5), (2, 5, 6), (2, 5, 7), (2, 5, 8), (3, 3, 5), (3, 3, 6), (3, 3, 7), (3, 3, 8), (3, 4, 5), (3, 4, 6), (3, 4, 7), (3, 4, 8), (3, 5, 5), (3, 5, 6), (3, 5, 7), (3, 5, 8)]

随意打印:

results = list(product(*a))

print('\n'.join([','.join(list(map(str, res))) for res in results]))

输出:

1,3,5
1,3,6
1,3,7
1,3,8
1,4,5
1,4,6
1,4,7
1,4,8
1,5,5
1,5,6
1,5,7
1,5,8
2,3,5
2,3,6
2,3,7
2,3,8
2,4,5
2,4,6
2,4,7
2,4,8
2,5,5
2,5,6
2,5,7
2,5,8
3,3,5
3,3,6
3,3,7
3,3,8
3,4,5
3,4,6
3,4,7
3,4,8
3,5,5
3,5,6
3,5,7
3,5,8

以下是如何使用 recursive function:

def comb(arrs, i=0, numbs=[]):
    if i == len(arrs):
        print(*numbs)
        return
    for j in arrs[i]:
        comb(arrs, i + 1, numbs + [j])

arrs = [[1,2,3],[3,4,5],[5,6,7,8]]
comb(arrs)

输出:

1 3 5
1 3 6
1 3 7
1 3 8
1 4 5
1 4 6
1 4 7
1 4 8
1 5 5
1 5 6
1 5 7
1 5 8
2 3 5
2 3 6
2 3 7
2 3 8
2 4 5
2 4 6
2 4 7
2 4 8
2 5 5
2 5 6
2 5 7
2 5 8
3 3 5
3 3 6
3 3 7
3 3 8
3 4 5
3 4 6
3 4 7
3 4 8
3 5 5
3 5 6
3 5 7
3 5 8

将其转换为线性列表并使用组合

from itertools import combinations
linearArr = [ele for ele in row for row in [[1,2,3],[3,4,5],[5,6,7,8]]]
for comb in combinations(linearArr,3):
    print(comb)

我尝试了两种不同的方法,直接列表理解和递归函数。

列表理解法是最简单的,但假设您的列表只有 3 个元素。如果您将第四组数字添加到您的主列表中,则需要调整列表理解以适应它。

相比之下,递归函数更复杂,但可以处理无限数量的列表,其中 sub-lists 具有任意数量的元素。当然,它是递归的,因此会达到一个限制,并且处理列表列表所需的时间将随着每个附加数字列表的增加而呈指数增长。

列表理解

lol = [
    [1,2,3],
    [3,4,5],
    [5,6,7,8]
]
o = [[x, y, z] for x in lol[0] for y in lol[1] for z in lol[2]]
print(repr(o))

输出

[
  [1, 3, 5],
  [1, 3, 6],
  [1, 3, 7],
  [1, 3, 8],
  [1, 4, 5],
  ...
  [3, 5, 6],
  [3, 5, 7],
  [3, 5, 8]
]

递归函数

def stich(l: list) -> list:
    def merge(a, b):
        if not a:
            return [[y,] for y in b]
        return [x + [y,] for x in a for y in b]
    ol = []
    tl = l.copy()
    while tl:
        ol = merge(ol, tl.pop(0))
    return ol


stiched_list = stich(lol)
print(repr(stiched_list))

输出

[
  [1, 3, 5],
  [1, 3, 6],
  [1, 3, 7],
  [1, 3, 8],
  [1, 4, 5],
  ...
  [3, 5, 6],
  [3, 5, 7],
  [3, 5, 8]
]