【Scala原理系列】scala Breeze Rand RandBasis 原理方法示例源码分析

本文详细介绍了Scala的Breeze库中Rand和RandBasis类的功能,包括随机数生成的接口方法、RandBasis类的实例方法如systemSeed、mt0和withSeed,以及如何通过这些类创建不同类型的随机数生成器并进行随机采样和变换。
摘要由CSDN通过智能技术生成

scala Breeze Rand RandBasis 原理方法示例源码分析

原理

Breeze是一个基于Scala的数值计算库,其中的RandRandBasis类提供了随机数生成的功能。

  • Rand类:Rand是一个特质(trait),用于表示从分布中获取样本的随机采样器。它定义了draw()方法,用于获取单个样本。具体的随机数生成逻辑由实现Rand特质的具体类来实现,例如MappedRandFlatMappedRand等。通过使用flatMapmapfilter等方法,可以对随机样本进行变换和过滤操作。

  • RandBasis类:RandBasis是一个类,用于创建随机数生成器的基础实例。它接受一个RandomGenerator对象作为参数,并提供了一系列方法来创建不同种类的随机数生成器。例如,systemSeed方法创建一个基于MersenneTwister的随机基础实例,其种子设置为系统时间和其他元数据。mt0方法创建一个基于MersenneTwister的随机基础实例,其种子设置为0。withSeed(seed: Int)方法创建一个基于MersenneTwister的随机基础实例,其种子设置为指定的值。

在Breeze中,随机数生成器的选择和种子设置非常灵活。可以根据需求选择合适的随机数生成器类型和种子设置方式。Rand类通过与RandBasis对象结合使用,提供了生成随机数样本、进行变换和过滤等操作的功能。

Rand接口

方法

该代码段中的方法主要用于随机采样和处理随机数样本。以下是这些方法的总结:

  • draw(): 从分布中获取一个样本。
  • get(): 等效于draw(),从分布中获取一个样本。
  • drawOpt(): 从分布中获取一个样本,并返回一个Option对象。如果无法获取样本,则返回None
  • sample(): 等效于get(),从分布中获取一个样本。
  • sample(n: Int): 从分布中获取n个样本,并以IndexedSeq形式返回。
  • samples: 返回一个无限长的迭代器,不断从分布中采样。
  • samplesVector[U >: T](size: Int)(implicit m: ClassTag[U]): 生成指定大小的样本向量,使用给定的ClassTag来确定向量类型。
  • flatMap[E](f: T => Rand[E]): 将随机采样器的样本转换为另一种类型的随机采样器。
  • map[E](f: T => E): 对随机采样器的样本进行映射转换。
  • foreach(f: T => Unit): 对随机采样器的样本应用给定的函数。
  • filter(p: T => Boolean): 根据给定的谓词函数对随机采样器的样本进行过滤。
  • withFilter(p: T => Boolean): 根据给定的谓词函数对随机采样器的样本进行过滤。
  • condition(p: T => Boolean): 根据给定的谓词函数对随机采样器的样本进行过滤。

这些方法提供了一系列灵活和方便的操作,用于生成、转换和处理随机数样本。可以根据具体需求选择适当的方法来实现所需的功能。

源码

package breeze.stats.distributions
 

import java.util.concurrent.atomic.AtomicInteger
import breeze.linalg.DenseVector
import org.apache.commons.math3.random.{MersenneTwister, RandomGenerator}
import breeze.macros.cforRange

import scala.collection.compat.immutable.ArraySeq
import scala.collection.mutable.ArrayBuffer
import scala.reflect.ClassTag

/**
 * 用于单子分布的特征。提供在 for-comprehensions 中使用的支持
 * @author dlwh
 */
trait Rand[@specialized(Int, Double) +T] extends Serializable { outer =>

  /**
   * 从分布中获取一个样本。等效于 sample()
   */
  def draw(): T

  def get() = draw()

  /** 由 filter/map/flatmap 覆盖,用于单子调用。基本上,rejeciton sampler 将返回 None */
  def drawOpt(): Option[T] = Some(draw())

  /**
   * 从分布中获取一个样本。等效于 get()
   */
  def sample() = get()

