您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Go语言

我用 Go语言 生成的随机数为什么不随机?随机数是怎样产生的

时间:2020-07-13 10:06:45  来源:  作者:
我用 Go 生成的随机数为什么不随机?随机数是怎样产生的

 

Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee French.

这篇文章基于 Go 1.13 版本

Go 实现了两个包来产生随机数:

  • 在包 math/rand 的一个伪随机数生成器( PRNG )
  • 在包 crypto/rand 中实现的加密伪随机数生成器( CPRNG )

如果这两个包都产生了随机数,你需要在真正的随机数和性能之间寻找平衡。

确定的结果

Go 的 rand 包会使用相同的源来产生一个确定的伪随机数序列。这个源会产生一个不变的数列,稍后在执行期间使用。将你的程序运行多次将会读到一个完全相同的序列并产生相同的结果。让我们用一个简单的例子来尝试一下:

func main() {
   for i := 0; i < 4; i++  {
      println(rand.Intn(100))
   }
}

多次运行这个程序将会产生相同的结果:

81
87
47
59

由于源代码已经发布到 Go 的官方标准库中,因此任何运行此程序的计算机都会得到相同的结果。但是,由于 Go 仅保留一个生成的数字序列,我们可能想知道 Go 是如何管理用户请求的时间间隔的。Go 实际上使用此数字序列来播种一个产生这个随机数的源,然后获取其请求间隔的模。例如,运行相同的程序,最大值为 10,则模 10 的结果相同。

1
7
7
9

让我们来看一下如何在每次运行我们的程序时得到不同的序列。

播种

Go 提供一个方法, Seed(see int64) ,该方法能让你初始化这个默认序列。默认情况下,它会使用变量 1。使用另一个变量将会提供一个新的序列,但会保持确定性:

func main() {
   rand.Seed(2)
   for i := 0; i < 4; i++  {
      println(rand.Intn(100))
   }
}

这些是新的结果:

86
86
92
40

在你每次运行这个程序时,这个序列将会保持不变。这是构建此序列的工作流:

我用 Go 生成的随机数为什么不随机?随机数是怎样产生的

 

The sequence is pre-generated at the bootstrap

获取一个全新序列的解决方案是使用一个在运行时能改变的变量,比如当前时间:

func main() {
   rand.Seed(time.Now().UnixNano())
   for i := 0; i < 3; i++  {
      println(rand.Intn(100))
   }
}

由于当前纳秒数在任何时刻都是不同的,因此这个程序每次运行都会使用一个不同的序列。然而,尽管这个序列在每次运行都是不同的,可这些数字仍是伪随机数。如果你准备牺牲性能来获得更好的随机性,那么 Go 已经为你提供了另一种实现方式。

随机数生成器

Go 的标准库也提供了一个适用于加密应用的随机数生成器。因此,理所当然的,生成的随机数并不固定,并且一定会提供更好的随机性。这有一个例子使用了这个新包 cryto/rand :

func main() {
   for i := 0; i < 4; i++  {
      n, _ := rand.Int(rand.Reader, big.NewInt(100))
      println(n.Int64())
   }
}

这是结果:

12
24
56
19

多次运行这个程序将会得到不同的结果。在内部,Go 应用了如下规则:

linux 和 FreeBSD 系统上,Reader 会使用 getrandom(2) (如果可用的话),否则使用 /dev/urandom。

在 OpenBSD 上,Reader 会使用 getentropy(2)。

在其他的类 Unix 系统上,Reader 会读取 /dev/urandom。

windows 系统上,Reader 会使用 CryptGenRandom API.

在 Wasm 上,Reader 会使用 Web Cryto API。

但是,获得更好的质量意味着性能降低,因为它必须执行更多的操作并且不能使用预生成的序列。

性能

为了理解生成随机数的两种不同方式之间的折衷,我基于先前的两个例子运行了一个基准测试。结果如下:

name    time/op
RandWithCrypto-8  272ns ± 3%
name    time/op
RandWithMath-8   22.8ns ± 4%

不出所料,crypto 包更慢一些。但是,如果你不用去处理安全的随机数,那么 math 包就足够了并且它将会给你提供最好的性能。

你也可以调整默认数字生成器,由于内部互斥锁的存在,它是并发安全的。如果生成器并不在并发环境下使用,那么你就可以在不使用锁的情况下创建你自己的生成器:

func main() {
   gRand := rand.New(rand.NewSource(1).(rand.Source64))
   for i := 0; i < 4; i++  {
      println(gRand.Intn(100))
   }
}

性能会更好:

