Spec-Zone.ru › Julia 1.3

Генерация случайных чисел

Генерация случайных чисел в Julia использует библиотеку Mersenne Twister через объекты MersenneTwister. Julia имеет глобальный генератор случайных чисел (RNG), который используется по умолчанию. Другие типы RNG могут быть подключены, наследуя тип AbstractRNG; они затем могут использоваться для создания нескольких потоков случайных чисел. Помимо MersenneTwister, Julia также предоставляет тип RNG RandomDevice, который является оболочкой над предоставляемой ОС энтропией.

Большинство функций, связанных с генерацией случайных чисел, принимают необязательный объект AbstractRNG в качестве первого аргумента, который по умолчанию равен глобальному, если не указан. Кроме того, некоторые из них необязательно принимают спецификации размерности dims... (которые могут быть заданы как кортеж) для генерации массивов случайных значений.

Генератор случайных чисел типа MersenneTwister или RandomDevice может генерировать равномерно распределенные случайные числа следующих типов: Float16, Float32, Float64, BigFloat, Bool, Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128, BigInt (или комплексные числа этих типов). Случайные числа с плавающей запятой генерируются равномерно в интервале $[0, 1)$. Поскольку BigInt представляет неограниченные целые числа, интервал должен быть указан (например, rand(big.(1:6))).

Кроме того, нормальное и экспоненциальное распределения реализованы для некоторых типов AbstractFloat и Complex, см. randn и randexp для подробностей.

Модуль случайных чисел

Random.RandomМодуль

Random

Поддержка генерации случайных чисел. Предоставляет rand, randn, AbstractRNG, MersenneTwister и RandomDevice.

исходный код

Функции генерации случайных чисел

Base.randФункция

rand([rng=GLOBAL_RNG], [S], [dims...])

Выбирает случайный элемент или массив случайных элементов из набора значений, заданных S; S может быть

  • индексируемым набором (например, 1:9 или ('x', "y", :z)),
  • объектом AbstractDict или AbstractSet,
  • строкой (рассматриваемой как набор символов), или
  • типом: набор значений для выбора эквивалентен typemin(S):typemax(S) для целых чисел (это не применимо к BigInt), $[0, 1)$ для чисел с плавающей запятой и $[0, 1)+i[0, 1)]$ для комплексных чисел с плавающей запятой;

S по умолчанию равен Float64.

Julia 1.1

Поддержка S в качестве кортежа требует как минимум Julia 1.1.

Примеры

julia> rand(Int, 2)
2-element Array{Int64,1}:
 1339893410598768192
 1575814717733606317

julia> using Random

julia> rand(MersenneTwister(0), Dict(1=>2, 3=>4))
1=>2
Примечание

Сложность rand(rng, s::Union{AbstractDict,AbstractSet}) линейна по отношению к длине s, если не доступен оптимизированный метод с постоянной сложностью, что имеет место для Dict, Set и BitSet. Для более чем нескольких вызовов используйте rand(rng, collect(s)) вместо этого, либо rand(rng, Dict(s)) или rand(rng, Set(s)) соответственно.

исходный код

Random.rand!Функция

rand!([rng=GLOBAL_RNG], A, [S=eltype(A)])

Заполняет массив A случайными значениями. Если S указан (S может быть типом или набором, см. rand для подробностей), значения выбираются случайным образом из S. Это эквивалентно copyto!(A, rand(rng, S, size(A))), но без выделения нового массива.

Примеры

julia> rng = MersenneTwister(1234);

julia> rand!(rng, zeros(5))
5-element Array{Float64,1}:
 0.5908446386657102
 0.7667970365022592
 0.5662374165061859
 0.4600853424625171
 0.7940257103317943
исходный код

Random.bitrandФункция

bitrand([rng=GLOBAL_RNG], [dims...])

Генерирует массив BitArray случайных логических значений.

Примеры

julia> rng = MersenneTwister(1234);

julia> bitrand(rng, 10)
10-element BitArray{1}:
 0
 1
 1
 1
 1
 0
 1
 0
 0
 1
исходный код

Base.randnФункция

randn([rng=GLOBAL_RNG], [T=Float64], [dims...])

