【问题标题】:Generation of all possible permutations using backtracking使用回溯生成所有可能的排列
【发布时间】:2020-08-25 17:44:59
【问题描述】:

我是编程新手,遇到了一些基本的回溯问题。其中之一是打印列表的所有可能排列。我从网上得到以下代码。

def permutation(arr,size,n):
    if size == 1:
        print(arr)
        return
    for i in range(size):
        permutation(arr,size-1,n)
        if size & 1:
            arr[0],arr[size-1] = arr[size-1],arr[0]
        else:
            arr[i],arr[size-1] = arr[size-1],arr[i]

这工作正常,它为输入 arr = [1,2,3] 打印了以下输出

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

现在我想将所有这些可能的排列存储到一个不同的数组中,并将代码修改为这个。

 ans = []
 def permutation(arr,size,n):
    if size == 1:
        ans.append(arr)
    for i in range(size):
        permutation(arr,size-1,n)
        if size & 1:
            arr[0],arr[size-1] = arr[size-1],arr[0]
        else:
            arr[i],arr[size-1] = arr[size-1],arr[i]

但是当我打印列表时,它打印了这个。

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

我在这里做错了什么?如何将所有可能的排列存储到一个列表中?

【问题讨论】:

  • if size == 1: 之后,您在第一个代码中有return,但在第二个代码中没有
  • 人们仍然支持重复最多的 Python 问题。很奇怪。
  • @superbrain 这里的问题是使用两个看似相同的代码,但实际上并不相同。从这个意义上说,它不是重复的
  • @JanStránský 这里唯一不重复的是同一个列表对象一遍又一遍地填充到结果列表中。与其他问题相同的问题。

标签: python recursion permutation backtracking


【解决方案1】:

问题在于,您总是将对arr 对象(它是一个列表)的同一实例的引用附加到ans 列表中。类似情况见this question

您在第一个代码中没有这个问题,因为您只需在每个排列时打印列表。但是在第二个代码中,您在每次递归时操作相同的列表,当您位于递归树的顶部时,您会返回原始列表 ([1, 2, 3])。

要解决此问题,您可以将列表的深拷贝添加到ans

def permutation(arr, size, n):
    if size == 1:
        # ans.append(arr)

        # this copies every element of arr into a 
        # new list and then appends the new list to ans.
        ans.append([e for e in arr]) 

    for i in range(size):
        permutation(arr, size-1, n)
        if size & 1:
            arr[0], arr[size-1] = arr[size-1], arr[0]
        else:
            arr[i], arr[size-1] = arr[size-1], arr[i]

【讨论】:

    【解决方案2】:
    原始答案,未回答问题(见编辑)

    回答“我在这里做错了什么?”问题。

    你有

        if size == 1:
            print(arr)
            return
    

    在第一个代码中,但是

        if size == 1:
            ans.append(arr)
    

    试试:

        if size == 1:
            ans.append(arr)
            return
    

    它应该按预期工作

    编辑

    感谢@superbrain的批评,确实没用,我对OP中的代码正确性过于乐观了。

    所以“我在这里做错了什么?”的答案。是这条线吗

            ans.append(arr)
    

    您每次都附加相同的列表实例,因此您以相同对象的列表结束

    类似这样的场景:

    list1 = [1,2,3]
    list2 = []
    list2.append(list1)
    list2.append(list1)
    print(list2) # [[1, 2, 3], [1, 2, 3]]
    list1[2] = 9
    print(list2) # [[1, 2, 9], [1, 2, 9]]
    

    【讨论】:

    • “应该”因为你没有测试过?
    • 是的,我可以使用“必须”而不是“应该”。某些情况(应该是?:-) 无需测试即可清楚
    • 你是对的,很明显这不会解决它,但我还是测试了它。
    • 您的更改除了防止多消耗几个 CPU 周期外没有任何作用。
    【解决方案3】:

    回溯是一种通用算法,“它会逐步构建解决方案的候选者,并在确定候选者不可能完成有效解决方案时立即放弃每个部分候选者(“回溯”)。” (维基百科)。

    因此,基本上,您所做的就是逐步构建所有排列。一旦你建立了一个排列,你就回溯并建立另一个排列,依此类推,直到你生成所有 n!可能的排列,例如,在 n 个符号上。

    示例:n=3,S={1,2,3}。

    您从 1 开始。然后向前选择 2(因为已经选择了 1),然后选择 3。此时您已经构建了第一个排列 123。然后您回溯并选择 3 而不是 2 ,然后选择 2,你有 132。你再次回溯,但你已经使用了 2 和 3,所以你再次回溯(上一级),选择 2 而不是 1,然后选择 1,最后选择 3,所以你有 213 个。

    【讨论】:

      猜你喜欢
      • 2012-03-22
      • 1970-01-01
      • 2022-01-14
      • 1970-01-01
      • 2015-07-16
      • 2012-05-05
      • 2016-09-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多