如何从一组列表中得到笛卡尔积(每一种可能的值组合)?
输入:
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), ...]
该技术的一个常见应用是避免深度嵌套循环。有关更具体的重复,请参见避免嵌套for循环。
如果你想要一个相同列表与它自身多次相乘的笛卡尔积,itertools。Product可以很好地处理这个问题。参见对列表中的每对元素的操作或生成具有重复的排列。
递归的方法:
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)
在99%的情况下,你应该使用itertools.product。它是用高效的C代码编写的,因此它可能比任何自定义实现都要好。
在1%的情况下,您需要只使用python算法(例如,如果您需要以某种方式修改它),您可以使用下面的代码。
def product(*args, repeat=1):
"""Find the Cartesian product of the arguments.
The interface is identical to itertools.product.
"""
# Initialize data structures and handle bad input
if len(args) == 0:
yield () # Match behavior of itertools.product
return
gears = [tuple(arg) for arg in args] * repeat
for gear in gears:
if len(gear) == 0:
return
tooth_numbers = [0] * len(gears)
result = [gear[0] for gear in gears]
# Rotate through all gears
last_gear_number = len(gears) - 1
finished = False
while not finished:
yield tuple(result)
# Get next result
gear_number = last_gear_number
while gear_number >= 0:
gear = gears[gear_number]
tooth_number = tooth_numbers[gear_number] + 1
if tooth_number < len(gear):
# No gear change is necessary, so exit the loop
result[gear_number] = gear[tooth_number]
tooth_numbers[gear_number] = tooth_number
break
result[gear_number] = gear[0]
tooth_numbers[gear_number] = 0
gear_number -= 1
else:
# We changed all the gears, so we are back at the beginning
finished = True
接口与itertools.product相同。例如:
>>> list(product((1, 2), "ab"))
[(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]
这个算法相对于本页上其他只使用python的解决方案有以下优点:
它不会在内存中建立中间结果,从而保持较小的内存占用。
它使用迭代而不是递归,这意味着您不会得到“超过最大递归深度”的错误。
它可以接受任意数量的输入可迭代对象,这使得它比使用嵌套的for循环更灵活。
这段代码基于itertools。PyPy的产品算法,它是在MIT许可下发布的。
虽然已经有很多答案,但我想分享一些我的想法:
迭代方法
def cartesian_iterative(pools):
result = [[]]
for pool in pools:
result = [x+[y] for x in result for y in pool]
return result
递归方法
def cartesian_recursive(pools):
if len(pools) > 2:
pools[0] = product(pools[0], pools[1])
del pools[1]
return cartesian_recursive(pools)
else:
pools[0] = product(pools[0], pools[1])
del pools[1]
return pools
def product(x, y):
return [xx + [yy] if isinstance(xx, list) else [xx] + [yy] for xx in x for yy in y]
Lambda方法
def cartesian_reduct(pools):
return reduce(lambda x,y: product(x,y) , pools)
只是补充一点已经说过的:如果你使用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
关于sympy。