Генерирует случайное число типа T с нормальным распределением (среднее значение 0, стандартное отклонение 1). Необязательно генерирует массив случайных чисел с нормальным распределением. Модуль Base в настоящее время предоставляет реализацию для типов Float16, Float32 и Float64 (по умолчанию), а также их комплексных аналогов. Когда аргумент типа является комплексным, значения выбираются из кругового симметричного комплексного нормального распределения с дисперсией 1 (что соответствует тому, что действительная и мнимая части имеют независимое нормальное распределение со средним значением 0 и дисперсией 1/2).

Примеры

julia> using Random

julia> rng = MersenneTwister(1234);

julia> randn(rng, ComplexF64)
0.6133070881429037 - 0.6376291670853887im

julia> randn(rng, ComplexF32, (2, 3))
2×3 Array{Complex{Float32},2}:
 -0.349649-0.638457im  0.376756-0.192146im  -0.396334-0.0136413im
  0.611224+1.56403im   0.355204-0.365563im  0.0905552+1.31012im
исходный код

Random.randn!Функция

randn!([rng=GLOBAL_RNG], A::AbstractArray) -> A

Заполняет массив A случайными числами с нормальным распределением (среднее значение 0, стандартное отклонение 1). Также см. функцию rand.

Примеры

julia> rng = MersenneTwister(1234);

julia> randn!(rng, zeros(5))
5-element Array{Float64,1}:
  0.8673472019512456
 -0.9017438158568171
 -0.4944787535042339
 -0.9029142938652416
  0.8644013132535154
исходный код

Random.randexpФункция

randexp([rng=GLOBAL_RNG], [T=Float64], [dims...])

Генерирует случайное число типа T согласно экспоненциальному распределению со масштабом 1. Необязательно генерирует массив таких случайных чисел. Модуль Base в настоящее время предоставляет реализацию для типов Float16, Float32 и Float64 (по умолчанию).

Примеры

julia> rng = MersenneTwister(1234);

julia> randexp(rng, Float32)
2.4835055f0

julia> randexp(rng, 3, 3)
3×3 Array{Float64,2}:
 1.5167    1.30652   0.344435
 0.604436  2.78029   0.418516
 0.695867  0.693292  0.643644
исходный код

Random.randexp!Функция

randexp!([rng=GLOBAL_RNG], A::AbstractArray) -> A

Заполняет массив A случайными числами, следующими экспоненциальному распределению (с масштабом 1).

Примеры

julia> rng = MersenneTwister(1234);

julia> randexp!(rng, zeros(5))
5-element Array{Float64,1}:
 2.4835053723904896
 1.516703605376473
 0.6044364871025417
 0.6958665886385867
 1.3065196315496677
исходный код

Random.randstringФункция

randstring([rng=GLOBAL_RNG], [chars], [len=8])

Создаёт случайную строку длины len, состоящую из символов из набора chars, который по умолчанию включает заглавные и строчные буквы, а также цифры от 0 до 9. Необязательный аргумент rng задаёт генератор случайных чисел, см. Случайные числа.

Примеры

julia> Random.seed!(0); randstring()
"0IPrGg0J"

julia> randstring(MersenneTwister(0), 'a':'z', 6)
"aszvqk"

julia> randstring("ACGT")
"TATCGGTC"
Примечание

chars может быть любым набором символов, типа Char или UInt8 (более эффективно), при условии, что rand может случайным образом выбирать символы из него.

исходный код

Подпоследовательности, перестановки и перемешивания

Random.randsubseqФункция

randsubseq([rng=GLOBAL_RNG,] A, p) -> Vector

Возвращает вектор, содержащий случайную подпоследовательность данного массива A, где каждый элемент A включается (в порядке) с независимой вероятностью p. (Сложность линейна по отношению к p*length(A), поэтому эта функция эффективна даже если p небольшой, а A большой.) В техническом плане этот процесс известен как «выборка Бернулли» из A.

Примеры

julia> rng = MersenneTwister(1234);

julia> randsubseq(rng, collect(1:8), 0.3)
2-element Array{Int64,1}:
 7
 8
исходный код END_OF_DOCUMENT_MARKER

Random.randsubseq!Функция

