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
对象结合使用,提供了生成随机数样本、进行变换和过滤等操作的功能。
该代码段中的方法主要用于随机采样和处理随机数样本。以下是这些方法的总结:
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 <- 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
类提供了一些用于组合新的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)
}
}
}
上述代码定义了Rand
对象和RandBasis
对象,提供了一些随机生成器的功能。
Rand
对象继承自RandBasis
,使用ThreadLocalRandomGenerator
和MersenneTwister
作为默认的随机数生成器。它提供了两个内部对象VariableSeed
和FixedSeed
,用于在使用Rands/Distributions
时选择不同的种子设置方式。
VariableSeed
:导入此对象以使用“默认”生成器创建使用的Rands/Distributions
。它使用Rand
对象作为默认的RandBasis
实例,即采用系统时间和其他元数据来设置种子。FixedSeed
:导入此对象以使用具有一致种子的生成器。它使用RandBasis.mt0
作为默认的RandBasis
实例,即使用种子值为0的生成器。RandBasis
对象定义了几个方法:
返回一个新的基于MersenneTwister
的随机基础,其种子设置为“无种子”,即使用时间加上其他元数据来设置种子。如果多个线程使用此方法,则每个线程都会获得一个新的生成器,也会用“无种子”进行初始化。
返回一个新的基于MersenneTwister
的随机基础,其种子设置为0。请注意,如果多个线程使用此方法,则每个线程都会获得一个新的生成器,种子是递增的随机数(从种子开始)。
返回一个新的基于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())))
}
}