产生无副作用的随机数

问题描述

最近,我开始研究使用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方法, 在这种情况下,每个参与者只能传递其状态的本地副本。

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

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

解决方法

您可以引入一个单独的演员,该演员将用下一个随机数答复。

例如,演员会将GetNextRandomNumber发送给该演员,并会回复NextRandomNumber(number)

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

实际上向该演员发送消息是一种副作用。

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

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

,

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

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

但是,这需要您将Ref显式传递到将要使用它的每个位置。这是一个功能,而不是错误,因为它使识别使用某种可变状态的所有位置变得更加容易-无法拥有全局可变状态。

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

,

如果要“使用纯功能设计风格”,则必须将种子传递给每个需要“随机”数字的功能。别无选择。所有其他选项都需要副作用,否则将失去参照透明性。

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

种子只是表示无限数量随机数的一种紧凑方式,因此一种替代方法是在函数外部生成随机数,然后将值传递给函数而不是种子。

,

您使用支持拆分的另一种PRNG类型。然后,您可以根据需要在主要角色上拆分生成器多次,然后将一个子生成器发送给其他每个角色。

例如,使用名为JAX does this的生成器threefry

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...