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

发布时间:2024年01月14日

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())))
  }
}
文章来源:https://blog.csdn.net/wang2leee/article/details/135504991
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。