randsubseq!([rng=GLOBAL_RNG,] S, A, p)

Аналогично randsubseq, но результаты хранятся в S (размер которого увеличивается по мере необходимости).

Примеры

julia> rng = MersenneTwister(1234);

julia> S = Int64[];

julia> randsubseq!(rng, S, collect(1:8), 0.3);

julia> S
2-element Array{Int64,1}:
 7
 8
исходный код

Random.randpermФункция

randperm([rng=GLOBAL_RNG,] n::Integer)

Создаёт случайную перестановку длиной n. Необязательный аргумент rng задаёт генератор случайных чисел (см. Случайные числа). Тип элементов результата совпадает с типом n.

Для случайной перестановки произвольного вектора см. shuffle или shuffle!.

Julia 1.1

В Julia 1.1 randperm возвращает вектор v с eltype(v) == typeof(n), в то время как в Julia 1.0 eltype(v) == Int.

Примеры

julia> randperm(MersenneTwister(1234), 4)
4-element Array{Int64,1}:
 2
 1
 4
 3
исходный код

Random.randperm!Функция

randperm!([rng=GLOBAL_RNG,] A::Array{<:Integer})

Создаёт в A случайную перестановку длиной length(A). Необязательный аргумент rng задаёт генератор случайных чисел (см. Случайные числа). Для случайной перестановки произвольного вектора см. shuffle или shuffle!.

Примеры

julia> randperm!(MersenneTwister(1234), Vector{Int}(undef, 4))
4-element Array{Int64,1}:
 2
 1
 4
 3
исходный код

Random.randcycleФункция

randcycle([rng=GLOBAL_RNG,] n::Integer)

Создаёт случайную циклическую перестановку длиной n. Необязательный аргумент rng задаёт генератор случайных чисел (см. Случайные числа). Тип элементов результата совпадает с типом n.

Julia 1.1

В Julia 1.1 randcycle возвращает вектор v с eltype(v) == typeof(n), в то время как в Julia 1.0 eltype(v) == Int.

Примеры

julia> randcycle(MersenneTwister(1234), 6)
6-element Array{Int64,1}:
 3
 5
 4
 6
 1
 2
исходный код

Random.randcycle!Функция

randcycle!([rng=GLOBAL_RNG,] A::Array{<:Integer})

Создаёт в A случайную циклическую перестановку длиной length(A). Необязательный аргумент rng задаёт генератор случайных чисел (см. Случайные числа).

Примеры

julia> randcycle!(MersenneTwister(1234), Vector{Int}(undef, 6))
6-element Array{Int64,1}:
 3
 5
 4
 6
 1
 2
исходный код

Random.shuffleФункция

shuffle([rng=GLOBAL_RNG,] v::AbstractArray)

Возвращает случайную перестановку копии v. Необязательный аргумент rng задаёт генератор случайных чисел (см. Случайные числа). Для перестановки v на месте см. shuffle!. Для получения случайно переставленных индексов см. randperm.

Примеры

julia> rng = MersenneTwister(1234);

julia> shuffle(rng, Vector(1:10))
10-element Array{Int64,1}:
  6
  1
 10
  2
  3
  9
  5
  7
  4
  8
исходный код

Random.shuffle!Функция

shuffle!([rng=GLOBAL_RNG,] v::AbstractArray)

Функция shuffle в режиме «на месте»: случайная перестановка v на месте, с необязательным генератором случайных чисел rng.

Примеры

julia> rng = MersenneTwister(1234);

julia> shuffle!(rng, Vector(1:16))
16-element Array{Int64,1}:
  2
 15
  5
 14
  1
  9
 10
  6
 11
  3
 16
  7
  4
 12
  8
 13
исходный код

Генераторы (создание и инициализация)

Random.seed!Функция

seed!([rng=GLOBAL_RNG], seed) -> rng
seed!([rng=GLOBAL_RNG]) -> rng

Переинициализирует генератор случайных чисел: rng даст воспроизводимую последовательность чисел только в том случае, если предоставлен seed. Некоторые генераторы случайных чисел не принимают seed, например, RandomDevice. После вызова seed!, rng эквивалентен новому объекту, инициализированному тем же seed.