  /**
   * 从分布中获取 n 个样本。
   */
  def sample(n: Int): IndexedSeq[T] = IndexedSeq.fill(n)(draw())

  /**
   * 一个无限长的迭代器,不断从 Rand 中采样
   * @return 重复采样的迭代器
   */
  def samples: Iterator[T] = Iterator.continually(draw())

  /**
   * 返回样本的向量。
   */
  def samplesVector[U >: T](size: Int)(implicit m: ClassTag[U]): DenseVector[U] = {
    val result = new DenseVector[U](new Array[U](size))
    cforRange(0 until size)(i => {
      result(i) = draw()
    })
    result
  }

  /**
   * 将一种类型的随机采样器转换为另一种类型的随机采样器。
   * 示例:
   * randInt(10).flatMap(x => randInt(3 * x.asInstanceOf[Int]) 给出一个在 [0,30] 范围内的 Rand[Int]
   * 等同于 for(x <- randInt(10); y <- randInt(30 *x)) yield y
   *
   * @param f 应用于采样值的变换函数。
   *
   */
  def flatMap[E](f: T => Rand[E]): Rand[E] = FlatMappedRand(outer, f)

  /**
   * 将一种类型的随机采样器转换为另一种类型的随机采样器。
   * 示例:
   * uniform.map(_*2) 给出一个在 [0,2] 范围内的 Rand[Double]
   * 等同于 for(x <- uniform) yield 2*x
   *
   * @param f 应用于采样值的变换函数。
   *
   */
  def map[E](f: T => E): Rand[E] = MappedRand(outer, f)

  /**
   * 采样一个元素并将提供的函数应用于它。
   * 尽管名字是 foreach,但该函数只会被应用一次。使用示例:
   * <pre> for(x &lt;- Rand.uniform) { println(x) } </pre>
   *
   * @param f 要应用的函数
   */
  def foreach(f: T => Unit) = f(get())

  def filter(p: T => Boolean) = condition(p)

  def withFilter(p: T => Boolean) = condition(p)

  // 并不是最高效的实现,但可以接受。
  def condition(p: T => Boolean): Rand[T] = SinglePredicateRand[T](outer, p)
}

private final case class MappedRand[@specialized(Int, Double) T, @specialized(Int, Double) U](
    rand: Rand[T],
    func: T => U)
    extends Rand[U] {
  def draw() = func(rand.draw())
  override def drawOpt() = rand.drawOpt().map(func)
  override def map[E](f: U => E): Rand[E] = MappedRand(rand, (x: T) => f(func(x)))
}

private final case class FlatMappedRand[@specialized(Int, Double) T, @specialized(Int, Double) U](
    rand: Rand[T],
    func: T => Rand[U])
    extends Rand[U] {
  def draw() = func(rand.draw()).draw()
  override def drawOpt() = rand.drawOpt().flatMap(x => func(x).drawOpt())
  override def flatMap[E](f: U => Rand[E]): Rand[E] = FlatMappedRand(rand, (x: T) => f(func(x).draw()))
}

private trait PredicateRandDraws[@specialized(Int, Double) T] extends Rand[T] {
  protected val rand: Rand[T]
  protected def predicate(x: T): Boolean

  def draw() = { // 并不是最高效的实现,但可以接受。
    var x = rand.draw()
    while (!predicate(x)) {
      x = rand.draw()
    }
    x
  }

  override def drawOpt() = {
    val x = rand.get()
    if (predicate(x)) {
      Some(x)
    } else {
      None
    }
  }
}

private final case class SinglePredicateRand[@specialized(Int, Double) T](rand: Rand[T], pred: T => Boolean)
    extends PredicateRandDraws[T] {
  protected final def predicate(x: T): Boolean = pred(x)

  override def condition(p: T => Boolean): Rand[T] = {
    val newPredicates = new Array[T => Boolean](2)
    newPredicates(0) = pred
    newPredicates(1) = p
    MultiplePredicatesRand(rand, newPredicates)
  }
}

