在go中生成所有排列

Sal*_*ali 11 algorithm go

我正在寻找一种方法来生成元素列表的所有可能的排列.类似于python的东西 itertools.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]
Run Code Online (Sandbox Code Playgroud)

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

Sal*_*ali 20

有很多算法可以生成排列.我找到的最简单的一个是Heap的算法:

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

在上面的链接中概述了一个接一个地打印排列的想法和伪代码.这是我的算法实现,它返回所有排列

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
}
Run Code Online (Sandbox Code Playgroud)

这是一个如何使用它的例子(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]]
Run Code Online (Sandbox Code Playgroud)

有一点需要注意的是,排列没有按字典顺序排序(正如你所见itertools.permutations).如果由于某种原因你需要对它进行排序,我发现它的一种方法是从阶乘数系统生成它们(它被描述permutation section并允许快速找到第n个词典排列).

PS你也可以在这里这里看看别人的代码


Pau*_*kin 11

这里的代码迭代所有排列而不首先生成所有排列.切片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))
    }
}
Run Code Online (Sandbox Code Playgroud)

  • +1 最快的字典顺序排列算法 afaik!如果 getPerm() 第 1 行更改为 result:=make([]int, len(orig)); 复制(结果,原件);(go1.10.3 darwin/amd64) 原因是 append 重复调用重新分配以根据 append 源的利用分配额外的内存(append 是时间关键的)。 (3认同)
  • 出色的实施(来自我的 +1)。感谢您提供另一种算法。只有一个小错误:它在空切片上发生恐慌。您还可以补充说,排列是按字典顺序排列的。 (2认同)
  • 我已经把它变成了一个个人项目的图书馆。我将函数转换为方法,修复了处理空切片的问题,并添加了从通道读取的选项。[看看](https://gist.github.com/schwern/93f7d93c1c80f8720e6de85bdc92096f)。你介意我发布这个开源吗? (2认同)