【问题标题】:Generate all permutations in go在 go 中生成所有排列
【发布时间】:2015-05-13 22:44:27
【问题描述】:

我正在寻找一种方法来生成元素列表的所有可能排列。类似于python'sitertools.permutations(arr)

permutations ([])
[]

permutations ([1])
[1]

permutations ([1,2])
[1, 2]
[2, 1]

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

不同之处在于,我不在乎排列是按需生成(如 python 中的生成器)还是一起生成。我也不关心它们是否会按字典顺序排序。我所需要的只是以某种方式获得这些n! 排列。

【问题讨论】:

    标签: algorithm go


    【解决方案1】:

    有很多算法可以生成permutations。我发现的最简单的方法之一是Heap's algorithm

    它通过选择一对从前一个排列生成每个排列 要交换的元素。

    上面的链接概述了一个又一个打印排列的想法和伪代码。这是我对返回所有排列的算法的实现

    func permutations(arr []int)[][]int{
        var helper func([]int, int)
        res := [][]int{}
    
        helper = func(arr []int, n int){
            if n == 1{
                tmp := make([]int, len(arr))
                copy(tmp, arr)
                res = append(res, tmp)
            } else {
                for i := 0; i < n; i++{
                    helper(arr, n - 1)
                    if n % 2 == 1{
                        tmp := arr[i]
                        arr[i] = arr[n - 1]
                        arr[n - 1] = tmp
                    } else {
                        tmp := arr[0]
                        arr[0] = arr[n - 1]
                        arr[n - 1] = tmp
                    }
                }
            }
        }
        helper(arr, len(arr))
        return res
    }
    

    这里是一个如何使用它的例子(Go playground):

    arr := []int{1, 2, 3}
    fmt.Println(permutations(arr))
    [[1 2 3] [2 1 3] [3 2 1] [2 3 1] [3 1 2] [1 3 2]]
    

    需要注意的一点是排列不是按字典顺序排序的(正如您在itertools.permutations 中看到的那样)。如果出于某种原因需要对其进行排序,我发现它的一种方法是从 factorial number system 生成它们(它在 permutation section 中进行了描述,并允许快速找到第 n 个字典排列)。

    P.S.你也可以看看别人的代码herehere

    【讨论】:

      【解决方案2】:

      这里的代码迭代所有排列而不首先生成它们。切片p 将中间状态保持为Fisher-Yates shuffle 算法中的偏移量。这有一个很好的属性,p 的零值描述了身份排列。

      package main
      
      import "fmt"
      
      func nextPerm(p []int) {
          for i := len(p) - 1; i >= 0; i-- {
              if i == 0 || p[i] < len(p)-i-1 {
                  p[i]++
                  return
              }
              p[i] = 0
          }
      }
      
      func getPerm(orig, p []int) []int {
          result := append([]int{}, orig...)
          for i, v := range p {
              result[i], result[i+v] = result[i+v], result[i]
          }
          return result
      }
      
      func main() {
          orig := []int{11, 22, 33}
          for p := make([]int, len(orig)); p[0] < len(p); nextPerm(p) {
              fmt.Println(getPerm(orig, p))
          }
      }
      

      【讨论】:

      • 出色的实施(来自我的+1)。感谢您提供另一种算法。只有一个小错误:它在空切片上恐慌。您还可以添加排列是按字典顺序排列的。
      • 我已经把它变成了一个个人项目的库。我将函数转换为方法,修复了空切片的处理,并添加了从通道读取的选项。 Have a look。你介意我发布这个开源吗?
      • +1 最快的字典顺序排列算法 afaik!如果将 getPerm() 第 1 行更改为 result:=make([]int, len(orig));复制(结果,原件); (go1.10.3 darwin/amd64) 原因是append重复调用relocation根据append源的利用分配额外的内存(append是时间关键的)。
      【解决方案3】:
      var res [][]int
      func permute(nums []int) [][]int {
          res=make([][]int,0)
          n:=len(nums)
          var backTrack func(int)
          backTrack=func(first int){
              if first == n{
                  temp:=make([]int, n)
                  copy(temp,nums)
                  res = append(res, temp)
              }
              for i:=first;i<n;i++{
                  nums[first],nums[i] = nums[i],nums[first]
                  backTrack(first+1)
                  nums[first],nums[i] = nums[i],nums[first]
              }
          }
        
          backTrack(0)
          return res
      }
      

      【讨论】:

        【解决方案4】:

        另一个工作代码

        package permutations
        
        import "fmt"
        
        func AllPermutation(a []int) {
            var res [][]int
            calPermutation(a, &res, 0)
            fmt.Println(res)
        }
        func calPermutation(arr []int, res *[][]int, k int) {
            for i := k; i < len(arr); i++ {
                swap(arr, i, k)
                calPermutation(arr, res, k+1)
                swap(arr, k, i)
            }
            if k == len(arr)-1 {
                r := make([]int, len(arr))
                copy(r, arr)
                *res = append(*res, r)
                return
            }
        }
        func swap(arr []int, i, k int) {
            arr[i], arr[k] = arr[k], arr[i]
        }
        //result [[1 2 3] [1 3 2] [2 1 3] [2 3 1] [3 2 1] [3 1 2]]
        

        【讨论】:

          【解决方案5】:

          在我的例子中,我引用了一个数组,然后我在您的示例中做了一些更改:

          func generateIntPermutations(array []int, n int, result *[][]int) {
              if n == 1 {
                  dst := make([]int, len(array))
                  copy(dst, array[:])
                  *result = append(*result, dst)
              } else {
                  for i := 0; i < n; i++ {
                      generateIntPermutations(array, n-1, result)
                      if n%2 == 0 {
                          // Golang allow us to do multiple assignments
                          array[0], array[n-1] = array[n-1], array[0]
                      } else {
                          array[i], array[n-1] = array[n-1], array[i]
                      }
                  }
              }
          }
          numbers := []int{0, 1, 2}
          var result [][]int
          generateIntPermutations(numbers, len(numbers), &result)
          
          // result -> [[0 1 2] [1 0 2] [2 1 0] [1 2 0] [2 0 1] [0 2 1]]
          

          【讨论】:

          • 谢谢@derricw,我忘了在函数中插入那个副本。我的原始函数使用了一段字符串,所以我当时不需要复制数组。以后我会努力寻找更好的方法。
          猜你喜欢
          • 1970-01-01
          • 2012-03-14
          • 2016-09-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-06-21
          相关资源
          最近更新 更多