使用递归计算数组组合

时间:2019-04-26 04:04:06

标签: python arrays recursion

我希望输入一个n * m数组,结果输出是一个包含行元素不同组合的数组。 这是一个要澄清的示例(尽管是非常简单的情况):

我希望输入以下形状的数组:

[[1, 2, 3]
[2, 5, 6]]

并希望收到以下输出:

[[1,2], [1,5], [1,6], [2,5], [2,6], [3,2], [3,5], [3,6]]

如您所见,由于重复,不包括[2,2]

当输入维事先已知时,我可以编写包含嵌套的for循环的快速而肮脏的代码:

A = [[1, 2, 3], [2, 5, 6]]
m = len(A[0])    
for i in range(0, m):      
    for j in range(0, m):
            if A[0][i]!=A[1][j]:                     #check and eliminate repetition
                combined.append([A[0][i],A[1][j])
                choice_num.append([i+1, j+1])    #See (**) below

我真的很想知道如何将其实现为递归函数,因此给定一些输入n-D数组A,可以简单地将其称为:

recursive_looper(A)

(**)我想要的另一个功能是该函数输出与组合中使用的元素对应的列号,因此我们得到两个输出:

element values:   [[1,2], [1,5], [1,6], [2,5], [2,6], [3,2], [3,5], [3,6]]
element position: [[1,1], [1,2], [1,3], [2,2], [2,3], [3,1], [3,2], [3,3]]

任何提示或建议将不胜感激!

编辑:我对可以实现所需输出的任何解决方案持开放态度。递归只是想到的第一件事。

编辑2(扩展功能):此代码不得限于特定的列表输入形状,而可以扩展为任何形状(n,m)的数组。 我将提供代码崩溃的示例。解决方法是实现n-1个条件语句,我想避免,因为必须先验知道数组形状。

A = [[2, 4, 1, 11, 3], [3, 2, 1, 4, 11], [2, 3, 4, 17, 13]]

如果我不对您的索引编制/过滤器进行任何修改,则会收到“过滤”列表的以下输出:

#[[2, 3, 2], [2, 3, 3], [2, 3, 4], [2, 3, 17], [2, 3, 13], [2, 1, 2], ..., [3, 11, 13]]

我立即注意到它只比较元素位置0和位置1的“相似性”,因此为什么第一个组合包含两个2。 我可以对索引采集器和过滤器循环进行如下修改:

for i in range(0, len(projects_master)-2):
    indexes = [idx for idx, t in enumerate(prod) if t[i] == t[i+1] or t[i]==t[i+2] or t[i+1] == t[i+2] ]

res = []
for i in range(0, len(A)-2):
    res.append(list(filter( lambda v: v[i] != v[i+1] and v[i] != v[i+2] and v[i+1] != v[i+2], prod)))
result = [list(t) for t in res[0]]

确实给出正确的输出,但是就像我说的,我需要写出n-1个t [i]和v [i]条件。如何自动完成?

编辑3-最终 多亏了那些提供不同方法来帮助我实现相同最终目标的人。我从每个人那里获得了一些见识,并写了一些对我来说有意义的东西,并且似乎对任何输入都起作用。过滤重复项并将其从组合中删除的代码如下所示:

ind_remove = []
for i in range(0, len(prod)):
    if len(prod[i]) != len(set(prod[i])):
        ind_remove.append(i)

adder=0
for i in ind_remove:
    del prod[i-adder]
    adder=adder+1   #takes into account change in indices after an element is deleted.

3 个答案:

答案 0 :(得分:0)

您可以使用itertools.product生成所需的组合,其作用类似于两组之间的笛卡尔乘积以生成组合。

因此,如果列表为[[1, 2], [3, 4]],则子列表中的笛卡尔乘积将为
[[1, 3], [1, 4], [2, 3], [2, 4]]

from itertools import product

a = [[1, 2, 3], [2, 5, 6]]

# Generate all possible products, *a gives you two lists
prod = list(product(*a))
#[(1, 2), (1, 5), (1, 6), (2, 2), (2, 5), (2, 6), (3, 2), (3, 5), (3, 6)]

#Get the list of duplicate indexes
indexes = [idx for idx, t in enumerate(prod) if t[0] == t[1] ]
print(indexes)
#[3]

#Remove tuples who are duplicates
res = list(filter( lambda v: v[0] != v[1], prod))
print(res)
#[(1, 2), (1, 5), (1, 6), (2, 5), (2, 6), (3, 2), (3, 5), (3, 6)]

#Convert the tuples to list
result = [list(t) for t in res]
print(result)
#[[1, 2], [1, 5], [1, 6], [2, 5], [2, 6], [3, 2], [3, 5], [3, 6]]

答案 1 :(得分:0)

您可以使用一个函数来迭代给定列表列表的第一个列表的项目,并将每个项目与递归调用中的组合合并:

def nonrepetitive_product(lists):
    if not lists:
        yield []
        return
    first, *rest = lists
    combinations = list(nonrepetitive_product(rest))
    for item in first:
        for combination in combinations:
            if item not in combination:
                yield [item, *combination]

所以给定:

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

list(nonrepetitive_product(l))返回:

[[1, 2], [1, 5], [1, 6], [2, 5], [2, 6], [3, 2], [3, 5], [3, 6]]

答案 2 :(得分:0)

如果您想要任意数量的行的位置和值,最好结合使用itertools.productenumerate。过滤有些棘手,但是可以做到:

import itertools

A = [[1, 2, 3], [2, 5, 6], [7, 8, 3]]

prod = itertools.product(*map(enumerate, A))     # yields ((i,x),(j,y),(k,z),...) nested tuples
transposed = ([*zip(*pairs)] for pairs in prod)  # yields ((i,j,k,...), (x,y,z,...)) 2-tuples
filtered = [(ijk, xyz) for ijk, xyz in transposed if len(xyz) == len(set(xyz))] # filter dupes

indexes, values = zip(*filtered) # you might find `filtered` more useful than separate lists