private final case class MultiplePredicatesRand[@specialized(Int, Double) T](
    rand: Rand[T],
    private val predicates: Array[T => Boolean])
    extends PredicateRandDraws[T] {
  override def condition(p: T => Boolean): Rand[T] = {
    val newPredicates = new Array[T => Boolean](predicates.size + 1)
    cforRange(0 until predicates.size)(i => {
      newPredicates(i) = predicates(i)
    })
    newPredicates(predicates.size) = p
    MultiplePredicatesRand(rand, newPredicates)
  }

  protected final def predicate(x: T) = {
    var result: Boolean = true
    var i = 0
    while ((i < predicates.size) && result) {
      result = result && predicates(i)(x)
      i = i + 1
    }
    result
  }
}


RandBasis类

方法

RandBasis类提供了一些用于组合新的Rands的标准组合器和其他随机数生成方法。

  1. choose[T](c: Iterable[T]):从给定的集合中选择一个元素作为样本。它使用uniform生成的随机数确定选择的位置。

  2. always[T](t: T):无论如何都返回指定的参数t,即始终生成相同的样本。

  3. fromBody[T](f: => T):每次调用时都重新评估传入的表达式f并生成结果作为样本。

  4. promote[U](col: Seq[Rand[U]]):将Seq[Rand[U]]转换为Rand[Seq[U]],即将多个随机生成器的样本收集到一个序列中。

  5. uniform:生成在区间[0, 1)内均匀分布的双精度随机数样本。

  6. randInt:生成在区间[0, MAX_INT]内均匀分布的整数样本。

  7. randInt(n: Int):生成在区间[0, n)内均匀分布的整数样本。

  8. randInt(n: Int, m: Int):生成在区间[n, m)内均匀分布的整数样本。

  9. randLong:生成在区间[0, MAX_LONG]内均匀分布的长整数样本。

  10. randLong(n: Long):生成在区间[0, n)内均匀分布的长整数样本。

  11. randLong(n: Long, m: Long):生成在区间[n, m)内均匀分布的长整数样本。

  12. gaussian:生成均值为0,标准差为1的高斯分布样本。

  13. gaussian(m: Double, s: Double):生成均值为m,标准差为s的高斯分布样本。

  14. permutation(n: Int):对从0到n的数字进行洗牌,返回一个洗牌后的索引序列作为样本。

  15. subsetsOfSize[T](set: IndexedSeq[T], n: Int):从集合中选择大小为n的子集,并对子集进行洗牌,返回洗牌后的子集作为样本。

这些方法提供了一系列常见的随机生成器和组合操作,方便生成不同分布的随机数样本。

示例

import breeze.stats.distributions.{Rand, RandBasis}

object RandBasisTest extends App{
  // 创建一个基于系统时间和其他元数据的随机基础
  val basis = RandBasis.systemSeed

  // 生成一个在集合中选择的随机数
  val fruits = Seq("apple", "banana", "orange", "grape")
  val chooseFruit = basis.choose(fruits).draw()
  println(chooseFruit) // 输出随机选择的水果

  // 使用固定种子创建一个具有一致性的随机基础
  val fixedBasis = Rand.FixedSeed.randBasis

  // 生成一个始终返回指定值的随机数
  val alwaysFive = fixedBasis.always(5).draw()
  println(alwaysFive) // 输出5

  // 生成一个均匀分布的随机整数
  val randomInt = fixedBasis.randInt.draw()
  println(randomInt) // 输出随机整数

  // 生成一个高斯分布的随机数
  val gaussianNum = basis.gaussian(0, 1).draw()
  println(gaussianNum) // 输出高斯分布的随机数

  // 对从0到9的数字进行洗牌
  val permutationSeq = basis.permutation(10).draw()
  println(permutationSeq) // 输出洗牌后的索引序列

  // 从集合中选择大小为3的子集并进行洗牌
  val numbers = IndexedSeq(1, 2, 3, 4, 5)
  val shuffledSubset = basis.subsetsOfSize(numbers, 3).draw()
  println(shuffledSubset) // 输出洗牌后的子集

}
//banana
//5
//209652396
//-0.25815497475797977
//Vector(5, 9, 4, 8, 6, 0, 3, 7, 1, 2)
//Vector(3, 2, 1)

源码

/**
 * 提供用于组合新的 Rands 的标准组合器等。
 */
class RandBasis(val generator: RandomGenerator) extends Serializable {

