Spec-Zone.ru › Julia 0.7

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

Генерация случайных чисел в 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:

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

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 пока не определён чётко, но в качестве правила:

  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/v0.7.0/stdlib/Random/

Spec-Zone.ru

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