Случайные числа
Генерация случайных чисел в Julia использует библиотеку Mersenne Twister через объекты MersenneTwister. Julia имеет глобальный генератор случайных чисел (RNG), который используется по умолчанию. Другие типы RNG могут быть подключены, унаследовав тип AbstractRNG; затем они могут использоваться для создания нескольких потоков случайных чисел. Помимо MersenneTwister, Julia также предоставляет тип RNG RandomDevice, который является оболочкой над энтропией, предоставленной ОС.
Большинство функций, связанных с генерацией случайных чисел, принимают необязательный объект AbstractRNG в качестве первого аргумента, который по умолчанию равен глобальному, если не указан. Кроме того, некоторые из них принимают необязательные спецификации размерности dims... (которые могут быть заданы как кортеж) для генерации массивов случайных значений.
RNG типа 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 для подробностей.
Поскольку точный способ генерации случайных чисел считается реализацией, исправления ошибок и улучшения скорости могут изменить поток чисел, сгенерированных после изменения версии. Поэтому не рекомендуется полагаться на определенный seed или сгенерированный поток чисел во время тестирования на единицу — вместо этого следует тестировать свойства проверяемых методов.
Модуль случайных чисел
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 (по умолчанию) и их комплексными аналогами Complex. Когда аргумент типа — комплексное число, значения выбираются из круговой симметричной комплексной нормальной распределения с дисперсией 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!(3); randstring()
"4zSHdXlw"
julia> randstring(MersenneTwister(3), 'a':'z', 6)
"bzlhqn"
julia> randstring("ACGT")
"AGGACATT"
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, 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, 1:8, 0.3)
2-element Array{Int64,1}:
7
8
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. Некоторые генераторы случайных чисел не принимают семя, например, RandomDevice. После вызова seed!, rng эквивалентно только что созданному объекту, инициализированному тем же семенем.
Если rng не указан, используется посев состояния общего генератора потока.
Примеры
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 может быть целым неотрицательным числом или вектором целых UInt32 чисел. Если семя не указано, генерируется случайное семя (используя энтропию из системы). См. функцию 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:
- генерация случайных значений пользовательских типов
- создание новых генераторов
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), но для большинства приложений могут быть достаточными следующие предопределённые генераторы:
SamplerType{T}()может быть использован для реализации генераторов, которые берут значения из типаT(например,rand(Int)). Это значение по умолчанию, возвращаемоеSamplerдля типов.SamplerTrivial(self)— это простой оболочный объект дляself, который можно получить с помощью[]. Это рекомендуемый генератор, когда нет необходимости в предварительно вычисленных данных (например,rand(1:3)). Это значение по умолчанию, возвращаемоеSamplerдля значений.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(10)
julia> rand(MersenneTwister(0), Die)
Die(16)
julia> rand(Die, 3)
3-element Array{Die,1}:
Die(5)
Die(20)
Die(9)
julia> a = Vector{Die}(undef, 3); rand!(a)
3-element Array{Die,1}:
Die(11)
Die(20)
Die(10)
Простой генератор без предварительно вычисленных данных
Здесь мы определяем генератор для коллекции. Если предварительно вычисленные данные не нужны, его можно реализовать с помощью генератора 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))
2
julia> rand(Die(4), 3)
3-element Array{Any,1}:
1
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 выбираются с заданными вероятностями, которые суммируются до единицы. Когда из этого распределения требуется много значений, самый быстрый метод — использование таблицы псевдонимов. Мы не предоставляем здесь алгоритм построения такой таблицы, но предположим, что она доступна в make_alias_table(probabilities) вместо этого, и draw_number(rng, alias_table) можно использовать для генерации случайного числа из неё.
Предположим, что распределение описывается
struct DiscreteDistribution{V <: AbstractVector}
probabilities::V
end
и что мы всегда хотим создавать таблицу псевдонимов, независимо от количества необходимых значений (мы узнаем, как настроить это ниже). Методы
Random.eltype(::Type{<:DiscreteDistribution}) = Int
function Random.Sampler(::Type{<: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 пока не определен чётко, но как правило:
- любой
randметод, производящий «базовые» типы (isbitstypeцелые и плавающие типы вBase), должен быть определен для этого конкретного генератора случайных чисел, если они необходимы; - другие документированные
randметоды, принимающиеAbstractRNG, должны работать «из коробки» (при условии, что реализованы методы из п. 1), на которые они опираются), но, конечно, могут быть специализированы для этого генератора, если есть возможность оптимизации; -
copyдля псевдогенераторов должен возвращать независимую копию, генерирующую точно такую же последовательность случайных чисел, как оригинал, с того момента, когда она вызывается таким же образом. Когда это нецелесообразно (например, генераторы случайных чисел на основе аппаратного обеспечения),copyне должен быть реализован.
Что касается п. 1), метод rand может случайно работать автоматически, но это не официально поддерживается и может сломаться без предупреждений в последующих выпусках.
Для определения нового метода rand для гипотетического генератора MyRNG и спецификации значения s (например, s == Int, или s == 1:10 ) типа S==typeof(s) или S==Type{s}, если s является типом, необходимо определить те же два метода, что и раньше:
-
Sampler(::Type{MyRNG}, ::S, ::Repetition), который возвращает объект типа, скажем,SamplerS 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 автоматически вызовет эту специализацию.
Воспроизводимость
Используя параметр генератора случайных чисел, инициализированный заданным seed, можно воспроизвести ту же последовательность псевдослучайных чисел при многократном запуске программы. Однако незначительное обновление Julia (например, с 1.3 до 1.4) может изменить последовательность псевдослучайных чисел, сгенерированных из определённого seed. (Даже если последовательность, производимая низкоуровневой функцией, такой как rand, не изменяется, вывод высокоуровневых функций, таких как randsubseq, может измениться из-за обновлений алгоритмов.) Обоснование: гарантия, что потоки псевдослучайных чисел никогда не меняются, препятствует многим улучшениям алгоритмов.
Если вам нужна гарантия точной воспроизводимости случайных данных, рекомендуется просто сохранить данные (например, в качестве дополнительного приложения в научной публикации). (Вы также, конечно, можете указать конкретную версию Julia и манифест пакета, особенно если вам требуется битовая воспроизводимость.)
Тесты программного обеспечения, которые полагаются на конкретные «случайные» данные, также, как правило, должны сохранять данные или включать их в код теста. С другой стороны, тесты, которые должны проходить для большинства случайных данных (например, тестирование A \ (A*x) ≈ x для случайной матрицы A = randn(n,n) ), могут использовать генератор случайных чисел с фиксированным seed, чтобы гарантировать, что простой многократный запуск теста не вызовет сбой из-за очень маловероятных данных (например, чрезвычайно плохо обусловленной матрицы).
Статистическое распределение, из которого извлекаются случайные выборки, гарантируется одинаковым во всех незначительных выпусках Julia.
© 2009–2020 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/v1.5.3/stdlib/Random/