Если rng не указан, используется seed по умолчанию для генератора потока.

Примеры

julia> Random.seed!(1234);

julia> x1 = rand(2)
2-element Array{Float64,1}:
 0.590845
 0.766797

julia> Random.seed!(1234);

julia> x2 = rand(2)
2-element Array{Float64,1}:
 0.590845
 0.766797

julia> x1 == x2
true

julia> rng = MersenneTwister(1234); rand(rng, 2) == x1
true

julia> MersenneTwister(1) == Random.seed!(rng, 1)
true

julia> rand(Random.seed!(rng), Bool) # not reproducible
true

julia> rand(Random.seed!(rng), Bool)
false

julia> rand(MersenneTwister(), Bool) # not reproducible either
true
исходный код

Random.AbstractRNGТип

AbstractRNG

Базовый тип для генераторов случайных чисел, таких как MersenneTwister и RandomDevice.

исходный код

Random.MersenneTwisterТип

MersenneTwister(seed)
MersenneTwister()

Создаёт объект генератора случайных чисел MersenneTwister . Разные объекты генераторов могут иметь свои собственные seed, что может быть полезно для генерации разных потоков случайных чисел. seed может быть целым неотрицательным числом или вектором целых чисел UInt32. Если seed не задан, он генерируется случайным образом (с использованием энтропии из системы). Смотрите функцию seed! для переинициализации уже существующего объекта MersenneTwister.

Примеры

julia> rng = MersenneTwister(1234);

julia> x1 = rand(rng, 2)
2-element Array{Float64,1}:
 0.5908446386657102
 0.7667970365022592

julia> rng = MersenneTwister(1234);

julia> x2 = rand(rng, 2)
2-element Array{Float64,1}:
 0.5908446386657102
 0.7667970365022592

julia> x1 == x2
true
исходный код

Random.RandomDeviceТип

RandomDevice()

Создаёт объект генератора случайных чисел RandomDevice . Два таких объекта всегда генерируют разные потоки случайных чисел. Энтропия получается из операционной системы.

исходный код

Интеграция в API Random

Есть два основных ортогональных способа расширения функциональности Random:

  1. генерирование случайных значений пользовательских типов
  2. создание новых генераторов

API для 1) довольно функциональный, но относительно новый, поэтому ему, возможно, потребуется эволюция в последующих версиях модуля Random. Например, обычно достаточно реализовать один метод rand, чтобы все другие стандартные методы работали автоматически.

API для 2) всё ещё незрелый и может потребовать больше усилий, чем строго необходимо от реализующего его разработчика, чтобы поддерживать стандартные типы генерируемых значений.

Генерация случайных значений пользовательских типов

Генерация случайных значений для некоторых распределений может включать различные компромиссы. Предвычисленные значения, такие как таблица соответствия для дискретных распределений или «функции сжатия» для одномерных распределений, могут значительно ускорить выборку. Сколько информации следует предварительно вычислить, может зависеть от количества значений, которые мы планируем получить из распределения. Кроме того, некоторые генераторы случайных чисел могут иметь определённые свойства, которые могут быть полезны различным алгоритмам.

Модуль Random определяет настраиваемый фреймворк для получения случайных значений, который может решить эти проблемы. Каждый вызов rand генерирует самплер, который можно настроить, учитывая вышеупомянутые компромиссы, добавив методы в Sampler, которые, в свою очередь, могут выполнять диспетчеризацию по генератору случайных чисел, объекту, характеризующему распределение, и предложению количества повторений. В настоящее время для последнего используются Val{1} (для одной выборки) и Val{Inf} (для произвольного количества), а Random.Repetition — псевдоним для обоих.

Затем объект, возвращаемый Sampler, используется для генерации случайных значений. При реализации интерфейса случайной генерации для значения X, которое можно выбирать случайным образом, реализующий должен определить метод

rand(rng, sampler)

для конкретного самплера sampler , возвращаемого Sampler(rng, X, repetition).