  /**
   * 从集合中选择一个元素。
   */
  def choose[T](c: Iterable[T]): Rand[T] = new Rand[T] {
    def draw() = {
      val sz = uniform.draw() * c.size
      val elems = c.iterator
      var i = 1
      var e = elems.next()
      while (i < sz) {
        e = elems.next()
        i += 1
      }
      e
    }
  }

  def choose[T](c: Seq[T]) = randInt(c.size).map(c(_))

  /**
   * 无关紧要的随机生成器:始终返回参数
   */
  def always[T](t: T): Rand[T] = new Rand[T] {
    def draw() = t
  }

  /**
   * 每次调用 get 时都重新评估主体
   */
  def fromBody[T](f: => T): Rand[T] = new Rand[T] {
    def draw() = f
  }

  /**
   * 将 Rand[T] 的 Seq 转换为 Rand[Seq[T]]
   */
  def promote[U](col: Seq[Rand[U]]) = fromBody(col.map(_.draw()))

  def promote[T1, T2](t: (Rand[T1], Rand[T2])) = fromBody((t._1.draw(), t._2.draw()))
  def promote[T1, T2, T3](t: (Rand[T1], Rand[T2], Rand[T3])) = fromBody((t._1.draw(), t._2.draw(), t._3.draw()))
  def promote[T1, T2, T3, T4](t: (Rand[T1], Rand[T2], Rand[T3], Rand[T4])) =
    fromBody((t._1.draw(), t._2.draw(), t._3.draw(), t._4.draw()))

  /**
   * 均匀采样在 [0,1) 中
   */
  val uniform: Rand[Double] = new Rand[Double] {
    def draw() = generator.nextDouble
  }

  /**
   * 均匀采样一个整数在 [0,MAX_INT] 中
   */
  val randInt: Rand[Int] = new Rand[Int] {
    def draw() = generator.nextInt & Int.MaxValue
  }

  /**
   * 均匀采样一个整数在 [0,n) 中
   */
  def randInt(n: Int): Rand[Int] = new Rand[Int] {
    def draw() = generator.nextInt(n)
  }

  /**
   * 均匀采样一个整数在 [n,m) 中
   */
  def randInt(n: Int, m: Int): Rand[Int] = new Rand[Int] {
    def draw() = generator.nextInt(m - n) + n
  }

  /**
   * 均匀采样一个长整数在 [0,MAX_LONG] 中
   */
  val randLong: Rand[Long] = new Rand[Long] {
    def draw() = generator.nextLong & Long.MaxValue
  }

  /**
   * 均匀采样一个长整数在 [0,n) 中
   */
  def randLong(n: Long): Rand[Long] = new Rand[Long] {
    require(n > 0)
    def draw(): Long = {
      val maxVal = Long.MaxValue - (Long.MaxValue % n) - 1
      var value = (generator.nextLong() & Long.MaxValue)
      while ( value > maxVal ) {
        value = (generator.nextLong() & Long.MaxValue)
      }
      value % n
    }
  }

  /**
   * 均匀采样一个长整数在 [n,m) 中
   */
  def randLong(n: Long, m: Long): Rand[Long] = new Rand[Long] {
    val inner = randLong(m - n)
    def draw() = {
      inner.draw() + n
    }
  }

  /**
   * 采样一个均值为 0,标准差为 1 的高斯分布
   */
  val gaussian: Rand[Double] = new Rand[Double] {
    def draw() = generator.nextGaussian()
  }

  /**
   * 采样一个均值为 m,标准差为 s 的高斯分布
   */
  def gaussian(m: Double, s: Double): Rand[Double] = new Rand[Double] {
    def draw() = m + s * gaussian.draw()
  }

  /**
   * 实现 Knuth shuffle 算法,用于对从 0 到 n 的数字进行洗牌。
   */
  def permutation(n: Int): Rand[IndexedSeq[Int]] = new Rand[IndexedSeq[Int]] {
    def draw() = {
      val arr = new ArrayBuffer[Int]()
      arr ++= (0 until n)
      var i = n
      while (i > 1) {
        val k = generator.nextInt(i)
        i -= 1
        val tmp = arr(i)
        arr(i) = arr(k)
        arr(k) = tmp
      }
      arr.toIndexedSeq
    }
  }

