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

文章目录

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

RandBasis类方法示例源码

RandBasisRand对象VariableSeedFixedSeed

RandBasis对象方法systemSeed:mt0withSeed(seed: Int)

源码

原理

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

Rand类:Rand是一个特质(trait),用于表示从分布中获取样本的随机采样器。它定义了draw()方法,用于获取单个样本。具体的随机数生成逻辑由实现Rand特质的具体类来实现,例如MappedRand、FlatMappedRand等。通过使用flatMap、map和filter等方法,可以对随机样本进行变换和过滤操作。 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,但该函数只会被应用一次。使用示例:

*

 for(x <- Rand.uniform) { println(x) } 

*

* @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的标准组合器和其他随机数生成方法。

choose[T](c: Iterable[T]):从给定的集合中选择一个元素作为样本。它使用uniform生成的随机数确定选择的位置。 always[T](t: T):无论如何都返回指定的参数t,即始终生成相同的样本。 fromBody[T](f: => T):每次调用时都重新评估传入的表达式f并生成结果作为样本。 promote[U](col: Seq[Rand[U]]):将Seq[Rand[U]]转换为Rand[Seq[U]],即将多个随机生成器的样本收集到一个序列中。 uniform:生成在区间[0, 1)内均匀分布的双精度随机数样本。 randInt:生成在区间[0, MAX_INT]内均匀分布的整数样本。 randInt(n: Int):生成在区间[0, n)内均匀分布的整数样本。 randInt(n: Int, m: Int):生成在区间[n, m)内均匀分布的整数样本。 randLong:生成在区间[0, MAX_LONG]内均匀分布的长整数样本。 randLong(n: Long):生成在区间[0, n)内均匀分布的长整数样本。 randLong(n: Long, m: Long):生成在区间[n, m)内均匀分布的长整数样本。 gaussian:生成均值为0,标准差为1的高斯分布样本。 gaussian(m: Double, s: Double):生成均值为m,标准差为s的高斯分布样本。 permutation(n: Int):对从0到n的数字进行洗牌,返回一个洗牌后的索引序列作为样本。 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,使用ThreadLocalRandomGenerator和MersenneTwister作为默认的随机数生成器。它提供了两个内部对象VariableSeed和FixedSeed,用于在使用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())))

}

}

推荐阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: