Генераторы псевдослучайных чисел

Пакет spire.random содержит генераторы случайных чисел, подходящие для множества различных задач, а также функциональный интерфейс для создания равномерного распределения значений.

Генераторы псевдослучайных чисел

Трейт mutable.Generator представляет собой стратегию PRNG. Используя равномерно сгенерированные значения Int или Long, он может генерировать случайные значения, массивы значений и т.д. Определить генератор относительно легко.

По умолчанию генераторы не являются потокобезопасными. Синхронизированный генератор можно создать из несинхронизированного с помощью метода sync. Генераторы можно копировать, а их начальные значения сохранять и восстанавливать. Это позволяет пользователям создавать детерминированные потоки значений, используя одно и то же начальное значение. В общем, пользователям предпочтительнее создавать и использовать свои собственные генераторы, а не полагаться на один генератор в нескольких потоках.

Хотя трейт mutable.Generator предоставляет только низкоуровневые методы, такие как nextInt, он может создавать значения любого типа, используя класс типов Dist[A], который будет обсуждаться ниже.

Трейт immutable.Generator похож на mutable.Generator, хотя хранимое в нем состояние является неизменным. Каждый раз, когда генерируется число, также возвращается новый генератор, что позволяет использовать эти генераторы в чисто функциональном контексте. Здесь также применимы те же примеры Dist[A], которые использовались бы с изменяемым генератором.

Создание случайных значений с помощью Dist[A]

Класс типов Dist[A] представляет собой стратегию генерации распределения значений A для данного экземпляра Generator. Dist[A] не дает никаких гарантий относительно того, как распределяются значения A (например, он всегда может возвращать одно и то же значение). Пользователи, интересующиеся конкретными реализациями, должны использовать трейты Uniform[A], Gaussian[A] и Exponential[A] для создания экземпляров Dist[A], соответствующих их потребностям.

Сами объекты Dist[A] неизменяемы и создаются от генераторов (как изменяемых, так и неизменяемых). Их можно преобразовать с помощью map, flatMap и других комбинаторов. При наличии соответствующей структуры A с экземплярами Dist[A] также можно работать, как если бы они были значениями.

Распределения

В настоящее время spire.random предоставляет классы типов Uniform[A], Gaussian[A] и Exponential[A], которые с учетом соответствующих параметров могут создавать экземпляры Dist[A]. Поскольку большинство типов имеют (приблизительно) бесконечное число возможных значений, на эти типы необходимо наложить границы и другие ограничения, прежде чем мы сможем с пользой говорить о (или реализовывать) распределения вероятностей в Spire.

Например:

object DistDemo:
  import spire.math.Complex
  import spire.random.Dist
  import spire.std.double.DoubleAlgebra
  
  val rng = spire.random.rng.Cmwc5()
  rng.nextLong() 
  
  // генерирует ассоциативный массив с от 10 до 20 записей
  given Dist[Map[Int, Complex[Double]]] =
    Dist.map[Int, Complex[Double]](10, 20)
  val m = rng.next[Map[Int, Complex[Double]]]
  
  // генерирует double в интервале [0.0, 1.0)
  val n = rng.next[Double]
  
  // генерирует комплексное число с действительной и мнимой частями в интервале [0.0, 1.0)
  val q = rng.next[Complex[Double]]

Предоставляется множество экземпляров по умолчанию, и для пользовательских типов легко создавать собственные экземпляры.


Ссылки: