Spec-Zone.ru › Julia 1.4

Случайные числа

Генерация случайных чисел в 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. Когда передаётся только один аргумент помимо необязательного rng и он является Tuple, он интерпретируется как набор значений (S) а не как dims.

Поддержка 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

julia> rand((2, 3))
3

julia> rand(Float64, (2, 3))
2×3 Array{Float64,2}:
 0.999717  0.0143835  0.540787
 0.696556  0.783855   0.938235

Сложность 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 (что соответствует тому, что вещественная и мнимая части имеют независимые нормальные распределения со средним значением ноль и дисперсией 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
исходный код

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 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 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 RNG. Разные объекты RNG могут иметь свои собственные seed, что может быть полезно для генерации разных потоков случайных чисел. seed может быть целым неотрицательным числом или вектором целых чисел. Если seed не указан, генерируется случайный 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 RNG. Два таких объекта всегда будут генерировать разные потоки случайных чисел. Энтропия извлекается из операционной системы.

исходный код

Встраивание в API Random

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

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

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

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

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

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

Модуль 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))

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

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

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

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

Исходный код

Random.SamplerTypeТип

SamplerType{T}()

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

Исходный код

Random.SamplerTrivialТип

SamplerTrivial(x)

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

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

Исходный код

Random.SamplerSimpleТип

SamplerSimple(x, data)

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

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

Исходный код

Отделение предварительных вычислений от фактического генерирования значений является частью 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 — это образец по умолчанию для типов. Для определения случайной генерации значений типа 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)

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

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

Для определения случайной генерации объектов типа 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}}).

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

Рассмотрим дискретное распределение, где числа 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

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

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

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

Пользовательские типы образцов

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

Вернёмся к нашему примеру Die: rand(::Die) использует случайную генерацию из диапазона, поэтому есть возможность этой оптимизации. Мы назовём наш пользовательский образец 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)

Теперь возможно получить образец с 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{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), на которых они опираются), но, конечно, могут быть специализированы для этого генератора, если есть возможность оптимизации;
  3. copy для псевдогенераторов должен возвращать независимую копию, генерирующую ровно ту же последовательность случайных чисел, что и оригинал, с того же момента при вызове таким же образом. Когда это не осуществимо (например, для генераторов на основе аппаратного обеспечения), copy реализовывать нельзя.

Что касается п. 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 считается внутренней деталью и может быть изменён без предупреждения.

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

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

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

Воспроизводимость

Используя параметр генератора случайных чисел, инициализированный заданным значением семян, можно воспроизвести ту же последовательность псевдослучайных чисел при многократном запуске программы. Однако незначительное обновление Julia (например, с 1.3 до 1.4) может изменить последовательность псевдослучайных чисел, генерируемых из конкретного значения семян. (Даже если последовательность, генерируемая низкоуровневой функцией, например, rand, не меняется, вывод функций более высокого уровня, таких как randsubseq, может измениться из-за обновлений алгоритма.) Обоснование: гарантия того, что потоки псевдослучайных чисел никогда не меняются, препятствует многим улучшениям алгоритмов.

Если вам нужна гарантия точной воспроизводимости случайных данных, рекомендуется просто сохранить данные (например, в качестве дополнительного приложения в научной публикации). (Вы также можете, конечно, указать конкретную версию Julia и манифест пакета, особенно если вам нужна воспроизводимость на уровне битов.)

Тесты программного обеспечения, которые полагаются на конкретные «случайные» данные, также обычно должны сохранять данные или включать их в код теста. С другой стороны, тесты, которые должны проходить для большинства случайных данных (например, тестирование A \ (A*x) ≈ x для случайной матрицы A = randn(n,n)) могут использовать генератор случайных чисел с фиксированным значением семян, чтобы гарантировать, что многократный запуск теста не столкнется с ошибкой из-за очень маловероятных данных (например, крайне плохо обусловленной матрицы).

Статистическое распределение, из которого берутся случайные образцы, гарантируется тем же во всех незначительных выпусках Julia.

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

Spec-Zone.ru

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