Случайные числа
Генерация случайных чисел в 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:9или('x', "y", :z)), - объектом типа
AbstractDictилиAbstractSet, - строкой (рассматриваемой как массив символов) или
- типом: множество значений для выбора тогда эквивалентно
typemin(S):typemax(S)для целых чисел (это не применимо кBigInt), и $[0, 1)$ для чисел с плавающей точкой;
S по умолчанию равен Float64.
Поддержка S как кортежа требует как минимум Julia 1.1.
Примеры
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 (по умолчанию), и их комплексных аналогов Complex. Когда аргумент типа является комплексным, значения выбираются из кругового симметричного комплексного нормального распределения.
Примеры
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.
Примеры
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 . Разные объекты генераторов могут иметь свои седы, что может быть полезно для генерации разных потоков случайных чисел. 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:
- генерация случайных значений пользовательских типов
- создание новых генераторов
API для 1) довольно функционален, но относительно новый, поэтому в последующих релизах модуля Random он может эволюционировать. Например, обычно достаточно реализовать один метод rand, чтобы все остальные обычные методы работали автоматически.
API для 2) всё ещё примитивен и может потребовать больше работы, чем строго необходимо от разработчика, чтобы поддерживать обычные типы генерируемых значений.
Генерация случайных значений пользовательских типов
Существуют две категории: генерация значений из типа (например, rand(Int)) или из коллекции (например, rand(1:3)). Простые случаи объяснены сначала, а более продвинутое использование представлено позже. Здесь мы предполагаем, что выбор алгоритма независим от генератора случайных чисел, поэтому мы используем 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 ) для некоторых типов результат вычисления используется для каждого вызова. В этом случае вычисления можно отделить от фактической генерации значений. Так происходит, например, с реализацией генерации случайных массивов по умолчанию (как в rand(1:20, 10) ). Предположим, что 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/v1.1.1/stdlib/Random/