繁体   English   中英

生成无副作用的随机数

[英]Generate random number with no side effect

最近我开始研究使用 Scala 作为参考语言的函数式编程范式。 我想出了这个问题:如何生成一个没有副作用的随机数? 谷歌搜索我找到了这个解决方案:

package fp.crazy-bankers.utils

    object Rng {
        def next(seed : Int) : (Int,Int) = {
            val newSeed = (seed * 0x5DEECE66DL + 0xBL) & 0x FFFFFFFFFFFFL
            val number = (newSeed >>> 16).toInt
            (newSeed, number)
        }
    }

基本上在这里状态(种子)被管理在每次调用 next 方法时显式传递它。 这个实现对我有用,直到我从同一个地方调用它,因为这样很容易保持状态并在每次调用时传递它。

但是如果我在不同的地方需要一个随机数怎么办? 例如,如果我从 10 个不同的参与者调用 next 方法,在这种情况下,每个参与者只能传递其本地状态副本。

基本上以这种方式,所有参与者都没有关于状态的全局知识,因此风险是不同的参与者获得相同的随机数。 如何解决这个问题?

我是否需要找到某种方法来全局管理状态或尝试使用完全不同的模式?

您可以引入一个单独的参与者,该参与者将使用下一个随机数进行回复。

例如,actor 会发送GetNextRandomNumber给这个 actor,它会回复NextRandomNumber(number)

该参与者将自行管理其状态( seed )。

实际上向这个actor发送消息会产生副作用。

通常生成随机数是一种副作用,因为没有副作用的函数不能在同一输入上产生不同的输出。

函数式编程的思想不是避免副作用,而是控制它们。 例如使用IOState等。

您可以使用Ref构造在不同位置之间共享可变状态。 它在 Scala 中有几种实现:

ZIO 参考: https ://zio.dev/docs/datatypes/datatypes_ref 猫效应参考: https : //typelevel.org/cats-effect/concurrency/ref.html

但是,这要求您将 Ref 显式传递到将要使用它的每个地方。 这是一个特性,而不是一个错误,因为它可以更容易地识别使用某些可变状态的所有位置——没有办法拥有全局可变状态。

但既然你提到了演员,我怀疑你是否真的在做纯 FP。 至少对于 Akka 来说,基本上没有办法做到这一点,因为即使在 Actor 之间发送消息也是一种副作用。

如果您想“使用纯函数式设计风格”,那么您必须将种子传递给每个需要“随机”数字的函数。 没有其他选择。 所有其他选项都需要副作用或失去参考透明度。

因此,如果您在多个函数中使用随机数,则必须为每个函数提供不同的种子。

种子只是表示无限随机数序列的一种紧凑方式,因此另一种方法是在函数外部生成随机数并将值传递给函数而不是种子。

您使用支持拆分的不同类型的 PRNG。 然后根据需要多次拆分主actor上的生成器,并将一个子生成器发送给其他每个actor。

例如, JAX使用名为Threefry的生成器来完成此 操作

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM