Golang中math/rand与crypto/rand的区别解析

Golang中math/rand与crypto/rand的区别解析 大家好!

首先感谢所有花时间阅读这篇文章的人。 我是Go语言的新手,刚刚发现了两个随机数生成包,并对它们之间的差异感到着迷。

非常感谢Saito San。这非常有帮助。 我查看了随机数生成器列表 没有关于LFG的评论。 我想了解LFG和Mersenne Twister的比较。 因为我计划使用Mersenne Twister(MT)或更好的算法。 有谁知道MT是否比LFG更好? Steve

这个讨论非常有启发性,但对于像我一样刚接触Go语言的人来说,真正看到随机数生成器的随机性表现可能会很有趣。

我写的小程序基本上是我尝试理解Rob Pike的fan-in模式。 Rob Pike的代码在这里:Go Playground - The Go Programming Language

我写了一个类似的程序,其中一个生成器生成1到6之间的随机数,模拟掷骰子。 两个这样的生成器同时启动,然后一个收集器goroutine将每个生成器的计数相加。

这个例子使用了math/rand:

Go Playground - The Go Programming Language

而这个例子使用了crypto/rand:

Go Playground - The Go Programming Language

第一个例子在所有10次掷骰中可能只有2种可能的结果,通常只有1种! 第二个更符合我对掷骰子的预期。有时A赢,有时B赢。

所以,我学到的是:每当我需要数字是随机的,比如在任何游戏中,我需要使用crypto/rand。

我的理解有误吗? 也非常欢迎对代码的任何评论。

谢谢, Raphael


更多关于Golang中math/rand与crypto/rand的区别解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html

11 回复

又一个好方法!非常感谢!!

非常巧妙

更多关于Golang中math/rand与crypto/rand的区别解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


感谢戈麦斯先生。

非常感谢!

明白了!感谢提供宝贵信息!

通常在游戏开始时这样做:

rand.Seed(time.Now().UnixNano())

在我的电脑上运行正常。这一定是 Go Playground 的某些特性造成的。

真有意思。

我尝试了这种方法,至少在 Go Playground 中,种子值在几分钟内保持不变。 请看这里: https://play.golang.org/p/jGi7LjzFUQC

啊!非常感谢! 那是不是要用 rand.Seed(seed int64) 方法? 但在游戏中我该怎么做呢? 我得找个种子来源…

确实如此,在 Go 语言游乐场中时间是固定的,正如此处所解释的:

在游乐场中,时间起始于 2009-11-10 23:00:00 UTC(确定这个日期的意义留给读者作为练习)。通过提供确定性输出,这使得缓存程序变得更加容易。

关于这个主题的更多信息:

官方的 ioutil.TempFile 函数(参见 https://golang.org/src/io/ioutil/tempfile.go)用于创建具有随机名称的临时文件,它使用了简单的《数值食谱》方法,并使用 time.Now().UnixNano() + int64(os.Getpid()) 作为种子。

func main() {
    fmt.Println("hello world")
}

raphbaph:

因此,我了解到的是:每当我在任何游戏中需要随机数时,都需要使用 crypto/rand。

我的理解有误吗?

你没有注意到可以为 math/rand 包更改种子值来生成不同的数字序列,同时也可以作为生成器的源。 math 包中的随机数生成器使用某些已知算法生成伪随机数,而 crypto 包中的随机数生成器在可能的情况下会使用真正的随机源。但后者的代价要高得多。

在我看来,math/rand 包可以用于简单的游戏,但在需要真正随机性的场景下则不适用。

// 代码示例:使用 math/rand 生成随机数
package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().UnixNano())
    fmt.Println("随机数:", rand.Intn(100))
}

在Go语言中,math/randcrypto/rand是两个用于生成随机数的包,但它们在设计目标、随机性质量和适用场景上有本质区别。你的测试结果正确反映了这一点:math/rand生成伪随机数,适合非安全场景,而crypto/rand提供密码学安全的随机数,适用于安全敏感应用。以下是对两个包的详细解析和代码示例。

1. math/rand:伪随机数生成器

  • 原理:基于确定性算法(如线性同余生成器)生成序列,初始种子相同则输出序列相同。
  • 性能:速度快,资源开销低。
  • 随机性:适用于模拟、测试、游戏等非安全场景,但随机性可预测。
  • 示例代码
package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	// 使用时间作为种子,确保每次运行结果不同
	rand.Seed(time.Now().UnixNano())
	fmt.Println("掷骰子结果:", rand.Intn(6) + 1) // 生成1到6的随机数
}

2. crypto/rand:密码学安全随机数生成器

  • 原理:从操作系统获取熵源(如硬件噪声),生成不可预测的随机数。
  • 性能:速度较慢,资源开销较高。
  • 随机性:适用于密钥生成、令牌、加密等安全敏感场景。
  • 示例代码
package main

import (
	"crypto/rand"
	"encoding/binary"
	"fmt"
)

func main() {
	var n int32
	// 读取随机字节并解码为int32
	binary.Read(rand.Reader, binary.BigEndian, &n)
	if n < 0 {
		n = -n // 确保非负
	}
	fmt.Println("掷骰子结果:", (n % 6) + 1) // 生成1到6的随机数
}

关键区别总结

特性 math/rand crypto/rand
随机性质量 伪随机,可预测 密码学安全,不可预测
性能
适用场景 游戏、模拟、测试 加密、认证、安全令牌
种子依赖 需显式设置种子 自动从系统熵源获取

针对你的代码分析

  • 在第一个示例(使用math/rand)中,由于未设置种子或种子固定,导致输出序列重复,这在伪随机数生成中是典型行为。
  • 第二个示例(使用crypto/rand)利用了系统熵源,结果更接近真实随机分布,符合掷骰子的预期。

因此,你的理解正确:在需要不可预测随机性的场景(如游戏输赢判定),应使用crypto/rand。对于非关键用途(如测试数据生成),math/rand更高效。根据需求选择包是Go开发中的标准实践。

回到顶部