如何在python中递归地置换列表中的n个元素?

时间:2015-04-30 14:49:04

标签: python recursion

我正试图弄清楚如何以递归方式从列表中创建所有可能的n个大小的排列。

例如,n=2list=[0,1,5]的结果为:

[[0, 0], [1, 0], [5, 0], [0, 1], [1, 1], [5, 1], [0, 5], [1, 5], [5, 5]]

n=3list=[2,3]

[[2, 2, 2], [3, 2, 2], [2, 3, 2], [3, 3, 2], [2, 2, 3], [3, 2, 3], [2, 3, 3], [3, 3, 3]]

(有点像笛卡尔产品)。

我设法提出了这段代码:

def perm(nlist, m):

    if m > len(nlist):
        return 
    if m == 0 or m == len(nlist):
        return [[]]
    results = []            
    for list_i in nlist:
        temp = list(nlist)          
        temp.remove(list_i)
        results.extend(map(lambda x: [list_i] + x, perm(temp, m-1)))
    return results

但它没有像[0,0] [1,1] [2,2]等那样给出排列。

有人为我提供解决方案吗?

如何在不使用map()lambda()的情况下完成此操作?

2 个答案:

答案 0 :(得分:2)

您要查找的结果是Cartesian product,它是所有有序对(a,b)的集合,其中a∈A和b∈B。或者,所有组合的所有排列。

from itertools import combinations_with_replacement as iter_combs
from itertools import permutations

def perms_combs(l, n):
    all_combs = []
    for l in list(iter_combs(l, n)):
          [all_combs.append(perm) for perm in list(permutations(l, n))]
    return list(set(all_combs))
>> print list(product([0, 1, 5], repeat=2))
    [(0, 0), (0, 0), (0, 1), (1, 0), (0, 5), (5, 0), (1, 1), (1, 1), (1, 5), (5, 1), (5, 5), (5, 5)]

>> print list(product([2, 3], repeat=3))
    [(3, 3, 3), (2, 2, 2), (2, 3, 2), (3, 3, 2), (2, 3, 3), (3, 2, 2), (3, 2, 3), (2, 2, 3)]

然而,此过程已由itertools函数涵盖:product

from itertools import product

>> print product([0, 1, 5], 2)
    [(0, 0), (0, 0), (0, 1), (1, 0), (0, 5), (5, 0), (1, 1), (1, 1), (1, 5), (5, 1), (5, 5), (5, 5)]

>> print product([2, 3], 3)
    [(3, 3, 3), (2, 2, 2), (2, 3, 2), (3, 3, 2), (2, 3, 3), (3, 2, 2), (3, 2, 3), (2, 2, 3)]

答案 1 :(得分:2)

这不像笛卡尔积的那种;它就像<笛卡尔产品一样

>>> from itertools import product
>>> list(product([0,1,5], repeat=2))
[(0, 0), (0, 1), (0, 5), (1, 0), (1, 1), (1, 5), (5, 0), (5, 1), (5, 5)]
>>> list(product([2,3], repeat=3))
[(2, 2, 2), (2, 2, 3), (2, 3, 2), (2, 3, 3), (3, 2, 2), (3, 2, 3), (3, 3, 2), (3, 3, 3)]

itertools.product的polyfill如下:

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)

但是既然你不能使用itertools,你也可以自由地为你的问题编写一个更有效的解决方案。由于我们只计算n个相同迭代的乘积,我们只需将其称为笛卡尔指数:

def cartesian_exponent(li, e=1):
    pools = [li] * e
    result = [[]]
    for pool in pools:
        result = [x+[y] for x in result for y in pool]
    return result

或递归使用另一个难以理解的列表理解:

def cartesian_exponent(li, e=1):
    if e == 1:
        return [[x] for x in li]
    else:
        return [[x]+y for x in li for y in cartesian_exponent(li, e=e-1)]

可以将其压缩为一行:

def cartesian_exponent(li, e=1):
    return [[x] for x in li] if e == 1 else [[x] + y for x in li for y in cartesian_exponent(li, e=e-1)]

但是那时你会牺牲简洁性的可读性而且这不是bueno。难以理解的列表理解已经不够透明了。

一些测试:

>>> cartesian_exponent([0,1,5], e=2)
[[0, 0], [0, 1], [0, 5], [1, 0], [1, 1], [1, 5], [5, 0], [5, 1], [5, 5]]
>>> cartesian_exponent([2,3], e=3)
[[2, 2, 2], [2, 2, 3], [2, 3, 2], [2, 3, 3], [3, 2, 2], [3, 2, 3], [3, 3, 2], [3, 3, 3]]