使用递归时出现意外结果

时间:2018-07-12 16:02:50

标签: python list recursion permutation

我才刚刚开始接触python领域,坦白地说,对于以下实现给我带来的结果,我有些困惑。

这只是一个递归函数,应输出给定列表的所有排列。我知道可能有上千种更好的方法可以做到这一点,但这就是我想出的。让我惊讶的是,完全相同的代码在C ++中也可以正常工作;我相信这可能与python *中传递变量的方式有关,但我不确定。

代码:

def permut (s, permut_s):
    if not s:
        return permut_s
    if not permut_s:
        last_s = s[-1]
        s.pop (-1)
        permut_s = [[last_s]]
        return permut (s, permut_s)
    new_permut_s = []
    last_s = s[-1]
    for x in range (len (permut_s)):
        aux = permut_s[x]
        aux = [last_s] + aux
        new_permut_s.append (aux)
        for y in range (len (aux) - 1):
            aux_elem = aux[y]
            aux[y] = aux[y + 1]
            aux[y + 1] = aux_elem
            new_permut_s.append (aux)
    s.pop (-1)
    return permut (s, new_permut_s)

def main(s, permut_s):
    print (permut (s, permut_s))

main ([1,2,3], [])

*之所以这样说,是因为在两行之间进行一些打印显示,在第一个非平凡执行之前,new_permut_s = [[2,3]],之后是[[3,2],[ 3,2]],应为[[2,3],[3,2]]

非常感谢!

3 个答案:

答案 0 :(得分:0)

忘记所有可以使用的排列:

import itertools
list(itertools.permutations([1, 2, 3]))

“不要重新发明轮子”是编程中最好的格言之一。

答案 1 :(得分:0)

问题是您多次追加同一列表。因此,您的修改会影响之前的new_permut_s元素(因为它们可以全部视为与aux中的元素相同的列表地址)。以下是最简单的解决方案(请参见# THE FIX):

def permut (s, permut_s):
    if not s:
        return permut_s
    if not permut_s:
        last_s = s[-1]
        s.pop (-1)
        permut_s = [[last_s]]
        return permut (s, permut_s)
    new_permut_s = []
    last_s = s[-1]
    for x in range (len (permut_s)):
        aux = permut_s[x]
        aux = [last_s] + aux
        new_permut_s.append (aux)
        for y in range (len (aux) - 1):
            aux = aux[:]  # THE FIX
            aux_elem = aux[y]
            aux[y] = aux[y + 1]
            aux[y + 1] = aux_elem
            new_permut_s.append (aux)
    s.pop (-1)
    return permut (s, new_permut_s)

def main(s, permut_s):
    print (permut (s, permut_s))

main ([1,2,3], [])

通过aux = aux[:](或aux = aux[0:len(aux)]),我们获取原始列表,获取其切片(至少在标准库中,它始终是一个新对象),然后分配切片(列表)这里)到相同的变量。

关于是否可以避免这种复制:我不这样认为,因为所有排列的列表应该是独立的。从理论上讲,您可以编写一个类来修补现有列表(例如,每个实例都包含对“全局”列表和某些本地修补程序的引用,然后在访问索引时为索引计算实际值),或者可以合并代码用于复制和交换(以便您通过几个步骤复制原始列表的多个部分)。但是这样的解决方案似乎对您的任务来说是过大的。


PythonTutor.com帮助调试此类问题。特别是,我使用了this "visualization"来调试您的代码。

在REPL中,此类检查非常有用:

>>> out = permut([1,2,3], [])
>>> out[0] is out[1]
True

答案 2 :(得分:0)

您要在输出中附加浅表副本。使用这个:

from copy import deepcopy

def permut (s, permut_s):
    print(s)
    print(permut_s)
    if not s:
        return permut_s
    if not permut_s:
        last_s = s[-1]
        s.pop (-1)
        permut_s = [[last_s]]
        return permut (s, permut_s)
    new_permut_s = []
    last_s = s[-1]
    for x in range (len (permut_s)):
        aux = permut_s[x]
        aux = [last_s] + aux
        new_permut_s.append (deepcopy(aux))
        for y in range (len (aux) - 1):
            aux_elem = aux[y]
            aux[y] = aux[y + 1]
            aux[y + 1] = aux_elem
            new_permut_s.append (deepcopy(aux))
    print("new")
    print(new_permut_s)
    s.pop (-1)
    return permut (s, new_permut_s)

def main(s, permut_s):
    print (permut (s, permut_s))

main ([1,2,3], [])