什么是全排列

从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。 (来自百度百科)

排列公式

排列公式
全排列公式:
全排列公式
含有重复元素全排列数量:
含有重复元素全排列数量

生成全排列的方法

以集合{1,2,3}为例

1.递归(穷举的思路)

递归全排序
获取一组数据的全排列,即为n个元素放在n个位置,有多少种不同的放置方式

  1. 只有1个元素,那么只有1中放置方式
  2. 我们首先固定n个位置,先思考第一个位置可以放置哪些元素
  3. 从集合中取出1个元素放在第一个位置后,将放置的元素从集合中移除
  4. 考虑剩余的元素和剩余的位置,重复上述过程(重复的过程及递归的过程)

2. 字典序

        如果集合中的所有元素间都是可以比较的,规定两个集合的比较方式为从第一个元素开始,相同位置的两个元素进行比较;若不等,则集合的大小即为此位置两个元素的大小;若相等,继续比较下一个位置的元素,若所有位置元素都相等,则两个集合相等
PS:这里用集合其实不太恰当,数学里集合中的元素是没有顺序的,而这里的集合是有序的
        集合元素递增排列即为该集合的最小排列,递减排列即为最大排列;因此求解集合全排列的过程为:给定一个排列,找到所有排列中大于此排列的最小排列,依次迭代,直到找到的排列为递增排列。

给定排列,寻找下一个排列的过程:
PS1: 如果当前排列已是递减排列,则无新排列
  1. 从右向左找到第一个非递增(是指从右向左递增)元素的位置:descIndex,非递增元素:descElem
  2. 从右向左找到第一个大于descElem的元素位置:greaterIndex,元素:greaterElem
  3. 交换descElem和greaterElem
  4. descIndex后的元素按照升序排列

字典序排列

完整代码

package alg

import (
	"sort"
	"testing"
)

// 阶乘计算
func sliceArrayCount(count int) int {
	if count == 0 {
		return 1
	}
	var result = 1
	for ; count > 0; count-- {
		result *= count
	}
	return result
}

func Test_sliceArrayCount(t *testing.T) {
	t.Log(sliceArrayCount(2), sliceArrayCount(3), sliceArrayCount(4))
}

// 生成全排列
func GenIntSlicePermutation(ori []int) [][]int {
	resSlice := make([][]int, 0, sliceArrayCount(len(ori)))
	permutation(&resSlice, ori, 0)
	return resSlice
}

// 递归思想: f(n)=n*f(n-1)
// 缩小问题规模:确定首位,对剩余元素进行全排列;首位与其他位元素交换,再对剩余元素进行全排列
// 即for i:=0;i<len(ori);i++ ori[0],ori[i]=ori[i],ori[0]; 排列ori[1:]
// 递归出口:1个元素的排列数为1
// 对于序列[1,2,3]
// 首位为1,对[2,3]进行全排列
// 交换1<->2,对[2,3]进行全排列
// 交换1<->3,对[2,1]进行全排列
// 对[2,3]进行全排列的过程与上相同,
// 递归出口: 只有一个元素时,排列数为1
func permutation(permutationSlice *[][]int, seq []int, index int) {
	if seq == nil || len(seq) == 0 {
		return
	}
	// 判断递归出口
	if index == len(seq) {
		res := make([]int, len(seq))
		copy(res, seq)
		*permutationSlice = append(*permutationSlice, res)
		return
	}

	// 循环过程
	for i := index; i < len(seq); i++ {
		seq[i], seq[index] = seq[index], seq[i] // 选定元素放在当次递归的第一个位置
		// 递归过程
		permutation(permutationSlice, seq, index+1)
		seq[i], seq[index] = seq[index], seq[i] // 使用的原数组进行的交换,保证在循环过程中元素的顺序不变
	}
}

// 升序排列->降序排列及过程中的所有排列构成元素的全排列
func GenIntSlicePermutationWithDict(ori []int) [][]int {
	resultSlice := make([][]int, 0, sliceArrayCount(len(ori)))
	// 升序排列
	sort.SliceStable(ori, func(i, j int) bool {
		return ori[i] < ori[j]
	})
	// 首个排列
	copyElem := make([]int, len(ori))
	copy(copyElem, ori)
	resultSlice = append(resultSlice, copyElem)
	for {
		hasNext, nextPermutation := intSliceNextPermutation(ori)
		if !hasNext {
			return resultSlice
		}
		resultSlice = append(resultSlice, nextPermutation)

	}
}

// 如何找出字典序升序的所有排序
// 从右向左找到第一个非递减(递减是指从左向右)元素的位置:descIndex,非递增元素:descElem
// 从右向左找到第一个大于descElem的元素位置:greaterIndex,元素:greaterElem
// 交换descElem和greaterElem
// descIndex后的元素按照升序排列
// 循环出口:元素降序排列
func intSliceNextPermutation(ori []int) (hasNext bool, next []int) {
	elemNum := len(ori)
	// 从右向左寻找第一组非递减的元素
	descIndex := -1
	for rIndex := elemNum - 1; rIndex > 0; rIndex-- {
		if ori[rIndex-1] <= ori[rIndex] {
			descIndex = rIndex - 1
			break
		}
	}
	if descIndex == -1 {
		return false, nil
	}
	// 从右向左寻找第一个大于ori[descIndex]的元素
	var greaterIndex int
	for rIndex := elemNum - 1; rIndex > descIndex; rIndex-- {
		if ori[rIndex] > ori[descIndex] {
			greaterIndex = rIndex
			break
		}
	}
	// 交换元素
	ori[descIndex], ori[greaterIndex] = ori[greaterIndex], ori[descIndex]
	// index + 1升序排列
	sort.SliceStable(ori[descIndex+1:], func(i, j int) bool {
		return ori[descIndex+1+i] < ori[descIndex+1+j]
	})
	next = make([]int, len(ori))
	copy(next, ori)
	return true, next
}

/**** for test ***********/
func TestGenIntSlicePermutation(t *testing.T) {
	resSlice := GenIntSlicePermutation([]int{1, 2, 3, 4})
	t.Logf("len:%d, permutation:%v\n", len(resSlice), resSlice)
}

func TestGenIntSlicePermutationWithDict(t *testing.T) {
	resSlice := GenIntSlicePermutationWithDict([]int{1, 2, 3, 4})
	t.Logf("dict len:%d, permutation:%v\n", len(resSlice), resSlice)
}

// for benchmark
func BenchmarkGenIntSlicePermutation(b *testing.B) {
	var ori = []int{1, 2, 3, 4}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		GenIntSlicePermutationWithDict(ori)
	}
}

func BenchmarkGenIntSlicePermutationWithDict(b *testing.B) {
	var ori = []int{1, 2, 3, 4}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		GenIntSlicePermutationWithDict(ori)
	}
}

/*
➜  permutation go test -v -run=none -bench=. -benchmem
goos: darwin
goarch: amd64
pkg: demo/alg/permutation
cpu: Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz
BenchmarkGenIntSlicePermutation
BenchmarkGenIntSlicePermutation-8                 421508              2435 ns/op            2304 B/op         61 allocs/op
BenchmarkGenIntSlicePermutationWithDict
BenchmarkGenIntSlicePermutationWithDict-8         483420              2494 ns/op            2304 B/op         61 allocs/op
PASS
ok      demo/alg/permutation    2.587s
*/