【问题标题】:Python Non-recursive PermutationPython非递归排列
【发布时间】:2016-08-17 04:03:08
【问题描述】:

有人了解以下用于生成数字列表的所有排列的迭代算法吗?

我不明白while len(stack) 循环中的逻辑。有人可以解释一下它是如何工作的吗?

# Non-Recursion
@param nums: A list of Integers.
@return: A list of permutations.

def permute(self, nums):
    if nums is None:
        return []
    nums = sorted(nums)
    permutation = []
    stack = [-1]
    permutations = []
    while len(stack):
        index = stack.pop()
        index += 1
        while index < len(nums):
            if nums[index] not in permutation:
                break
            index += 1
        else:
            if len(permutation):
                permutation.pop()
            continue

        stack.append(index)
        stack.append(-1)
        permutation.append(nums[index])
        if len(permutation) == len(nums):
            permutations.append(list(permutation))
    return permutations

我只是想理解上面的代码。

【问题讨论】:

  • 你试过用调试器单步调试吗?理解算法的好方法。

标签: python algorithm permutation


【解决方案1】:

正如您问题的 cmets 部分所述,调试可能会提供一种有用的方式来理解代码的作用。不过,让我从高级别的角度了解您的代码的作用。

首先,虽然没有对函数 permute 的递归调用,但您提供的代码实际上是递归的,因为它所做的只是保留自己的堆栈,而不是使用您的内存管理器提供的堆栈操作系统。具体来说,变量stack 保持递归状态,可以说是从一个递归调用传递到另一个。您可以并且也许应该将permute 函数中外部while 循环的每次迭代视为递归调用。如果这样做,您将看到外部 while 循环有助于以深度优先的方式“递归”遍历 nums 的每个排列。

注意到这一点,很容易弄清楚每个“递归调用”的作用。基本上,变量permutation 保持nums 的当前排列,这是随着while 循环的进行而形成的。变量permutations 存储找到的nums 的所有排列。如您所见,permutations 仅在 len(permutation) 等于 len(nums) 时更新,这可以被视为使用自定义堆栈实现的递归关系的基本情况。最后,内部while 循环选择nums 的哪个元素添加到正在形成的当前排列(即存储在变量permutation 中)。

就是这样,真的。如建议的那样,您可以使用调试器找出与维护stack 相关的行中正在执行的操作。作为最后一点,让我重申一下,我个人不会认为这种实现是非递归的。碰巧的是,这个递归解决方案没有使用操作系统提供的抽象,而是保留了自己的堆栈。为了更好地理解正确的非递归解决方案是怎样的,您可以观察递归和迭代解决方案在找到下面提供的第 nth 斐波那契数的问题上的区别。如您所见,非递归解决方案不保留堆栈,并且不是将问题分成较小的实例(递归),而是从较小的解决方案构建解决方案。 (动态规划)

def recursive_fib(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    return recursive_fib(n-1) + recursive_fib(n-2)

def iterative_fib(n):
    f_0 = 0
    f_1 = 1
    for i in range(3, n):
        f_2 = f_1 + f_0
        f_0 = f_1
        f_1 = f_2
    return f_1

【讨论】:

  • 之前的算法不正确。尝试 recursive_fib(1) 它会失败。它会生成recursive_fib(-1)?那是不正确的。加上递归调用应该添加而不是减去。我提交了修改。
【解决方案2】:

@ilim 的答案是正确的,应该是公认的答案,但我只是想添加另一个不适合作为评论的观点。虽然我想你正在研究这个算法作为练习,但应该指出,根据列表的大小,更好的方法可能是用户 itertools 的 permutations() 函数:

print [x for x in itertools.permutations([1, 2, 3])]

在我的机器上使用包含 11 个项目(39m 排列)的列表进行测试,itertools.permutations(x) 花费了 1.7 秒,但使用上面的自定义解决方案花费了 76 秒。但是请注意,对于 12 个项目(479m 排列),itertools 解决方案会因内存错误而崩溃。如果您需要有效地生成这种大小的排列,您最好使用本机代码。

【讨论】:

    猜你喜欢
    • 2012-10-18
    • 2016-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-18
    • 2011-07-23
    • 2012-08-24
    相关资源
    最近更新 更多