Генерация случайных чисел
Генерация случайных чисел в 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 для подробностей.
Функции генерации случайных чисел
Base.randФункция
rand([rng=GLOBAL_RNG], [S], [dims...])
Выбирает случайный элемент или массив случайных элементов из набора значений, заданного S; S может быть
- индексируемым набором (например,
1:nили['x','y','z']), - объектом
AbstractDictилиAbstractSet, - строкой (рассматриваемой как набор символов), или
- типом: множество значений для выбора затем эквивалентно
typemin(S):typemax(S)для целых чисел (это не применимо кBigInt), и к $[0, 1)$ для чисел с плавающей точкой;
S по умолчанию равен Float64 (за исключением случая, когда dims является кортежем целых чисел, в этом случае S должен быть указан).
Примеры
julia> rand(Int, 2)
2-element Array{Int64,1}:
1339893410598768192
1575814717733606317
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}:
false
true
true
true
true
false
true
false
false
true
исходный код
Base.randnФункция
randn([rng=GLOBAL_RNG], [T=Float64], [dims...])
Генерирует случайное число типа T с нормальным распределением (среднее 0, стандартное отклонение 1). Необязательно генерирует массив случайных чисел с нормальным распределением. Модуль Base в настоящее время предоставляет реализацию для типов Float16, Float32 и Float64 (по умолчанию) и их комплексных аналогов. Если тип аргумента комплексный, значения выбираются из комплексного нормального распределения с круговой симметрией.
Примеры
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(A, p) -> Vector
Возвращает вектор, состоящий из случайной подпоследовательности заданного массива A, где каждый элемент A включён (в порядке) с независимой вероятностью p. (Сложность линейна относительно p*length(A), поэтому функция эффективна даже если p небольшой, а A большой). Технически этот процесс известен как "выборка Бернулли" из A.
Random.randsubseq!Функция
randsubseq!(S, A, p)
Аналогично randsubseq, но результаты хранятся в S (который изменяет размер по мере необходимости).
Random.randpermФункция
randperm([rng=GLOBAL_RNG,] n::Integer)
Строит случайную перестановку длины n. Необязательный аргумент rng задаёт генератор случайных чисел (см. Генерация случайных чисел). Для случайной перестановки произвольного вектора, см. shuffle или shuffle!.
Примеры
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 задаёт генератор случайных чисел (см. Случайные числа).
Примеры
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 в режиме in-place: случайная перестановка 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. Некоторые генераторы RNG не принимают seed, например, RandomDevice. После вызова 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.MersenneTwisterТип
MersenneTwister(seed) MersenneTwister()
Создать объект генератора MersenneTwister RNG. Разные объекты RNG могут иметь свои семена, что полезно для генерации различных потоков случайных чисел. 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 RNG. Два таких объекта всегда будут генерировать различные потоки случайных чисел. Энтропия берется из операционной системы.
Встраивание в API Random
Существует два основных ортогональных способа расширения функциональности Random:
- генерация случайных значений пользовательских типов
- создание новых генераторов
API для 1) довольно функциональный, но относительно новый, поэтому он может ещё эволюционировать в последующих версиях модуля Random. Например, обычно достаточно реализовать один метод rand, чтобы все другие обычные методы работали автоматически.
API для 2) все ещё является базовым и может потребовать больше работы, чем строго необходимо от разработчика, для поддержки обычных типов генерируемых значений.
Генерация случайных значений пользовательских типов
Есть две категории: генерация значений из типа (например, rand(Int)) или из коллекции (например, rand(1:3)). Простые случаи будут объяснены сначала, а более продвинутые - позже. Мы предполагаем здесь, что выбор алгоритма не зависит от RNG, поэтому мы используем AbstractRNG в наших подписях.
Генерация значений из типа
Учитывая тип T, в настоящее время предполагается, что если rand(T) определен, будет создан объект типа T. Для определения случайной генерации значений типа T, можно определить следующий метод: rand(rng::AbstractRNG, ::Random.SamplerType{T}) (это должно возвращать то, что ожидается от rand(rng, T)).
Давайте рассмотрим пример: мы реализуем тип Die, со переменным количеством n граней, пронумерованных от 1 до n. Мы хотим, чтобы rand(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)
Генерация значений из коллекции
Учитывая тип коллекции S, в настоящее время предполагается, что если rand(::S) определен, будет создан объект типа eltype(S). Для определения случайной генерации из объектов типа 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
В последнем примере создаётся Vector{Any}, потому что eltype(Die) == Any. Решение заключается в определении Base.eltype(::Type{Die}) = Int.
Генерация значений для типа AbstractFloat
AbstractFloat типы являются специальными, потому что по умолчанию случайные значения не генерируются в целом типе домена, а скорее в [0,1). Следующий метод должен быть реализован для T <: AbstractFloat: Random.rand(::AbstractRNG, ::Random.SamplerTrivial{Random.CloseOpen01{T}})
Оптимизация генерации с кэшированием вычислений между вызовами
Когда многократно генерируются случайные значения (с теми же rand параметрами), для некоторых типов результат вычисления используется для каждого вызова. В этом случае вычисления можно отделить от фактической генерации значений. Это, например, относится к реализации по умолчанию для AbstractArray. Предположим, что 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)). Для реализации этого разделения для пользовательского типа можно использовать вспомогательный тип. Вернемся к нашему примеру Die: rand(::Die) использует случайную генерацию из диапазона, поэтому есть возможность для этой оптимизации:
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[].
В настоящее время доступен ещё один вспомогательный тип 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)).
Примечание: Sampler(rng, x) — это просто сокращение для Sampler(rng, x, Val(Inf)), а Random.Repetition — псевдоним для Union{Val{1}, Val{Inf}}.
Создание новых генераторов
API пока не определён чётко, но в качестве правила:
- любой метод
randдля получения "базовых" типов (isbitstypeцелых и плавающих типов вBase) должен быть определён для этого конкретного генератора случайных чисел, если они необходимы; - другие документированные методы
rand, принимающиеAbstractRNG, должны работать из коробки (при условии, что реализованы методы из пункта 1), на которых они опираются), но, конечно, могут быть специализированы для этого генератора, если есть возможность оптимизации.
Что касается пункта 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 будет автоматически вызывать эту специализацию внутри.
© 2009–2019 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/v0.7.0/stdlib/Random/