Самплеры могут быть произвольными значениями, которые реализуют rand(rng, sampler), но для большинства приложений могут быть достаточными следующие предопределённые самплеры:

  1. SamplerType{T}() можно использовать для реализации самплеров, которые выбирают значения из типа T (например, rand(Int)). Это значение по умолчанию, возвращаемое Sampler для типов.

  2. SamplerTrivial(self) — простой оболочкой для self, к которой можно получить доступ с помощью []. Это рекомендуемый самплер, когда предварительно вычисленные данные не нужны (например, rand(1:3) ), и это значение по умолчанию, возвращаемое Sampler для значений.

  3. SamplerSimple(self, data) также содержит дополнительное поле data, которое можно использовать для хранения произвольных предварительно вычисленных значений, которые должны вычисляться в специальном методе Sampler.

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

Random.SamplerТип

Sampler(rng, x, repetition = Val(Inf))

Возвращает объект sampler, который можно использовать для генерации случайных значений из rng для x.

Когда sp = Sampler(rng, x, repetition), rand(rng, sp) будет использоваться для получения случайных значений и должно быть определено соответствующим образом.

repetition может быть Val(1) или Val(Inf), и должно использоваться как рекомендация для определения объёма предварительных вычислений, если применимо.

Random.SamplerType и Random.SamplerTrivial являются стандартными значениями по умолчанию для типов и значений соответственно. Random.SamplerSimple можно использовать для хранения предварительно вычисленных значений без определения дополнительных типов только для этой цели.

source

Random.SamplerTypeТип

SamplerType{T}()

Sampler для типов, не содержащий другой информации. Значение по умолчанию для Sampler при вызове с типами.

source

Random.SamplerTrivialТип

SamplerTrivial(x)

Создаёт sampler, который просто оборачивает заданное значение x. Это значение по умолчанию для значений. eltype этого sampler равно eltype(x).

Рекомендуемое использование — выбор значений без предварительно вычисленных данных.

source

Random.SamplerSimpleТип

SamplerSimple(x, data)

Создаёт sampler, который оборачивает заданное значение x и data. eltype этого sampler равно eltype(x).

Рекомендуемое использование — выбор значений с предварительно вычисленными данными.

source

Разделение предварительных вычислений от фактической генерации значений является частью API и также доступно пользователю. Например, предположим, что rand(rng, 1:20) нужно вызывать многократно в цикле: способ использовать это разделение следующий:

rng = MersenneTwister()
sp = Random.Sampler(rng, 1:20) # or Random.Sampler(MersenneTwister, 1:20)
for x in X
    n = rand(rng, sp) # similar to n = rand(rng, 1:20)
    # use n
end

Этот механизм также используется в стандартной библиотеке, например, в реализации генерации случайных массивов (как в rand(1:20, 10)).

Генерация значений из типа

Для типа T, в настоящее время предполагается, что если rand(T) определено, будет создан объект типа T . SamplerType — это стандартный sampler для типов. Для определения генерации случайных значений типа T, метод rand(rng::AbstractRNG, ::Random.SamplerType{T}) должен быть определён и должен возвращать значения, которые rand(rng, T) ожидается вернуть.

Рассмотрим пример: мы реализуем тип Die, с переменным числом n граней, пронумерованных от 1 до n. Мы хотим, чтобы rand(Die) генерировал Die с случайным числом граней до 20 (и не менее 4):

struct Die
    nsides::Int # number of sides
end

Random.rand(rng::AbstractRNG, ::Random.SamplerType{Die}) = Die(rand(rng, 4:20))

# output

Скалярные и массивообразные методы для Die теперь работают как ожидается:

julia> rand(Die)
Die(18)

julia> rand(MersenneTwister(0), Die)
Die(4)

julia> rand(Die, 3)
3-element Array{Die,1}:
 Die(6)
 Die(11)
 Die(5)

julia> a = Vector{Die}(undef, 3); rand!(a)
3-element Array{Die,1}:
 Die(18)
 Die(6)
 Die(8)

Простой sampler без предварительно вычисленных данных

Здесь мы определяем sampler для коллекции. Если предварительные вычисления не требуются, его можно реализовать с помощью SamplerTrivial sampler, который на самом деле является стандартным значения по умолчанию для значений.

