Spec-Zone.ru › Julia 1.2

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

Генерация случайных чисел в 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 для подробностей.

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

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)$ для чисел с плавающей точкой;

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

Julia 1.1

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

Примеры

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

julia> using Random

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

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

исходный код

Random.rand!Функция

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

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

Примеры

julia> rng = MersenneTwister(1234);

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

Random.bitrandФункция

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

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

Примеры

julia> rng = MersenneTwister(1234);

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

Base.randnФункция

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

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

Примеры

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

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

Примеры

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

Random.randperm!Функция

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

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

Примеры

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

Random.randcycleФункция

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

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

Julia 1.1

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

Примеры

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

Random.randcycle!Функция

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

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

Примеры

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

Random.shuffleФункция

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

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

Примеры

julia> rng = MersenneTwister(1234);

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

Random.shuffle!Функция

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

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

Примеры

julia> rng = MersenneTwister(1234);

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

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

Random.seed!Функция

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

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

Примеры

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! для переинициализации уже существующего объекта 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) всё ещё является предварительным и может потребовать больше работы, чем строго необходимо от разработчика, чтобы поддержать обычные типы генерируемых значений.

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

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

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

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

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

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

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

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

В некоторых случаях для данного типа генератора случайных чисел генерация массива случайных значений может быть более эффективной с помощью специализированного метода, чем с помощью простого использования техники разделения, описанной ранее. Например, это справедливо для 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.2.0/stdlib/Random/

Spec-Zone.ru

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