当前位置:首页 > 文章列表 > Golang > Go教程 > 解决Go中使用seed得到相同随机数的问题

解决Go中使用seed得到相同随机数的问题

来源:脚本之家 2022-12-24 11:59:19 0浏览 收藏

本篇文章向大家介绍《解决Go中使用seed得到相同随机数的问题》,主要包括随机数、goseed,具有一定的参考价值,需要的朋友可以参考一下。

1. 重复的随机数

废话不多说,首先我们来看使用seed的一个很神奇的现象。

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

<p>可能不熟悉seed用法的看到这里会很疑惑,我不是都用了seed吗?为何我随机出来的数字都是一样的?不应该每次都不一样吗?</p>
<p>可能会有人说是你数据的样本空间太小了,OK,我们加大样本空间到10w再试试。</p>

<pre class="brush:plain;">
func main() {
  for i := 0; i 

<p>你会发现结果仍然是一样的。简单的推理一下我们就能知道,在上面那种情况,每次都取到相同的随机数跟我们所取的样本空间大小是无关的。那么唯一有关的就是seed。我们首先得明确seed的用途。</p>
<p><span style="color: #ff0000"><strong>2. seed的用途</strong></span><br></p>
<p>在这里就不卖关子了,先给出结论。</p>
<p>上面每次得到相同随机数是因为在上面的循环中,每次操作的间隔都在毫秒级下,所以每次通过time.Now().Unix()取出来的时间戳都是同一个值,换句话说就是使用了同一个seed。</p>
<p>这个其实很好验证。只需要在每次循环的时候将生成的时间戳打印出来,你就会发现每次打印出来的时间戳都是一样的。</p>
<p>每次rand都会使用相同的seed来生成随机队列,这样一来在循环中使用相同seed得到的随机队列都是相同的,而生成随机数时每次都会去取同一个位置的数,所以每次取到的随机数都是相同的。</p>
<p>seed 只用于决定一个确定的随机序列。不管seed多大多小,只要随机序列一确定,本身就不会再重复。除非是样本空间太小。解决方案有两种:</p>
<p>在全局初始化调用一次seed即可<br>
每次使用纳秒级别的种子(强烈不推荐这种)<br></p>
<p><span style="color: #ff0000"><strong>3. 不用每次调用</strong></span><br></p>
<p>上面的解决方案建议各位不要使用第二种,给出是因为在某种情况下的确可以解决问题。比如在你的服务中使用这个seed的地方是串行的,那么每次得到的随机序列的确会不一样。</p>
<p>但是如果在高并发下呢?你能够保证每次取到的还是不一样的吗?事实证明,在高并发下,即使使用UnixNano作为解决方案,同样会得到相同的时间戳,Go官方也不建议在服务中同时调用。</p>
<p>Seed should not be called concurrently with any other Rand method.</p>
<p>接下来会带大家了解一下代码的细节。想了解源码的可以继续读下去。</p>
<p><span style="color: #ff0000"><strong>4. 源码解析-seed</strong></span><br></p>
<p>4.1 seed<br></p>
<p>首先来看一下seed做了什么。</p>

<pre class="brush:plain;">
func (rng *rngSource) Seed(seed int64) {
  rng.tap = 0
  rng.feed = rngLen - rngTap

  seed = seed % int32max
  if seed = 0 {
      var u int64
      u = int64(x) 

<p>首先,seed赋值了两个定义好的变量,rng.tap和rng.feed。rngLen和rngTap是两个常量。我们来看一下相关的常量定义。</p>

<pre class="brush:plain;">
const (
  rngLen  = 607
  rngTap  = 273
  rngMax  = 1 

<p>由此可见,无论seed是否相同,这两个变量的值都不会受seed的影响。同时,seed的值会最终决定x的值,只要seed相同,则得到的x就相同。而且无论seed是否被赋值,只要检测到是零值,都会默认的赋值为89482311。</p>
<p>接下来我们再看seedrand。</p>
<p>4.2 seedrand<br></p>

<pre class="brush:plain;">
// seed rng x[n+1] = 48271 * x[n] mod (2**31 - 1)
func seedrand(x int32) int32 {
  const (
    A = 48271
    Q = 44488
    R = 3399
  )

  hi := x / Q    // 取除数
  lo := x % Q    // 取余数
  x = A*lo - R*hi // 通过公式重新给x赋值
  if x 

<p></p>
<p>可以看出,只要传入的x相同,则最后输出的x一定相同。进而最后得到的随机序列rng.vec就相同。</p>
<p>到此我们验证我们最开始给出的结论,即只要每次传入的seed相同,则生成的随机序列就相同。验证了这个之后我们再继续验证为什么每次取到的随机序列的值都是相同的。</p>
<p><span style="color: #ff0000"><strong>5. 源码解析-Intn</strong></span><br></p>
<p>首先举个例子,来直观的描述上面提到的问题。</p>
<p></p>

<pre class="brush:plain;">
func printRandom() {
 for i := 0; i 

<p>假设printRandom是一个单独的Go文件,那么你无论run多少次,每次打印出来的随机序列都是一样的。通过阅读seed的源码我们知道,这是因为生成了相同的随机序列。那么为什么会每次都取到同样的值呢?不说废话,我们一层一层来看。</p>
<p><strong>5.1 Intn</strong><br></p>

<pre class="brush:plain;">
func (r *Rand) Intn(n int) int {
  if n 

<p>可以看到,如果n小于等于0,就会直接panic。其次,会根据传入的数据类型,返回对应的类型。</p>
<p>虽然说这里调用分成了Int31n和Int63n,但是往下看的你会发现,其实都是调用的r.Int63(),只不过在返回64位的时候做了一个右移的操作。</p>

<pre class="brush:js;">
// r.Int31n的调用
func (r *Rand) Int31() int32 { return int32(r.Int63() >> 32) }

// r.Int63n的调用
func (r *Rand) Int63() int64 { return r.src.Int63() }

5.2 Int63

先给出这个函数的相关代码。

// 返回一个非负的int64伪随机数.
func (rng *rngSource) Int63() int64 {
  return int64(rng.Uint64() & rngMask)
}

func (rng *rngSource) Uint64() uint64 {
  rng.tap--
  if rng.tap 

<p>可以看到,无论是int31还是int63,最终都会进入Uint64这个函数中。而在这两个函数中,这两个变量的值显得尤为关键。因为直接决定了最后得到的随机数,这两个变量的赋值如下。</p>

<pre class="brush:plain;">
rng.tap = 0
rng.feed = rngLen - rngTap

tap的值是常量0,而feed的值决定于rngLen和rngTap,而这两个变量的值也是一个常量。如此,每次从随机队列中取到的值都是确定的两个值的和。

到这,我们也验证了只要传入的seed相同,并且每次都调用seed方法,那么每次随机出来的值一定是相同的。

6. 结论

首先评估是否需要使用seed,其次,使用seed只需要在全局调用一次即可,如果多次调用则有可能取到相同随机数。

总结

以上所述是小编给大家介绍的解决Go中使用seed得到相同随机数的问题,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对golang学习网网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

以上就是《解决Go中使用seed得到相同随机数的问题》的详细内容,更多关于golang的资料请关注golang学习网公众号!

版本声明
本文转载于:脚本之家 如有侵犯,请联系study_golang@163.com删除
浅谈Go中数字转换字符串的正确姿势浅谈Go中数字转换字符串的正确姿势
上一篇
浅谈Go中数字转换字符串的正确姿势
使用go在mangodb中进行CRUD操作
下一篇
使用go在mangodb中进行CRUD操作
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    542次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    511次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    498次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    484次学习
查看更多
AI推荐
  • AI边界平台:智能对话、写作、画图,一站式解决方案
    边界AI平台
    探索AI边界平台,领先的智能AI对话、写作与画图生成工具。高效便捷,满足多样化需求。立即体验!
    418次使用
  • 讯飞AI大学堂免费AI认证证书:大模型工程师认证,提升您的职场竞争力
    免费AI认证证书
    科大讯飞AI大学堂推出免费大模型工程师认证,助力您掌握AI技能,提升职场竞争力。体系化学习,实战项目,权威认证,助您成为企业级大模型应用人才。
    425次使用
  • 茅茅虫AIGC检测:精准识别AI生成内容,保障学术诚信
    茅茅虫AIGC检测
    茅茅虫AIGC检测,湖南茅茅虫科技有限公司倾力打造,运用NLP技术精准识别AI生成文本,提供论文、专著等学术文本的AIGC检测服务。支持多种格式,生成可视化报告,保障您的学术诚信和内容质量。
    561次使用
  • 赛林匹克平台:科技赛事聚合,赋能AI、算力、量子计算创新
    赛林匹克平台(Challympics)
    探索赛林匹克平台Challympics,一个聚焦人工智能、算力算法、量子计算等前沿技术的赛事聚合平台。连接产学研用,助力科技创新与产业升级。
    663次使用
  • SEO  笔格AIPPT:AI智能PPT制作,免费生成,高效演示
    笔格AIPPT
    SEO 笔格AIPPT是135编辑器推出的AI智能PPT制作平台,依托DeepSeek大模型,实现智能大纲生成、一键PPT生成、AI文字优化、图像生成等功能。免费试用,提升PPT制作效率,适用于商务演示、教育培训等多种场景。
    570次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码