为了账号安全,请及时绑定邮箱和手机立即绑定

在 golang 中保存随机状态

在 golang 中保存随机状态

Go
隔江千里 2021-09-27 16:03:58
有没有办法在 golang math/rand 包中保存随机状态?我想将它序列化并存储以备后用,但随机状态是一个接口,接口下的具体结构未导出(因此 json.Marshall 显然不能使用)。作为保存 rand.Source 对象的替代方法,我想只保存底层的 int64 种子值。您可以使用 rand.Seed 设置它,但我没有看到获取种子值的方法,以便可以保留以供以后使用。
查看完整描述

2 回答

?
LEATH

TA贡献1936条经验 获得超6个赞

正如您已经注意到的那样,该math/rand包不会让您深入了解(伪)随机数生成器的当前状态(种子)。如果你需要这个,你必须自己实现它(如 Anonymous 所提到的)。


如果您愿意:好的,假设您知道随机数生成器的内部状态等同于1234将设置种子值的位置。这比种子是任何其他具体或“随机”数字有什么好处?


以下是如何“模拟”访问生成器种子值的提示:


假设您已经创建了Rand对象,已经设置并使用了它(已经生成了一些随机数)。这个Rand对象也可能是math/rand包的默认/全局对象,它不必是一个不同的Rand.


您到达了一个点,您希望按顺序保存随机数生成器的“状态”,以便稍后您可以从该点重复精确的伪随机序列。这将需要获取当前种子,稍后当您想从这一点开始重复序列时,您只需将存储的种子设置为Rand对象。问题:您无法访问的种子。


但是您可以做的是设置一个新种子,然后您就会知道内部种子就是您刚刚设置的那个!所以要模拟一个GetSeed()(在包的默认值Rand上math/rand):


func GetSeed() int64 {

    seed := time.Now().UnixNano() // A new random seed (independent from state)

    rand.Seed(seed)

    return seed

}

模拟一个GetSeed()或任何Rand(不是默认的):


func GetSeed2(r rand.Rand) int64 {

    seed := time.Now().UnixNano() // A new random seed (independent from state)

    r.Seed(seed)

    return seed

}

保留随机性的“伪”部分

上面提出的GetSeed()使用当前时间来“重新播种”Rand对象。这将根据它被调用的时间改变伪随机序列。这可能好也可能不好(在大多数情况下这不是问题)。


但是如果是这样,我们可以避免这种情况,如果我们使用Rand对象本身来指定新种子,就像这样(通过这样做,新种子将只取决于当前状态 - 当前种子):


func GetSeed() int64 {

    seed := rand.Int63() // A new pseudo-random seed: determined by current state/seed

    rand.Seed(seed)

    return seed

}


func GetSeed2(r rand.Rand) int64 {

    seed := r.Int63() // A new pseudo-random seed: determined by current state/seed

    r.Seed(seed)

    return seed

}

笔记:


该GetSeed()功能旨在偶尔使用,例如当您想保存游戏时。正常用法是生成数千个随机数并且只调用GetSeed() 一次。


正如 Anonymous 所指出的,使用伪随机数生成器的当前算法,在某些极端情况下(例如您GetSeed()在每个生成的随机数之后调用)这可能会导致生成的随机数出现循环并返回之前生成的序列的随机数。序列的长度可能在几千左右。这是一个序列长度为 8034 的示例:Repetition Go Playground Example。


但同样:这不是正常用法,GetSeed()在每次随机数生成后有意调用;这仅适用于您使用Rand自身生成新种子的情况。Rand例如,如果您使用当前时间重新设置对象,则不会发生重复。


查看完整回答
反对 回复 2021-09-27
  • 2 回答
  • 0 关注
  • 181 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信