1- # 各种洗牌算法的Go语言实现
1+ # 洗牌算法(Shuffle Algorithm)
22
33# 一、支持的洗牌算法
44
1515go get -u github.com/golang-infrastructure/go-shuffle
1616```
1717
18- # 三、Fisher–Yates-Knuth洗牌算法
18+ # 三、API代码示例
1919
20- 假设现在有一个数组:
21-
22- ```
23- [1, 2, 3, 4, 5]
24- ```
25-
26- 从最右边的坐标` len(slice)-1 ` 开始作为` right_index ` ,每次从` [0, right_index] ` 随机选择一个下标,将选中的下标的值与` right_index ` 交换,并将` right_index ` 减一往左偏移。
27-
28- 代码示例:
20+ ## 3.1 对切片shuffle
2921
3022``` go
3123package main
@@ -37,16 +29,104 @@ import (
3729
3830func main () {
3931
32+ // 对切片中的元素shuffle
4033 slice := []int {1 , 2 , 3 , 4 , 5 }
41- shuffle.FisherYatesKnuthShuffle (slice)
34+ shuffle.Shuffle (slice)
4235 fmt.Println (slice)
4336 // Output:
4437 // [5 1 2 3 4]
4538
4639}
4740```
4841
49- # 四、Scatology算法
42+ ## 3.2 对矩阵shuffle
43+
44+ ``` go
45+ package main
46+
47+ import (
48+ " fmt"
49+ " github.com/golang-infrastructure/go-shuffle"
50+ )
51+
52+ func main () {
53+
54+ // 对二维矩阵中的元素shuffle
55+ matrix := [][]int {
56+ {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 },
57+ {11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 },
58+ {21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 },
59+ {31 , 32 , 33 , 34 , 35 , 36 , 37 , 38 , 39 , 40 },
60+ }
61+ // 注意可能会返回错误,比如二维数组每行长度不一致则无法shuffle
62+ err := shuffle.ShuffleMatrix (matrix)
63+ if err != nil {
64+ fmt.Println (" Shuffle matrix failed: " + err.Error ())
65+ return
66+ }
67+ fmt.Println (matrix)
68+ // Output:
69+ // [[11 40 6 23 15 28 4 7 37 21] [29 26 33 5 35 13 22 32 19 34] [31 30 36 20 2 10 24 39 9 27] [16 8 18 14 1 17 38 12 25 3]]
70+
71+ }
72+ ```
73+
74+ # 四、Fisher–Yates-Knuth洗牌算法
75+
76+ 假设现在有一个数组:
77+
78+ ```
79+ [1, 2, 3, 4, 5]
80+ ```
81+
82+ 从最右边的坐标` len(slice)-1 ` 开始作为` right_index ` ,每次从` [0, right_index] ` 随机选择一个下标,将选中的下标的值与` right_index ` 交换,并将` right_index ` 减一往左偏移。
83+
84+ 代码示例:
85+
86+ ``` go
87+ // 使用自己独立的随机数生成器,与其它的调用区分开
88+ var standaloneRand = rand.New (rand.NewSource (time.Now ().Unix ()))
89+
90+ // FisherYatesKnuthShuffle Fisher–Yates-Knuth Shuffle或 算法对一维数组洗牌,O(n)
91+ func FisherYatesKnuthShuffle [T any](slice []T ) {
92+ for index := len (slice) - 1 ; index > 0 ; index-- {
93+ chooseIndex := standaloneRand.Intn (index + 1 )
94+ slice[chooseIndex], slice[index] = slice[index], slice[chooseIndex]
95+ }
96+ }
97+ ```
98+
99+ 我们对上面的算法扩展一下,很容易就能得到矩阵的shuffle算法,将矩阵的每一行看做是拼接起来的一维数组,则将对矩阵进行shuffle的算法转换为了对切片shufle的算法,而对切片进行shuffle我们已经实现过了,API代码示例:
100+
101+ ``` go
102+ // FisherYatesShuffleMatrix Fisher–Yates-Knuth shuffle算法对矩阵洗牌
103+ func FisherYatesShuffleMatrix [T any](matrix [][]T ) error {
104+
105+ // 参数检查
106+ if err := check (matrix); err != nil {
107+ return err
108+ }
109+
110+ row , col := len (matrix), len (matrix[0 ])
111+ for index := row*col - 1 ; index > 0 ; index-- {
112+ chooseIndex := standaloneRand.Intn (index + 1 )
113+ matrix[index/col][index%col], matrix[chooseIndex/col][chooseIndex%col] = matrix[chooseIndex/col][chooseIndex%col], matrix[index/col][index%col]
114+ }
115+
116+ return nil
117+ }
118+
119+ // 需要保证传入的二维数据是一个矩阵,否则后面可能会越界panic
120+ func check[T any](matrix [][]T ) error {
121+ for i := 1 ; i < len (matrix); i++ {
122+ if len (matrix[i]) != len (matrix[i-1 ]) {
123+ return ErrMatrixUnavailable
124+ }
125+ }
126+ return nil
127+ }
128+ ```
50129
51- 就是在Fisher–Yates-Knuth的基础上随机选择的时候不再选择最右边的 ` [0,right_index) ` ,但是感觉这样子似乎可能会有概率问题?
130+ # 五、Scatology算法
52131
132+ 就是在Fisher–Yates-Knuth的基础上随机选择的时候不再选择最右边的` [0,right_index) ` ,不再展开详解。
0 commit comments