name                  time/op
RandWithMathNoLock-8  10.7ns ± 4%

via:https://medium.com/a-journey-with-go/go-how-are-random-numbers-generated-e58ee8696999

作者:Vincent Blanchon[1]译者:sh1luo[2]校对:lxbwolf[3]

本文由 GCTT[4] 原创编译,Go 中文网[5] 荣誉推出

参考资料

[1]

Vincent Blanchon: https://medium.com/@blanchon.vincent

[2]

sh1luo: https://github.com/sh1luo

[3]

lxbwolf: https://github.com/lxbwolf

[4]

GCTT: https://github.com/studygolang/GCTT

[5]

Go 中文网: https://studygolang.com/



Tags:Go语言 随机数   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee French.这篇文章基于 Go 1.13 版本Go 实现了两个包来产生随机数: ...【详细内容】
2020-07-13  Tags: Go语言 随机数  点击:(71)  评论:(0)  加入收藏
▌简易百科推荐
zip 是一种常见的归档格式,本文讲解 Go 如何操作 zip。首先看看 zip 文件是如何工作的。以一个小文件为例:(类 Unix 系统下)$ cat hello.textHello!执行 zip 命令进行归档:$ zip...【详细内容】
2021-12-17  Go语言中文网    Tags:Go语言   点击:(12)  评论:(0)  加入收藏
大家好,我是 polarisxu。前段时间,Russ Cox 明确了泛型相关的事情,原计划在标准库中加入泛型相关的包,改放到 golang.org/x/exp 下。目前,Go 泛型的主要设计者 ianlancetaylor 完...【详细内容】
2021-11-30  Go语言中文网    Tags:slices 包   点击:(24)  评论:(0)  加入收藏
前言最近因为项目需要写了一段时间的 Go ,相对于 Java 来说语法简单同时又有着一些 Python 之类的语法糖,让人大呼”真香“。 但现阶段相对来说还是 Python 写的多一些,偶尔还...【详细内容】
2021-11-25  crossoverJie    Tags:Go   点击:(29)  评论:(0)  加入收藏
go-micro是基于 Go 语言用于开发的微服务的 RPC 框架,主要功能如下:服务发现,负载均衡 ,消息编码,请求/响应,Async Messaging,可插拔接口,最后这个功能牛p安装步骤安装proto...【详细内容】
2021-09-06    石老师小跟班  Tags:go-micro   点击:(196)  评论:(0)  加入收藏
GoLand 2021.2 EAP 5 现已发布。用户可以从工具箱应用程序中获得 EAP 构建,也可以从官方网站手动下载。并且从此 EAP 开始,只有拥有有效的 JetBrains 帐户才能加入该计划。手...【详细内容】
2021-06-29  IT实战联盟  今日头条  Tags:GoLand   点击:(185)  评论:(0)  加入收藏
作者:HDT3213今天给大家带来的开源项目是 Godis:一个用 Go 语言实现的 Redis 服务器。支持: 5 种数据结构(string、list、hash、set、sortedset) 自动过期(TTL) 发布订阅、地理位...【详细内容】
2021-06-18  HelloGitHub  今日头条  Tags:Go   点击:(125)  评论:(0)  加入收藏
统一规范篇合理规划目录本篇主要描述了公司内部同事都必须遵守的一些开发规矩,如统一开发空间,既使用统一的开发工具来保证代码最后的格式的统一,开发中对文件和代码长度的控制...【详细内容】
2021-05-18  1024课堂    Tags:Go语言   点击:(232)  评论:(0)  加入收藏
闭包概述 闭包不是Go语言独有的概念,在很多编程语言中都有闭包 闭包就是解决局部变量不能被外部访问的一种解决方案 是把函数当作返回值的一种应用 代码演示总体思想:在函数...【详细内容】
2021-05-14  HelloGo  今日头条  Tags:Go语言   点击:(223)  评论:(0)  加入收藏
一时想不开,想了解一下Go语言,于是安装了并体验了一下。下载1. 进入golang.google.cn 点击Download Go 2.选择对应的操作系统,点击后开始下载。 安装1. windows下执行傻瓜式安...【详细内容】
2021-05-12  程序员fearlazy  fearlazy  Tags:Go语言   点击:(236)  评论:(0)  加入收藏
1.简介channel是Go语言的一大特性,基于channel有很多值得探讨的问题,如 channel为什么是并发安全的? 同步通道和异步通道有啥区别? 通道为何会阻塞协程? 使用通道导致阻塞的协程...【详细内容】
2021-05-10  程序员麻辣烫  今日头条  Tags:Go通道   点击:(272)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条