Skip to content

Commit 0d21ce8

Browse files
committed
add matrix check & examples & doc
1 parent c617a13 commit 0d21ce8

File tree

10 files changed

+189
-25
lines changed

10 files changed

+189
-25
lines changed

Diff for: README.md

+94-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# 各种洗牌算法的Go语言实现
1+
# 洗牌算法(Shuffle Algorithm)
22

33
# 一、支持的洗牌算法
44

@@ -15,17 +15,9 @@
1515
go 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
3123
package main
@@ -37,16 +29,104 @@ import (
3729

3830
func 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)`,不再展开详解。

Diff for: errors.go

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package shuffle
2+
3+
import "errors"
4+
5+
var ErrMatrixUnavailable = errors.New("matrix unavailable")

Diff for: examples/shuffle_matrix/main.go

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"github.com/golang-infrastructure/go-shuffle"
6+
)
7+
8+
func main() {
9+
10+
// 对二维矩阵中的元素shuffle
11+
matrix := [][]int{
12+
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
13+
{11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
14+
{21, 22, 23, 24, 25, 26, 27, 28, 29, 30},
15+
{31, 32, 33, 34, 35, 36, 37, 38, 39, 40},
16+
}
17+
// 注意可能会返回错误,比如二维数组每行长度不一致则无法shuffle
18+
err := shuffle.ShuffleMatrix(matrix)
19+
if err != nil {
20+
fmt.Println("Shuffle matrix failed: " + err.Error())
21+
return
22+
}
23+
fmt.Println(matrix)
24+
// Output:
25+
// [[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]]
26+
27+
}

Diff for: examples/main.go renamed to examples/shuffle_slice/main.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import (
77

88
func main() {
99

10+
// 对切片中的元素shuffle
1011
slice := []int{1, 2, 3, 4, 5}
11-
shuffle.FisherYatesKnuthShuffle(slice)
12+
shuffle.Shuffle(slice)
1213
fmt.Println(slice)
1314
// Output:
1415
// [5 1 2 3 4]

Diff for: fisher_yates_knuth_shuffle.go

+19-3
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,28 @@ func FisherYatesKnuthShuffle[T any](slice []T) {
1717
}
1818

1919
// FisherYatesShuffleMatrix Fisher–Yates-Knuth shuffle算法对矩阵洗牌
20-
func FisherYatesShuffleMatrix[T any](matrix [][]T) {
21-
// TODO 检查参数是否符合矩阵
22-
// TODO 提高性能
20+
func FisherYatesShuffleMatrix[T any](matrix [][]T) error {
21+
22+
// 参数检查
23+
if err := check(matrix); err != nil {
24+
return err
25+
}
26+
2327
row, col := len(matrix), len(matrix[0])
2428
for index := row*col - 1; index > 0; index-- {
2529
chooseIndex := standaloneRand.Intn(index + 1)
2630
matrix[index/col][index%col], matrix[chooseIndex/col][chooseIndex%col] = matrix[chooseIndex/col][chooseIndex%col], matrix[index/col][index%col]
2731
}
32+
33+
return nil
34+
}
35+
36+
// 需要保证传入的二维数据是一个矩阵,否则后面可能会越界panic
37+
func check[T any](matrix [][]T) error {
38+
for i := 1; i < len(matrix); i++ {
39+
if len(matrix[i]) != len(matrix[i-1]) {
40+
return ErrMatrixUnavailable
41+
}
42+
}
43+
return nil
2844
}

Diff for: fisher_yates_knuth_shuffle_test.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package shuffle
22

3-
import "testing"
3+
import (
4+
"github.com/stretchr/testify/assert"
5+
"testing"
6+
)
47

58
func TestFisherYatesShuffle(t *testing.T) {
69
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
@@ -15,6 +18,7 @@ func TestFisherYatesShuffleMatrix(t *testing.T) {
1518
{21, 22, 23, 24, 25, 26, 27, 28, 29, 30},
1619
{31, 32, 33, 34, 35, 36, 37, 38, 39, 40},
1720
}
18-
FisherYatesShuffleMatrix(matrix)
21+
err := FisherYatesShuffleMatrix(matrix)
22+
assert.Nil(t, err)
1923
t.Log(matrix)
2024
}

Diff for: go.mod

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
module github.com/golang-infrastructure/go-shuffle
22

33
go 1.18
4+
5+
require (
6+
github.com/davecgh/go-spew v1.1.1 // indirect
7+
github.com/pmezard/go-difflib v1.0.0 // indirect
8+
github.com/stretchr/objx v0.5.0 // indirect
9+
github.com/stretchr/testify v1.8.1 // indirect
10+
gopkg.in/yaml.v3 v3.0.1 // indirect
11+
)

Diff for: go.sum

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
5+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
6+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
7+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
8+
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
9+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
10+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
11+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
12+
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
13+
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
14+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
15+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
16+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
17+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

Diff for: sattolo.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,18 @@ func SattoloShuffle[T any](slice []T) {
88
}
99
}
1010

11-
func SattoloShuffleMatrix[T any](matrix [][]T) {
11+
func SattoloShuffleMatrix[T any](matrix [][]T) error {
12+
13+
// 参数检查
14+
if err := check(matrix); err != nil {
15+
return err
16+
}
17+
1218
row, col := len(matrix), len(matrix[0])
1319
for index := row*col - 1; index > 0; index-- {
14-
// TODO 检查参数是否符合矩阵
15-
// TODO 提高性能
1620
chooseIndex := standaloneRand.Intn(index)
1721
matrix[index/col][index%col], matrix[chooseIndex/col][chooseIndex%col] = matrix[chooseIndex/col][chooseIndex%col], matrix[index/col][index%col]
1822
}
23+
24+
return nil
1925
}

Diff for: shuffle.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ func Shuffle[T any](slice []T) {
66
}
77

88
// ShuffleMatrix 对矩阵中的元素进行洗牌
9-
func ShuffleMatrix[T any](matrix [][]T) {
10-
FisherYatesShuffleMatrix(matrix)
9+
func ShuffleMatrix[T any](matrix [][]T) error {
10+
return FisherYatesShuffleMatrix(matrix)
1111
}

0 commit comments

Comments
 (0)