Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > go语言 洗牌算法

go语言实现FisherYates Shuffle洗牌算法(附带源码)

作者:南城花随雪。

本文主要介绍了go语言实现FisherYates Shuffle洗牌算法(附带源码),分别介绍了三种实现版本,并详细解释了每种版本的实现逻辑和使用场景,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧

一、项目背景详细介绍

在算法设计与工程开发中,“随机打乱”是一个非常常见的需求。例如:

很多初学者会写出类似这样的“伪随机”洗牌代码:

for i := 0; i < n; i++ {
j := rand.Intn(n)
arr[i], arr[j] = arr[j], arr[i]
}

但这种写法并不能保证所有排列的概率相等。

真正能够保证:

每一种排列出现的概率都完全相同

的算法是 —— Fisher–Yates Shuffle

二、Fisher–Yates Shuffle 算法介绍

Fisher–Yates Shuffle 又被称为:

该算法最早由统计学家:

提出,用于统计抽样。

三、项目需求详细介绍

本项目目标:

使用 Go 语言完整实现 Fisher–Yates Shuffle 算法,并提供完整教学级代码示例。

功能要求

四、相关技术详细介绍

1. Fisher–Yates 算法原理

假设数组长度为 n。

算法步骤:

从最后一个元素开始:

for i := n-1 down to 1:
j = random(0, i)
swap(arr[i], arr[j])

核心思想:

每次将当前位置与前面任意一个元素交换。

2. 为什么是等概率?

对于长度为 n 的数组:

总排列数:n!

每个排列概率:1 / n!

3. 时间复杂度

指标数值
时间复杂度O(n)
空间复杂度O(1)
是否原地
是否稳定无意义(随机)

4. Go 语言随机数说明

Go 提供两种随机源:

math/rand

crypto/rand

五、实现思路详细介绍

我们将实现三种版本:

第一种:基础 int 版本

步骤:

第二种:泛型版本(Go 1.18+)

使用:

func Shuffle[T any](arr []T)

第三种:安全版本

使用:

crypto/rand

并通过:

rand.Int(rand.Reader, big.NewInt(int64(i+1)))

生成安全随机数。

六、完整实现代码

// =========================
// file: main.go
// =========================
package main
import (
	"crypto/rand"
	"fmt"
	mrand "math/rand"
	"math/big"
	"time"
)
//////////////////////////////////////////////////////////
// 1. 基础版本:int 类型 Fisher-Yates Shuffle
//////////////////////////////////////////////////////////
// ShuffleInt 使用 math/rand 实现 int 类型洗牌
func ShuffleInt(arr []int) {
	n := len(arr)
	// 从后往前遍历
	for i := n - 1; i > 0; i-- {
		// 生成 [0, i] 之间的随机整数
		j := mrand.Intn(i + 1)
		// 交换元素
		arr[i], arr[j] = arr[j], arr[i]
	}
}
//////////////////////////////////////////////////////////
// 2. 泛型版本(Go 1.18+)
//////////////////////////////////////////////////////////
// Shuffle 泛型洗牌函数
func Shuffle[T any](arr []T) {
	n := len(arr)
	for i := n - 1; i > 0; i-- {
		j := mrand.Intn(i + 1)
		arr[i], arr[j] = arr[j], arr[i]
	}
}
//////////////////////////////////////////////////////////
// 3. 密码学安全版本
//////////////////////////////////////////////////////////
// ShuffleCrypto 使用 crypto/rand 实现安全洗牌
func ShuffleCrypto(arr []int) error {
	n := len(arr)
	for i := n - 1; i > 0; i-- {
		// 生成安全随机数
		r, err := rand.Int(rand.Reader, big.NewInt(int64(i+1)))
		if err != nil {
			return err
		}
		j := int(r.Int64())
		arr[i], arr[j] = arr[j], arr[i]
	}
	return nil
}
//////////////////////////////////////////////////////////
// 4. 测试主函数
//////////////////////////////////////////////////////////
func main() {
	// 设置伪随机种子
	mrand.Seed(time.Now().UnixNano())
	// 测试数组
	arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	fmt.Println("原始数组:", arr)
	// 基础版本
	ShuffleInt(arr)
	fmt.Println("ShuffleInt 后:", arr)
	// 泛型版本
	Shuffle(arr)
	fmt.Println("Shuffle[T] 后:", arr)
	// 安全版本
	err := ShuffleCrypto(arr)
	if err != nil {
		fmt.Println("安全洗牌出错:", err)
		return
	}
	fmt.Println("ShuffleCrypto 后:", arr)
}

七、代码详细解读(只解读方法作用)

ShuffleInt

Shuffle[T]

ShuffleCrypto

八、项目详细总结

通过本项目你掌握:

九、常见问题及解答

Q1:为什么不能用随机交换?

因为概率不均匀。

Q2:是否适合并发?

不适合。必须顺序执行。

Q3:能否用于链表?

不适合,效率低。

Q4:生产环境用哪个?

场景推荐
一般系统math/rand
安全系统crypto/rand

十、扩展方向与性能优化

1. 可插拔随机源设计

type Random interface {
Intn(n int) int
}

2. 支持结构体切片

泛型已支持。

3. Benchmark 测试

go test -bench .

4. 并发随机源池

可使用 sync.Pool 优化。

Fisher–Yates Shuffle 是:

在工程实践中极其重要。

到此这篇关于go语言实现FisherYates Shuffle洗牌算法(附带源码)的文章就介绍到这了,更多相关go语言 洗牌算法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文