  /**
   * 对集合中大小为 n 的子集实现 Knuth shuffle
   */
  def subsetsOfSize[T](set: IndexedSeq[T], n: Int): Rand[IndexedSeq[T]] = new Rand[IndexedSeq[T]] {
    def draw() = {
      val arr = Array.range(0, set.size)
      var i = 0
      while (i < n.min(set.size)) {
        val k = generator.nextInt(set.size - i) + i
        val temp = arr(i)
        arr(i) = arr(k)
        arr(k) = temp
        i += 1
      }
      arr.take(n).toIndexedSeq.map(set)
    }
  }
}

RandBasis

上述代码定义了Rand对象和RandBasis对象,提供了一些随机生成器的功能。

Rand对象

Rand对象继承自RandBasis,使用ThreadLocalRandomGeneratorMersenneTwister作为默认的随机数生成器。它提供了两个内部对象VariableSeedFixedSeed,用于在使用Rands/Distributions时选择不同的种子设置方式。

VariableSeed

  • VariableSeed:导入此对象以使用“默认”生成器创建使用的Rands/Distributions。它使用Rand对象作为默认的RandBasis实例,即采用系统时间和其他元数据来设置种子。

FixedSeed

  • FixedSeed:导入此对象以使用具有一致种子的生成器。它使用RandBasis.mt0作为默认的RandBasis实例,即使用种子值为0的生成器。

RandBasis对象

方法

RandBasis对象定义了几个方法:

systemSeed:

返回一个新的基于MersenneTwister的随机基础,其种子设置为“无种子”,即使用时间加上其他元数据来设置种子。如果多个线程使用此方法,则每个线程都会获得一个新的生成器,也会用“无种子”进行初始化。

mt0

返回一个新的基于MersenneTwister的随机基础,其种子设置为0。请注意,如果多个线程使用此方法,则每个线程都会获得一个新的生成器,种子是递增的随机数(从种子开始)。

withSeed(seed: Int)

返回一个新的基于MersenneTwister的随机基础,其种子设置为特定值。如果多个线程使用此方法,则每个线程都会获得一个新的生成器,种子是递增的随机数(从种子开始)。

通过使用RandBasis对象和Rand对象,您可以根据需求选择不同的随机数生成器和种子设置方式,并使用提供的方法进行随机数生成和处理。

源码

/**
 * 提供一些随机生成器,使用随机种子设置为系统时间和某个对象的身份哈希码的函数
 */
object Rand extends RandBasis(new ThreadLocalRandomGenerator(new MersenneTwister())) {
  /** 导入此内容以使用“默认”生成器创建使用的 Rands/Distributions */
  object VariableSeed {
    implicit val randBasis: RandBasis = Rand
  }

  /** 导入此内容以使用具有一致种子的生成器。*/
  object FixedSeed {
    implicit val randBasis: RandBasis = RandBasis.mt0
  }
}


object RandBasis {
  /**
   * 返回一个新的基于 MersenneTwister 的随机基础,其种子设置为“无种子”(即,它使用时间加上其他元数据来设置种子)
   * 如果多个线程使用此方法,则每个线程都会获得一个新的生成器,也会用“无种子”进行初始化
   * @return
   */
  def systemSeed: RandBasis = new RandBasis(new ThreadLocalRandomGenerator(new MersenneTwister()))

  /**
   * 返回一个新的基于 MersenneTwister 的随机基础,其种子设置为 0。请注意,
   * 如果多个线程使用此方法,则每个线程都会获得一个新的生成器,种子是递增的随机数(从种子开始)
   * @return
   */
  def mt0: RandBasis = withSeed(0)

  /**
   * 返回一个新的基于 MersenneTwister 的随机基础,其种子设置为特定值。
   * 如果多个线程使用此方法,则每个线程都会获得一个新的生成器,种子是递增的随机数(从种子开始)
   */
  def withSeed(seed: Int): RandBasis = {
    val int = new AtomicInteger(seed)
    new RandBasis(new ThreadLocalRandomGenerator(new MersenneTwister(int.getAndIncrement())))
  }
}
  • 36
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值