Python 得到一系列列表的笛卡尔积?
如何从一组列表中获得笛卡尔积(每个可能的值组合) 输入:Python 得到一系列列表的笛卡尔积?,python,list,cartesian-product,Python,List,Cartesian Product,如何从一组列表中获得笛卡尔积(每个可能的值组合) 输入: somelists = [ [1, 2, 3], ['a', 'b'], [4, 5] ] 期望输出: [(1, 'a', 4), (1, 'a', 5), (1, 'b', 4), (1, 'b', 5), (2, 'a', 4), (2, 'a', 5) ...] 可从Python2.6获得 import itertools somelists = [ [1, 2, 3], ['a', 'b'],
somelists = [
[1, 2, 3],
['a', 'b'],
[4, 5]
]
期望输出:
[(1, 'a', 4), (1, 'a', 5), (1, 'b', 4), (1, 'b', 5), (2, 'a', 4), (2, 'a', 5) ...]
可从Python2.6获得
import itertools
somelists = [
[1, 2, 3],
['a', 'b'],
[4, 5]
]
for element in itertools.product(*somelists):
print(element)
这和,
for element in itertools.product([1, 2, 3], ['a', 'b'], [4, 5]):
print(element)
与:
在Python2.6及更高版本中,可以使用“itertools.product”。在较旧版本的Python中,您可以使用以下(几乎——请参阅文档)等效项,至少作为起点:
def product(*args, **kwds):
# product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
# product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
pools = map(tuple, args) * kwds.get('repeat', 1)
result = [[]]
for pool in pools:
result = [x+[y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
两者的结果都是一个迭代器,因此如果您确实需要一个列表进行进一步的处理,请对Python 2.5及更早版本使用
list(result)
:
>>> [(a, b, c) for a in [1,2,3] for b in ['a','b'] for c in [4,5]]
[(1, 'a', 4), (1, 'a', 5), (1, 'b', 4), (1, 'b', 5), (2, 'a', 4),
(2, 'a', 5), (2, 'b', 4), (2, 'b', 5), (3, 'a', 4), (3, 'a', 5),
(3, 'b', 4), (3, 'b', 5)]
下面是product()
的递归版本(只是一个示例):
例如:
>>> list(product([1,2,3], ['a','b'], [4,5]))
[(1, 'a', 4), (1, 'a', 5), (1, 'b', 4), (1, 'b', 5), (2, 'a', 4),
(2, 'a', 5), (2, 'b', 4), (2, 'b', 5), (3, 'a', 4), (3, 'a', 5),
(3, 'b', 4), (3, 'b', 5)]
>>> list(product([1,2,3]))
[(1,), (2,), (3,)]
>>> list(product([]))
[]
>>> list(product())
[()]
这是一个递归生成器,它不存储任何临时列表
def product(ar_list):
if not ar_list:
yield ()
else:
for a in ar_list[0]:
for prod in product(ar_list[1:]):
yield (a,)+prod
print list(product([[1,2],[3,4],[5,6]]))
输出:
[(1, 3, 5), (1, 3, 6), (1, 4, 5), (1, 4, 6), (2, 3, 5), (2, 3, 6), (2, 4, 5), (2, 4, 6)]
对已经说过的话再补充一点:如果你使用sympy,你可以使用符号而不是字符串,这使得它们在数学上很有用
import itertools
import sympy
x, y = sympy.symbols('x y')
somelist = [[x,y], [1,2,3], [4,5]]
somelist2 = [[1,2], [1,2,3], [4,5]]
for element in itertools.product(*somelist):
print element
关于。我会使用列表理解:
somelists = [
[1, 2, 3],
['a', 'b'],
[4, 5]
]
cart_prod = [(a,b,c) for a in somelists[0] for b in somelists[1] for c in somelists[2]]
对上述可变风格的递归生成器解决方案进行了细微修改:
def product_args(*args):
if args:
for a in args[0]:
for prod in product_args(*args[1:]) if args[1:] else ((),):
yield (a,) + prod
当然还有一个包装器,它的工作原理与该解决方案完全相同:
def product2(ar_list):
"""
>>> list(product(()))
[()]
>>> list(product2(()))
[]
"""
return product_args(*ar_list)
使用一个权衡:它检查递归是否应该在每个外部循环上中断,一个增益:空调用时不产生收益,例如产品(())
,我认为这在语义上更正确(请参阅doctest)
关于列表理解:数学定义适用于任意数量的参数,而列表理解只能处理已知数量的参数。虽然已经有很多答案,但我想分享我的一些想法: 迭代法 递归方法 Lambda法
递归方法:
def rec_cart(start, array, partial, results):
if len(partial) == len(array):
results.append(partial)
return
for element in array[start]:
rec_cart(start+1, array, partial+[element], results)
rec_res = []
some_lists = [[1, 2, 3], ['a', 'b'], [4, 5]]
rec_cart(0, some_lists, [], rec_res)
print(rec_res)
def itr_cart(array):
results = [[]]
for i in range(len(array)):
temp = []
for res in results:
for element in array[i]:
temp.append(res+[element])
results = temp
return results
some_lists = [[1, 2, 3], ['a', 'b'], [4, 5]]
itr_res = itr_cart(some_lists)
print(itr_res)
def giveAllLists(a, t):
if (t + 1 == len(a)):
x = []
for i in a[t]:
p = [i]
x.append(p)
return x
x = []
out = giveAllLists(a, t + 1)
for i in a[t]:
for j in range(len(out)):
p = [i]
for oz in out[j]:
p.append(oz)
x.append(p)
return x
xx= [[1,2,3],[22,34,'se'],['k']]
print(giveAllLists(xx, 0))
迭代方法:
def rec_cart(start, array, partial, results):
if len(partial) == len(array):
results.append(partial)
return
for element in array[start]:
rec_cart(start+1, array, partial+[element], results)
rec_res = []
some_lists = [[1, 2, 3], ['a', 'b'], [4, 5]]
rec_cart(0, some_lists, [], rec_res)
print(rec_res)
def itr_cart(array):
results = [[]]
for i in range(len(array)):
temp = []
for res in results:
for element in array[i]:
temp.append(res+[element])
results = temp
return results
some_lists = [[1, 2, 3], ['a', 'b'], [4, 5]]
itr_res = itr_cart(some_lists)
print(itr_res)
def giveAllLists(a, t):
if (t + 1 == len(a)):
x = []
for i in a[t]:
p = [i]
x.append(p)
return x
x = []
out = giveAllLists(a, t + 1)
for i in a[t]:
for j in range(len(out)):
p = [i]
for oz in out[j]:
p.append(oz)
x.append(p)
return x
xx= [[1,2,3],[22,34,'se'],['k']]
print(giveAllLists(xx, 0))
巨石阵方法:
def rec_cart(start, array, partial, results):
if len(partial) == len(array):
results.append(partial)
return
for element in array[start]:
rec_cart(start+1, array, partial+[element], results)
rec_res = []
some_lists = [[1, 2, 3], ['a', 'b'], [4, 5]]
rec_cart(0, some_lists, [], rec_res)
print(rec_res)
def itr_cart(array):
results = [[]]
for i in range(len(array)):
temp = []
for res in results:
for element in array[i]:
temp.append(res+[element])
results = temp
return results
some_lists = [[1, 2, 3], ['a', 'b'], [4, 5]]
itr_res = itr_cart(some_lists)
print(itr_res)
def giveAllLists(a, t):
if (t + 1 == len(a)):
x = []
for i in a[t]:
p = [i]
x.append(p)
return x
x = []
out = giveAllLists(a, t + 1)
for i in a[t]:
for j in range(len(out)):
p = [i]
for oz in out[j]:
p.append(oz)
x.append(p)
return x
xx= [[1,2,3],[22,34,'se'],['k']]
print(giveAllLists(xx, 0))
输出:
[[1, 22, 'k'], [1, 34, 'k'], [1, 'se', 'k'], [2, 22, 'k'], [2, 34, 'k'], [2, 'se', 'k'], [3, 22, 'k'], [3, 34, 'k'], [3, 'se', 'k']]
我相信这是可行的:
def cartesian_product(L):
if L:
return {(a,) + b for a in L[0]
for b in cartesian_product(L[1:])}
else:
return {()}
您可以使用标准库中的
itertools.product
获取笛卡尔积。itertools
中的其他酷的相关实用程序包括置换
,组合
,以及带有替换的组合。下面是针对以下代码段的python代码笔:
来自itertools导入产品的
某些列表=[
[1, 2, 3],
['a','b'],
[4, 5]
]
结果=列表(产品(*SomeList))
打印(结果)
提前拒绝:
我必须得到一个非常大的笛卡尔乘积的第一个结果。尽管我只想要一件东西,但要花很长时间。问题是,由于结果的顺序,在找到正确的结果之前,它必须遍历许多不需要的结果。因此,如果我有10个50个元素的列表,并且前两个列表中的第一个元素不兼容,那么它必须遍历最后8个列表的笛卡尔积,尽管它们都会被拒绝
此实现允许在结果包含每个列表中的一项之前对其进行测试。因此,当我检查某个元素是否与先前列表中已包含的元素不兼容时,我会立即转到当前列表的下一个元素,而不是遍历以下列表中的所有产品。根据文档,实际的itertools.product实现不会生成中间结果,这可能很贵。对于中等规模的列表,使用此技术可能会很快失控。我只能将OP指向文档,而不是为他阅读。文档中的代码旨在演示产品功能的功能,不是Python早期版本的变通方法。请注意,“每种可能的组合”与“笛卡尔乘积”并不完全相同,因为笛卡尔乘积中允许重复。笛卡尔乘积是否有非重复版本?@KJW是,set(笛卡尔乘积)
笛卡尔乘积中不应存在重复项,除非输入列表本身包含重复项。如果您希望笛卡尔乘积中没有重复项,请在所有输入列表中使用set(inputlist)
。从数学上讲,笛卡尔积是一个集合,所以笛卡尔积不包含重复项。另一方面,如果输入有重复项,itertools.product
将在输出中有重复项。因此,严格来说,itertools.product
不是笛卡尔积,除非将输入包装在set
中,如@CamilB所述。如果一些参数
是迭代器,则递归版本不起作用。如果使用OP提供的变量SomeList,则需要添加“*”字符。SomeList之前的*
有什么用?它的作用是什么?@VineetKumarDoshi:这里它用于将列表解压为函数调用的多个参数。请在此处阅读更多信息:注意:仅当每个列表至少包含一个item@igo当任何列表包含零项时,它也能工作——至少一个零大小列表和任何其他列表的笛卡尔乘积是空列表,这正是它产生的结果。它们存储在堆栈中,尽管如此。@QuentinPradet你是说像def():while True:yield 1
这样的生成器在我们处理它的过程中会不断增加其堆栈大小吗?@QuentinPradet是的,但即使在这种情况下,也只需要最大深度所需的堆栈,而不是整个列表,所以在这种情况下,堆栈为3是真的,对不起。基准可能很有趣。:)SomeList之前的*
有什么用?@VineetKumarDoshi“product(SomeList)”是子列表之间的笛卡尔乘积,Python首先将“[1,2,3]”作为元素,然后在下一个逗号之后获取另一个元素,这就是换行符,因此第一个乘积项是([1,2,3]),类似于第二个([4,5]),等等”[([1,2,3],),([4,5],),([6,7],)]”。如果您想在元组内的元素之间获得笛卡尔乘积,您需要告诉带星号的Python
def my_product(pools: List[List[Any]], rules: Dict[Any, List[Any]], forbidden: List[Any]) -> Iterator[Tuple[Any]]:
"""
Compute the cartesian product except it rejects some combinations based on provided rules
:param pools: the values to calculate the Cartesian product on
:param rules: a dict specifying which values each value is incompatible with
:param forbidden: values that are never authorized in the combinations
:return: the cartesian product
"""
if not pools:
return
included = set()
# if an element has an entry of 0, it's acceptable, if greater than 0, it's rejected, cannot be negative
incompatibles = defaultdict(int)
for value in forbidden:
incompatibles[value] += 1
selections = [-1] * len(pools)
pool_idx = 0
def current_value():
return pools[pool_idx][selections[pool_idx]]
while True:
# Discard incompatibilities from value from previous iteration on same pool
if selections[pool_idx] >= 0:
for value in rules[current_value()]:
incompatibles[value] -= 1
included.discard(current_value())
# Try to get to next value of same pool
if selections[pool_idx] != len(pools[pool_idx]) - 1:
selections[pool_idx] += 1
# Get to previous pool if current is exhausted
elif pool_idx != 0:
selections[pool_idx] = - 1
pool_idx -= 1
continue
# Done if first pool is exhausted
else:
break
# Add incompatibilities of newly added value
for value in rules[current_value()]:
incompatibles[value] += 1
included.add(current_value())
# Skip value if incompatible
if incompatibles[current_value()] or \
any(intersection in included for intersection in rules[current_value()]):
continue
# Submit combination if we're at last pool
if pools[pool_idx] == pools[-1]:
yield tuple(pool[selection] for pool, selection in zip(pools, selections))
# Else get to next pool
else:
pool_idx += 1