递归-python实现n个数的排列组合

0.摘要

给定n个不相同的数字,输出所有的排列组合。

1.思路

先看一下排列组合的思路:

step1:a[0],a[1]……a[n],从n个元素选择一个

step2:除去step1已经选取的元素,从n-1个元素再选择一个

……

step(n):除去step1-step(n-1)已经选取的n-1个元素,只剩一个元素可选。

根据排列组合的思路:n个不同的数,排列组合共有n!种。因此,直接的思路是,使用n重for循环遍历,即可得到所有排列组合结果。

但是,这样的思路存在两个问题:

  • 问题一:题目中n无法确定,所以for循环层数不确定。上述思路只适合n为确定数字的情况;
  • 问题二:如何保证已经选取的元素,不再被选取

2.改进思路

我们发现,在上一种思路中,每一步的操作都是类似的,并且最后终止的条件都是“只剩一个元素”。

这样的情况非常符合运用递归的条件:

  • 可以把要解决的问题转化为一个新问题,而这个新的问题的解决方法仍与原来的解决方法相同,只是所处理的对象有规律地递增或递减。
  • 可以应用这个转化过程使问题得到解决。
  • 必定要有一个明确的结束递归的条件。

因此我们考虑使用递归解决问题:

import numpy as np

def fun(in_list,out_list):
    if len(in_list) == 0:
        print(out_list)
    else:
        for element in in_list:
            inner_in = in_list.copy()
            inner_out = out_list.copy()
            inner_in.remove(element)
            inner_out.append(element)
            fun(inner_in,inner_out)

def full_permutation(in_list):
    out_list = []
    fun(in_list,out_list)

a = np.arange(5).tolist()
full_permutation(a)

 问题一的解决:

在fun()函数中,我们实现了递归操作,并添加了for循环语句。这样,就能够利用递归构建多层for循环。从而解决了问题一

程序最后给出了一个简单的测试样例,读者可自己运行,以求验证。

问题二的解决:

我们只需要把已经用过的数字,从列表中删除,这样就能保证下一次不会再取到。

这里之所以需要对in_list,out_list进行copy操作,是因为在python中,形参和实参会相互影响,具体可看下面的例子:

def fun(lst):
    lst.pop()

lst = [1,2,3,4,5,6,7]
fun(lst)
print(lst)
fun(lst)
print(lst)
fun(lst)
print(lst)
fun(lst)
print(lst)
fun(lst)
print(lst)

从运行结果上,可以看出,fun()中的lst和原始的lst是同一个变量。

所以,在递归过程中,需要调用list.copy(),将备份传入函数形参中,否则不同递归路径的结果会相互影响,这是很重要的一点。

3.利用辅助数组解决问题二

在上一步的思路中,我们将列表大量copy,这样防止了不同递归路径的数据干扰,但却需要占用大量空间。如何在不复制列表的情况下,解决问题二呢?

方法当然是有的,引入辅助列表即可解决。引入一个列表,记录数据是否已经被使用。

当该数据已被使用,则状态为False,下一次取数的时候,就不会取到该数据。

import numpy as np

def fun(n,in_list,state,out_list):
    if (n == 0):
        print(out_list)
    else:
        for i in range(len(in_list)):
            if state[i]:
                out_list[n-1] = in_list[i]
                state[i] = False
                fun(n-1,in_list,state,out_list)
                state[i] = True

def full_permutation(in_list):
    n = len(in_list)
    state = [True for _ in range(n)]
    out_list = in_list.copy()
    fun(n,in_list,state,out_list)

a = np.arange(4).tolist()
full_permutation(a)

在这段代码中,我们使用了state[]记录排列组合数据的状态。

在递归过程中,数据使用后,状态置为False;

本次递归结束后,数据状态要恢复为True,不影响其他递归过程对数据的使用。

另外,为了方便书写,排列组合取值从最后一位开始。

猜你喜欢

转载自blog.csdn.net/qq_17753903/article/details/82730089