如何使用递归函数创建具有n个元素的列表排列列表? |蟒蛇

时间:2015-05-01 13:42:02

标签: python python-2.7

我想制作包含n个元素的嵌套列表,其中包含给定列表的所有排列。

预期输出如下:

public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (!(obj instanceof AbstractAnimal))
        return false;
    AbstractAnimal other = (AbstractAnimal) obj;
    if (name == null) {
        if (other.name != null)
            return false;
    } else if (!name.equals(other.name))
        return false;
    if (sound == null) {
        if (other.sound != null)
            return false;
    } else if (!sound.equals(other.sound))
        return false;
    return true;
}

我怎么能写一个python代码来做同样的事情。

3 个答案:

答案 0 :(得分:7)

你可以像这样递归地解决你的问题:

def perm(n, a):
  # recursion anchor
  if n <= 0:
    return [[]]
  else:
    result = []
    for smallPerm in perm(n-1, a):
      # take all elements from a
      for elem in a:
        # and prepend them to all permutations we get from perm(n-1, 1)
        result.append([elem] + smallPerm)
    return result

你可以把这个函数写成两行,但我决定让它更容易理解。

不确定你对递归有多了解,但我知道初学者不容易理解......让我试着解释一下。

在每个递归函数中,您都需要一个递归锚点。这是函数不递归但直接传递结果的点。它通常是一个定义明确的基本/原子案例。在这个例子中是n==0的情况。由于只有一个排列,其中0个元素是空列表,因此返回包含空列表的列表。

我把n<=0放在那里,所以如果一些smartcookie用例如烫发器调用烫发器,我们就不会遇到麻烦n=-1,但n<0的例外情况也适用。

现在你建立在这个锚点上并且说,好吧我的函数为n==0提供了正确的结果,所以当我当前的n为1并且我用n-1调用它时我可以信任它。现在的诀窍是相信无论你从哪里开始,它都会为每个n-1提供正确的结果!你可能想知道这是怎么回事。这就是具有递归函数的东西,当你编写它们时,你已经依赖它们做正确的工作。

现在这意味着调用perm(n-1, a)会为您提供长度为n-1的所有排列。然后,剩下要做的就是使用a中的所有元素附加(或预先添加)这些排列,并且您有一个列表,其中包含大小为n的排列!完成工作。

但请注意,有时需要使用多个锚点或锚点来处理多种输入可能性 例如着名的低效斐波那契函数:

def fib(n):
  # first anchor
  if n == 0:
    return 0
  # second anchor
  if n == 1:
    return 1

  # recursive calls
  if n < 0:
    # negative if even, positive otherwise
    return -fib(-n) if -n % 2 == 0 else fib(-n)
  return fib(n - 2) + fib(n - 1)

需要两个锚点因为fib(n - 2)你走了两步!

<强>跟进:

为什么这个功能效率低下?原因类似于斯特凡在关于我的第一个解决方案的评论中所指出的 我犯了错误,将递归调用放在循环中。这不一定是错的,但在这种情况下,调用的参数不会被外部循环改变,这使得它不必要。因此,它每次重新生成相同的列表,而不是一次生成,然后保留它。最糟糕的是所有递归实例都做了同样的事情! 你可以通过查看分支因子来看出它有多糟糕。此处fib函数调用自身两次,这意味着它的分支因子为2。因此,如果您的解决方案需要n步深,您将对该功能进行2^n评估 虽然这个分支因子是常数,但我的第一个实现为a中的每个元素调用了自己,因此分支因子等于列表中元素的数量。
我很高兴Stefan没有尝试perm(20,[1,2,3])这将是3 ^ 20次评估,这比他尝试的2 ^ 20次评估大约多3千倍。是的,仅仅因为一个元素而增加了3千倍。这是指数运行时的行动!
为了简单起见,我忽略了问题本身已经与len(a)**n一致的事实。

答案 1 :(得分:2)

itertools库是一个非常高效的东向使用库以及Python,根据您的要求,您可以使用此模块的product方法来获得预期的输出,但是其他一些选项,例如permutationscombinations等,您也必须尝试。

import itertools
n=3

def perm(n, lst):
    return list(itertools.product(lst, repeat=n))

print perm(n, [1,2])

答案 2 :(得分:2)

我意识到你要求一个递归解决方案,但这真的有必要吗?

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

或者如果你确实需要内部列表:

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

如果你不是真的需要整个事情作为一个列表而只是想处理三元组,product单独完成这项工作:

>>> from itertools import product
>>> for triple in product([1, 2], repeat=3):
        print(triple)

(1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 2, 2)
(2, 1, 1)
(2, 1, 2)
(2, 2, 1)
(2, 2, 2)

递归解决方案:

def perm(elements, n):
    return [[e] + p for p in perm(elements, n-1) for e in elements] if n else [[]]

print(perm([1, 2], 3))