Для определения генерации случайных значений объектов типа S, должен быть определён метод rand(rng::AbstractRNG, sp::Random.SamplerTrivial{S}). Здесь sp просто оборачивает объект типа S, к которому можно обратиться через sp[]. Продолжая пример Die, мы теперь хотим определить rand(d::Die) для генерации Int соответствующей одной из граней d:

julia> Random.rand(rng::AbstractRNG, d::Random.SamplerTrivial{Die}) = rand(rng, 1:d[].nsides);

julia> rand(Die(4))
3

julia> rand(Die(4), 3)
3-element Array{Any,1}:
 3
 4
 2

Для типа коллекции S, в настоящее время предполагается, что если rand(::S) определено, будет создан объект типа eltype(S) . В последнем примере генерируется Vector{Any}, потому что eltype(Die) == Any. Решение — определить Base.eltype(::Type{Die}) = Int.

Генерация значений для типа AbstractFloat

Типы AbstractFloat являются специальными случаями, потому что по умолчанию случайные значения не генерируются в области всего типа, а скорее в [0,1) . Следующий метод должен быть реализован для T <: AbstractFloat: Random.rand(::AbstractRNG, ::Random.SamplerTrivial{Random.CloseOpen01{T}})

Оптимизированный sampler с предварительно вычисленными данными

Рассмотрим дискретное распределение, где числа 1:n выбираются с заданными вероятностями, которые суммируются до единицы. Когда требуется много значений из этого распределения, самым быстрым методом является использование таблицы alias. Мы не приводим здесь алгоритм построения такой таблицы, но предположим, что она доступна в make_alias_table(probabilities), и draw_number(rng, alias_table) можно использовать для получения случайного числа из неё.

Предположим, что распределение описывается

struct DiscreteDistribution{V <: AbstractVector}
    probabilities::V
end

и что мы всегда хотим строить таблицу alias, независимо от количества необходимых значений (мы узнаем, как настроить это ниже). Методы

Random.eltype(::Type{<:DiscreteDistribution}) = Int

function Random.Sampler(::AbstractRng, distribution::DiscreteDistribution, ::Repetition)
    SamplerSimple(disribution, make_alias_table(distribution.probabilities))
end

должны быть определены для возврата sampler с предварительно вычисленными данными, тогда

function rand(rng::AbstractRNG, sp::SamplerSimple{<:DiscreteDistribution})
    draw_number(rng, sp.data)
end

будут использоваться для получения значений.

Пользовательские типы sampler

Тип SamplerSimple подходит для большинства случаев использования с предварительно вычисленными данными. Однако, чтобы продемонстрировать, как использовать пользовательские типы sampler, здесь мы реализуем что-то подобное SamplerSimple.

Вернёмся к нашему примеру Die: rand(::Die) использует случайную генерацию из диапазона, поэтому есть возможность этой оптимизации. Мы называем наш пользовательский sampler SamplerDie.

import Random: Sampler, rand

struct SamplerDie <: Sampler{Int} # generates values of type Int
    die::Die
    sp::Sampler{Int} # this is an abstract type, so this could be improved
end

Sampler(RNG::Type{<:AbstractRNG}, die::Die, r::Random.Repetition) =
    SamplerDie(die, Sampler(RNG, 1:die.nsides, r))
# the `r` parameter will be explained later on

rand(rng::AbstractRNG, sp::SamplerDie) = rand(rng, sp.sp)

Теперь можно получить sampler с sp = Sampler(rng, die), и использовать sp вместо die в любом вызове rand, включающем rng. В упрощенном примере выше, die не нужно хранить в SamplerDie, но это часто бывает на практике.

Конечно, этот паттерн настолько часто используется, что вспомогательный тип, используемый выше, а именно Random.SamplerSimple, доступен, экономя нам определение SamplerDie: мы могли бы реализовать наше разделение с:

Sampler(RNG::Type{<:AbstractRNG}, die::Die, r::Random.Repetition) =
    SamplerSimple(die, Sampler(RNG, 1:die.nsides, r))

rand(rng::AbstractRNG, sp::SamplerSimple{Die}) = rand(rng, sp.data)

Здесь sp.data относится ко второму параметру при вызове конструктора SamplerSimple (в данном случае равен Sampler(rng, 1:die.nsides, r)), в то время как объект Die можно получить через sp[].

