数字滚动随机生成器_我用 Go 生成的随机数为什么不随机?随机数是怎样产生的...

0e5181d9fa3dfcbdeb5246b8dc723f0e.png

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 

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

81874759

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

1779

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

播种

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

func main() {   rand.Seed(2)   for i := 0; i 

这些是新的结果:

86869240

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

7097f6eea56396db04e146bfb1032304.png

The sequence is pre-generated at the bootstrap

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

func main() {   rand.Seed(time.Now().UnixNano())   for i := 0; i 

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

随机数生成器

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

func main() {   for i := 0; i 

这是结果:

12245619

多次运行这个程序将会得到不同的结果。在内部,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/opRandWithCrypto-8  272ns ± 3%name    time/opRandWithMath-8   22.8ns ± 4%

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

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

func main() {   gRand := rand.New(rand.NewSource(1).(rand.Source64))   for i := 0; i 

性能会更好:

name                  time/opRandWithMathNoLock-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/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,您的问题是关于Python的随机密码生成和连续质数计算。以下是一个使用整数17作为随机数种子生成随机密码的示例代码: ```python import random # 定义密码长度 password_length = 8 # 定义密码字符集 password_charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" # 使用整数17作为随机数种子 random.seed(17) # 生成随机密码 password = "" for i in range(password_length): password += random.choice(password_charset) print("随机密码为:", password) ``` 在上面的代码中,我们首先定义了密码长度和密码字符集,然后使用`random.seed(17)`来设置随机数种子,确保每次运行程序时生成随机密码都是相同的。接着,我们使用`random.choice(password_charset)`随机选择密码字符集中的字符,并将其添加到密码字符串中,直到生成的密码达到所需的长度为止。 接下来,以下是一个计算连续质数的示例代码: ```python # 定义起始数和结束数 start_num = 1 end_num = 100 # 计算连续质数 prime_list = [] for num in range(start_num, end_num): if all(num % i != 0 for i in range(2, int(num ** 0.5) + 1)): prime_list.append(num) # 输出结果 print("从", start_num, "到", end_num, "之间的连续质数为:", prime_list) ``` 在上面的代码中,我们首先定义了起始数和结束数,然后使用一个for循环遍历从起始数到结束数之间的所有数字。在每次迭代中,我们使用`all`函数和一个生成表达式来判断当前数字是否为质数。如果当前数字是质数,则将其添加到一个列表中。最后,我们输出列表中的所有质数。 希望这可以解决您的问题!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值