Как и SamplerDie, любой пользовательский sampler должен быть подтипом Sampler{T}, где T — тип генерируемых значений. Обратите внимание, что SamplerSimple(x, data) isa Sampler{eltype(x)}, поэтому это ограничивает то, чем может быть первый аргумент SamplerSimple (рекомендуется использовать SamplerSimple как в примере Die, где x просто передаётся при определении метода Sampler). Аналогично, SamplerTrivial(x) isa Sampler{eltype(x)}.

Другой вспомогательный тип в настоящее время доступен для других случаев, Random.SamplerTag, но считается частью внутреннего API и может быть изменен в любой момент без надлежащей отмены.

Использование разных алгоритмов для генерации скалярных или массивообразных значений

В некоторых случаях, то, хочет ли пользователь сгенерировать лишь несколько значений или большое количество, повлияет на выбор алгоритма. Это обрабатывается третьим параметром конструктора Sampler . Предположим, что мы определили два вспомогательных типа для Die, скажем, SamplerDie1, который должен использоваться для генерации небольшого количества случайных значений, и SamplerDieMany для большого количества. Мы можем использовать эти типы следующим образом:

Sampler(RNG::Type{<:AbstractRNG}, die::Die, ::Val{1}) = SamplerDie1(...)
Sampler(RNG::Type{<:AbstractRNG}, die::Die, ::Val{Inf}) = SamplerDieMany(...)

Конечно, rand также должен быть определён для этих типов (т.е. rand(::AbstractRNG, ::SamplerDie1) и rand(::AbstractRNG, ::SamplerDieMany)). Обратите внимание, что, как обычно, SamplerTrivial и SamplerSimple можно использовать, если пользовательские типы не нужны.

Примечание: Sampler(rng, x) — это просто сокращение для Sampler(rng, x, Val(Inf)), а Random.Repetition — псевдоним для Union{Val{1}, Val{Inf}}.

Создание новых генераторов

API ещё не полностью определён, но как правило:

  1. любой метод rand, генерирующий "базовые" типы (isbitstype целочисленные и числовые типы в Base) должен быть определён для этого конкретного генератора случайных чисел, если они нужны;
  2. другие документированные методы rand принимающие AbstractRNG должны работать "из коробки" (при условии реализации методов из 1), на которых они опираются), но, конечно, могут быть специализированы для этого генератора случайных чисел, если есть возможность оптимизации.

Что касается 1), метод rand может работать автоматически, но это не поддерживается официально и может сломаться без предупреждений в последующем релизе.

Для определения нового метода rand для гипотетического генератора MyRNG и спецификации значения s (например, s == Int, или s == 1:10 ) типа S==typeof(s) или S==Type{s} , если s является типом, необходимо определить те же два метода, что и раньше:

  1. Sampler(::Type{MyRNG}, ::S, ::Repetition), который возвращает объект типа, скажем, SamplerS
  2. rand(rng::MyRNG, sp::SamplerS)

Может случиться, что Sampler(rng::AbstractRNG, ::S, ::Repetition) уже определён в модуле Random . В этом случае можно было бы пропустить этап 1) на практике (если нужно специализировать генерацию для этого конкретного типа генератора случайных чисел), но соответствующий тип SamplerS считается внутренней деталью и может быть изменён без предупреждения.

Специализация генерации массивов

В некоторых случаях для заданного типа генератора случайных чисел (RNG) создание массива случайных значений может быть более эффективным с помощью специализированного метода, чем просто использование техники декомпозиции, объяснённой ранее. Это, например, относится к MersenneTwister, который напрямую записывает случайные значения в массив.

Для реализации этой специализации для MyRNG и для спецификации s, производящей элементы типа S, можно определить следующий метод: rand!(rng::MyRNG, a::AbstractArray{S}, ::SamplerS), где SamplerS — тип выборщика, возвращаемого Sampler(MyRNG, s, Val(Inf)). Вместо AbstractArray, можно реализовать функциональность только для подтипа, например, Array{S}. Неизменяющий метод массива rand автоматически вызовет эту специализацию внутри.

© 2009–2020 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/v1.3.1/stdlib/Random/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API