Random Numbers

توليد الأرقام العشوائية في جوليا يستخدم خوارزمية Xoshiro256++ بشكل افتراضي، مع حالة لكل Task. يمكن توصيل أنواع أخرى من مولدات الأرقام العشوائية عن طريق وراثة نوع AbstractRNG؛ يمكن استخدامها بعد ذلك للحصول على عدة تدفقات من الأرقام العشوائية.

مولدات الأرقام العشوائية الزائفة (PRNGs) التي يتم تصديرها بواسطة حزمة Random هي:

  • TaskLocalRNG: رمز يمثل استخدام دفق المهام المحلي النشط حاليًا، مزروع بشكل حتمي من المهمة الأصلية، أو بواسطة RandomDevice (مع عشوائية النظام) عند بدء البرنامج
  • Xoshiro: يولد تدفقًا عالي الجودة من الأرقام العشوائية مع متجه حالة صغير وأداء عالٍ باستخدام خوارزمية Xoshiro256++
  • RandomDevice: للحصول على عشوائية مقدمة من نظام التشغيل. يمكن استخدام ذلك للحصول على أرقام عشوائية آمنة من الناحية التشفيرية (CS(P)RNG).
  • MersenneTwister: مولد أرقام عشوائية عالي الجودة بديل كان الافتراضي في الإصدارات القديمة من جوليا، وهو سريع أيضًا، ولكنه يتطلب مساحة أكبر بكثير لتخزين متجه الحالة وتوليد تسلسل عشوائي.

تقبل معظم الدوال المتعلقة بالتوليد العشوائي كائن AbstractRNG اختياري كأول وسيط. كما تقبل بعض الدوال أيضًا مواصفات الأبعاد dims... (والتي يمكن أيضًا تقديمها كزوج مرتب) لتوليد مصفوفات من القيم العشوائية. في برنامج متعدد الخيوط، يجب عليك عمومًا استخدام كائنات RNG مختلفة من خيوط أو مهام مختلفة من أجل أن تكون آمنًا من حيث الخيوط. ومع ذلك، فإن RNG الافتراضي آمن من حيث الخيوط اعتبارًا من Julia 1.3 (باستخدام RNG لكل خيط حتى الإصدار 1.6، وRNG لكل مهمة بعد ذلك).

يمكن أن تولد مولدات الأرقام العشوائية المقدمة أرقامًا عشوائية موحدة من الأنواع التالية: 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 لمزيد من التفاصيل.

لإنشاء أرقام عشوائية من توزيعات أخرى، راجع حزمة Distributions.jl.

Warning

لأن الطريقة الدقيقة التي يتم بها توليد الأرقام العشوائية تعتبر من تفاصيل التنفيذ، قد تؤدي إصلاحات الأخطاء وتحسينات السرعة إلى تغيير تدفق الأرقام التي يتم توليدها بعد تغيير النسخة. لذلك، يُنصح بعدم الاعتماد على بذور معينة أو تدفق محدد من الأرقام أثناء اختبار الوحدة - بدلاً من ذلك، اعتبر اختبار خصائص الطرق المعنية.

Random numbers module

Random generation functions

Base.randFunction
rand([rng=default_rng()], [S], [dims...])

اختر عنصرًا عشوائيًا أو مصفوفة من العناصر العشوائية من مجموعة القيم المحددة بواسطة S؛ يمكن أن تكون S

  • مجموعة قابلة للفهرسة (على سبيل المثال 1:9 أو ('x', "y", :z))

  • كائن AbstractDict أو AbstractSet

  • سلسلة (تعتبر مجموعة من الأحرف)، أو

  • نوع من القائمة أدناه، يتوافق مع مجموعة القيم المحددة

    • الأنواع الصحيحة الملموسة تأخذ عينات من typemin(S):typemax(S) (باستثناء BigInt الذي لا يدعمه)
    • الأنواع العائمة الملموسة تأخذ عينات من [0, 1)
    • الأنواع المركبة الملموسة Complex{T} إذا كان T نوعًا يمكن أخذ عينات منه، تأخذ مكوناتها الحقيقية والتخييلية بشكل مستقل من مجموعة القيم المقابلة لـ T، ولكن لا تدعم إذا لم يكن T قابلًا لأخذ العينات.
    • جميع الأنواع <:AbstractChar تأخذ عينات من مجموعة القيم الصحيحة لـ Unicode
    • نوع معرف من قبل المستخدم ومجموعة من القيم؛ للحصول على إرشادات التنفيذ، يرجى الاطلاع على Hooking into the Random API
    • نوع من التوابل المعروفة الحجم حيث يكون كل معلم من S هو نوع قابل لأخذ العينات؛ إرجاع قيمة من نوع S. لاحظ أن أنواع التوابل مثل Tuple{Vararg{T}} (حجم غير معروف) و Tuple{1:2} (مُعلمة بقيمة) غير مدعومة
    • نوع Pair، مثل Pair{X, Y} بحيث يتم تعريف rand لـ X و Y، وفي هذه الحالة يتم إنتاج أزواج عشوائية.

S افتراضيًا هو Float64. عندما يتم تمرير حجة واحدة فقط بخلاف rng الاختياري وتكون Tuple، يتم تفسيرها كمجموعة من القيم (S) وليس كـ dims.

انظر أيضًا randn للأرقام الموزعة بشكل طبيعي، و rand! و randn! للمعادلات في المكان.

Julia 1.1

يتطلب دعم S كـ tuple على الأقل Julia 1.1.

Julia 1.11

يتطلب دعم S كنوع Tuple على الأقل Julia 1.11.

أمثلة

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

julia> using Random

julia> rand(Xoshiro(0), Dict(1=>2, 3=>4))
3 => 4

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
Note

تعقيد rand(rng, s::Union{AbstractDict,AbstractSet}) هو خطي في طول s، ما لم تكن هناك طريقة محسّنة ذات تعقيد ثابت متاحة، وهو الحال بالنسبة لـ Dict و Set و BitSets الكثيفة. لأكثر من بضع مكالمات، استخدم rand(rng, collect(s)) بدلاً من ذلك، أو إما rand(rng, Dict(s)) أو rand(rng, Set(s)) حسب الاقتضاء.

source
Random.rand!Function
rand!([rng=default_rng()], A, [S=eltype(A)])

قم بملء المصفوفة A بقيم عشوائية. إذا تم تحديد S (S يمكن أن يكون نوعًا أو مجموعة، انظر rand للحصول على التفاصيل)، يتم اختيار القيم عشوائيًا من S. هذا يعادل copyto!(A, rand(rng, S, size(A))) ولكن دون تخصيص مصفوفة جديدة.

أمثلة

julia> rand!(Xoshiro(123), zeros(5))
5-element Vector{Float64}:
 0.521213795535383
 0.5868067574533484
 0.8908786980927811
 0.19090669902576285
 0.5256623915420473
source
Random.bitrandFunction
bitrand([rng=default_rng()], [dims...])

توليد BitArray من قيم بوليانية عشوائية.

أمثلة

julia> bitrand(Xoshiro(123), 10)
10-element BitVector:
 0
 1
 0
 1
 0
 1
 0
 0
 1
 1
source
Base.randnFunction
randn([rng=default_rng()], [T=Float64], [dims...])

توليد رقم عشوائي موزع بشكل طبيعي من النوع T بمتوسط 0 وانحراف معياري 1. مع الأخذ في الاعتبار الوسيط الاختياري dims، يتم توليد مصفوفة بحجم dims من هذه الأرقام. تدعم مكتبة جوليا القياسية randn لأي نوع من أنواع النقاط العائمة التي تنفذ rand، مثل الأنواع الأساسية Float16، Float32، Float64 (الافتراضي)، وBigFloat، جنبًا إلى جنب مع نظيراتها Complex.

(عندما يكون T معقدًا، يتم سحب القيم من توزيع طبيعي معقد دائري متماثل بتباين 1، مما يتوافق مع الأجزاء الحقيقية والتخيلية التي لها توزيع طبيعي مستقل بمتوسط صفر وتباين 1/2).

انظر أيضًا randn! للعمل في المكان.

أمثلة

توليد رقم عشوائي واحد (مع النوع الافتراضي Float64):

julia> randn()
-0.942481877315864

توليد مصفوفة من الأرقام العشوائية الطبيعية (مع النوع الافتراضي Float64):

julia> randn(2,3)
2×3 Matrix{Float64}:
  1.18786   -0.678616   1.49463
 -0.342792  -0.134299  -1.45005

إعداد مولد الأرقام العشوائية rng مع بذور محددة من قبل المستخدم (لأرقام قابلة للتكرار) واستخدامه لتوليد رقم عشوائي من نوع Float32 أو مصفوفة من الأرقام العشوائية ComplexF32:

julia> using Random

julia> rng = Xoshiro(123);

julia> randn(rng, Float32)
-0.6457307f0

julia> randn(rng, ComplexF32, (2, 3))
2×3 Matrix{ComplexF32}:
  -1.03467-1.14806im  0.693657+0.056538im   0.291442+0.419454im
 -0.153912+0.34807im    1.0954-0.948661im  -0.543347-0.0538589im
source
Random.randn!Function
randn!([rng=default_rng()], A::AbstractArray) -> A

املأ المصفوفة A بأرقام عشوائية موزعة بشكل طبيعي (بمتوسط 0، وانحراف معياري 1). راجع أيضًا دالة rand.

أمثلة

julia> randn!(Xoshiro(123), zeros(5))
5-element Vector{Float64}:
 -0.6457306721039767
 -1.4632513788889214
 -1.6236037455860806
 -0.21766510678354617
  0.4922456865251828
source
Random.randexpFunction
randexp([rng=default_rng()], [T=Float64], [dims...])

توليد رقم عشوائي من النوع T وفقًا للتوزيع الأسي مع مقياس 1. يمكن اختيارياً توليد مصفوفة من هذه الأرقام العشوائية. يوفر وحدة Base حاليًا تنفيذًا للأنواع Float16، Float32، و Float64 (الافتراضي).

أمثلة

julia> rng = Xoshiro(123);

julia> randexp(rng, Float32)
1.1757717f0

julia> randexp(rng, 3, 3)
3×3 Matrix{Float64}:
 1.37766  0.456653  0.236418
 3.40007  0.229917  0.0684921
 0.48096  0.577481  0.71835
source
Random.randexp!Function
randexp!([rng=default_rng()], A::AbstractArray) -> A

املأ المصفوفة A بأرقام عشوائية تتبع التوزيع الأسي (مع مقياس 1).

أمثلة

julia> randexp!(Xoshiro(123), zeros(5))
5-element Vector{Float64}:
 1.1757716836348473
 1.758884569451514
 1.0083623637301151
 0.3510644315565272
 0.6348266443720407
source
Random.randstringFunction
randstring([rng=default_rng()], [chars], [len=8])

قم بإنشاء سلسلة عشوائية بطول len، تتكون من أحرف من chars، والتي تكون افتراضيًا مجموعة من الأحرف الكبيرة والصغيرة والأرقام من 0-9. يحدد الوسيط الاختياري rng مولد الأرقام العشوائية، انظر Random Numbers.

أمثلة

julia> Random.seed!(3); randstring()
"Lxz5hUwn"

julia> randstring(Xoshiro(3), 'a':'z', 6)
"iyzcsm"

julia> randstring("ACGT")
"TGCTCCTC"

!!! ملاحظة يمكن أن تكون chars أي مجموعة من الأحرف، من نوع Char أو UInt8 (أكثر كفاءة)، بشرط أن يتمكن rand من اختيار الأحرف منها بشكل عشوائي.

source

Subsequences, permutations and shuffling

Random.randsubseqFunction
randsubseq([rng=default_rng(),] A, p) -> Vector

إرجاع متجه يتكون من تسلسل فرعي عشوائي من المصفوفة المعطاة A، حيث يتم تضمين كل عنصر من A (بالترتيب) باحتمالية مستقلة p. (التعقيد خطي في p*length(A)، لذا فإن هذه الدالة فعالة حتى لو كان p صغيرًا وA كبيرًا.) تقنيًا، تُعرف هذه العملية باسم "عينات برنولي" من A.

أمثلة

julia> randsubseq(Xoshiro(123), 1:8, 0.3)
2-element Vector{Int64}:
 4
 7
source
Random.randsubseq!Function
randsubseq!([rng=default_rng(),] S, A, p)

مثل randsubseq، ولكن يتم تخزين النتائج في S (الذي يتم تغيير حجمه حسب الحاجة).

أمثلة

julia> S = Int64[];

julia> randsubseq!(Xoshiro(123), S, 1:8, 0.3)
2-element Vector{Int64}:
 4
 7

julia> S
2-element Vector{Int64}:
 4
 7
source
Random.randpermFunction
randperm([rng=default_rng(),] n::Integer)

قم بإنشاء ترتيب عشوائي بطول n. يحدد الوسيط الاختياري rng مولد الأرقام العشوائية (انظر الأرقام العشوائية). نوع عنصر النتيجة هو نفس نوع n.

لإعادة ترتيب متجه عشوائي بشكل عشوائي، انظر shuffle أو shuffle!.

جوليا 1.1

في جوليا 1.1، تعيد randperm متجهًا v حيث eltype(v) == typeof(n) بينما في جوليا 1.0 eltype(v) == Int.

أمثلة

julia> randperm(Xoshiro(123), 4)
4-element Vector{Int64}:
 1
 4
 2
 3
source
Random.randperm!Function
randperm!([rng=default_rng(),] A::Array{<:Integer})

قم بإنشاء ترتيب عشوائي في A بطول length(A). يحدد الوسيط الاختياري rng مولد الأرقام العشوائية (انظر الأرقام العشوائية). لخلط متجه عشوائي، انظر shuffle أو shuffle!.

أمثلة

julia> randperm!(Xoshiro(123), Vector{Int}(undef, 4))
4-element Vector{Int64}:
 1
 4
 2
 3
source
Random.randcycleFunction
randcycle([rng=default_rng(),] n::Integer)

قم بإنشاء تبديل دوري عشوائي بطول n. يحدد الوسيط الاختياري rng مولد الأرقام العشوائية، انظر الأرقام العشوائية. نوع العنصر في النتيجة هو نفس نوع n.

هنا، يعني "التبديل الدوري" أن جميع العناصر تقع ضمن دورة واحدة. إذا كان n > 0، فهناك $(n-1)!$ تبديلات دورية ممكنة، والتي يتم أخذ عينات منها بشكل موحد. إذا كان n == 0، فإن randcycle ترجع متجهًا فارغًا.

randcycle! هو نسخة في المكان من هذه الدالة.

Julia 1.1

في Julia 1.1 وما فوق، ترجع randcycle متجهًا v حيث eltype(v) == typeof(n) بينما في Julia 1.0 eltype(v) == Int.

أمثلة

julia> randcycle(Xoshiro(123), 6)
6-element Vector{Int64}:
 5
 4
 2
 6
 3
 1
source
Random.randcycle!Function
randcycle!([rng=default_rng(),] A::Array{<:Integer})

قم بإنشاء في A ترتيب دوري عشوائي بطول n = length(A). يحدد الوسيط الاختياري rng مولد الأرقام العشوائية، انظر Random Numbers.

هنا، يعني "الترتيب الدوري" أن جميع العناصر تقع ضمن دورة واحدة. إذا كانت A غير فارغة (n > 0)، فهناك $(n-1)!$ ترتيبات دورية ممكنة، والتي يتم أخذ عينات منها بشكل موحد. إذا كانت A فارغة، فإن randcycle! تتركها دون تغيير.

randcycle هو متغير من هذه الدالة يقوم بتخصيص متجه جديد.

أمثلة

julia> randcycle!(Xoshiro(123), Vector{Int}(undef, 6))
6-element Vector{Int64}:
 5
 4
 2
 6
 3
 1
source
Random.shuffleFunction
shuffle([rng=default_rng(),] v::AbstractArray)

إرجاع نسخة مرتبة عشوائيًا من v. يحدد الوسيط الاختياري rng مولد أرقام عشوائية (انظر الأرقام العشوائية). لخلط v في مكانه، انظر shuffle!. للحصول على مؤشرات مرتبة عشوائيًا، انظر randperm.

أمثلة

julia> shuffle(Xoshiro(123), Vector(1:10))
10-element Vector{Int64}:
  5
  4
  2
  3
  6
 10
  8
  1
  9
  7
source
Random.shuffle!Function
shuffle!([rng=default_rng(),] v::AbstractArray)

نسخة في المكان من shuffle: قم بخلط v عشوائيًا في المكان، مع إمكانية تزويد مولد الأرقام العشوائية rng.

أمثلة

julia> shuffle!(Xoshiro(123), Vector(1:10))
10-element Vector{Int64}:
  5
  4
  2
  3
  6
 10
  8
  1
  9
  7
source

Generators (creation and seeding)

Random.default_rngFunction
Random.default_rng() -> rng

ارجع مولد الأرقام العشوائية (RNG) الافتراضي العالمي، والذي يُستخدم بواسطة دوال rand ذات الصلة عندما لا يتم توفير RNG صريح.

عند تحميل وحدة Random، يتم تهيئة RNG الافتراضي عشوائيًا، عبر Random.seed!(): هذا يعني أنه في كل مرة يتم فيها بدء جلسة جوليا جديدة، فإن أول استدعاء لـ rand() ينتج نتيجة مختلفة، ما لم يتم استدعاء seed!(seed) أولاً.

إنه آمن للاستخدام في الخيوط: يمكن للخيوط المتميزة استدعاء دوال rand ذات الصلة على default_rng() بشكل متزامن بأمان، على سبيل المثال rand(default_rng()).

Note

نوع RNG الافتراضي هو تفصيل تنفيذي. عبر إصدارات جوليا المختلفة، يجب ألا تتوقع أن يكون لـ RNG الافتراضي دائمًا نفس النوع، ولا أنه سينتج نفس سلسلة الأرقام العشوائية لزر معين.

Julia 1.3

تم تقديم هذه الدالة في جوليا 1.3.

source
Random.seed!Function
seed!([rng=default_rng()], seed) -> rng
seed!([rng=default_rng()]) -> rng

إعادة تهيئة مولد الأرقام العشوائية: rng سيعطي تسلسلًا قابلًا للتكرار من الأرقام إذا وفقط إذا تم توفير seed. بعض مولدات الأرقام العشوائية لا تقبل بذورًا، مثل RandomDevice. بعد استدعاء seed!، يصبح rng مكافئًا لكائن تم إنشاؤه حديثًا تم تهيئته بنفس البذور. تعتمد أنواع البذور المقبولة على نوع rng، ولكن بشكل عام، يجب أن تعمل البذور الصحيحة.

إذا لم يتم تحديد rng، فإنه يتضمن بشكل افتراضي تهيئة حالة المولد المشترك المحلي للمهام.

أمثلة

julia> Random.seed!(1234);

julia> x1 = rand(2)
2-element Vector{Float64}:
 0.32597672886359486
 0.5490511363155669

julia> Random.seed!(1234);

julia> x2 = rand(2)
2-element Vector{Float64}:
 0.32597672886359486
 0.5490511363155669

julia> x1 == x2
true

julia> rng = Xoshiro(1234); rand(rng, 2) == x1
true

julia> Xoshiro(1) == Random.seed!(rng, 1)
true

julia> rand(Random.seed!(rng), Bool) # not reproducible
true

julia> rand(Random.seed!(rng), Bool) # not reproducible either
false

julia> rand(Xoshiro(), Bool) # not reproducible either
true
source
Random.TaskLocalRNGType
TaskLocalRNG

TaskLocalRNG لديه حالة محلية لمهمته، وليس لخيطه. يتم تهيئته عند إنشاء المهمة، من حالة المهمة الأصلية، ولكن دون التقدم في حالة مولد الأرقام العشوائية (RNG) الخاص بالوالد.

كميزة، فإن TaskLocalRNG سريع جدًا، ويسمح بمحاكاة متعددة الخيوط قابلة للتكرار (باستثناء حالات السباق)، مستقلة عن قرارات الجدولة. طالما أن عدد الخيوط لا يُستخدم لاتخاذ قرارات بشأن إنشاء المهام، فإن نتائج المحاكاة تكون أيضًا مستقلة عن عدد الخيوط / وحدات المعالجة المركزية المتاحة. يجب ألا تعتمد سلسلة الأرقام العشوائية على تفاصيل الأجهزة، حتى النهاية و possibly حجم الكلمة.

استخدام أو تهيئة مولد الأرقام العشوائية (RNG) لأي مهمة أخرى غير تلك التي تم إرجاعها بواسطة current_task() هو سلوك غير محدد: سيعمل معظم الوقت، وقد يفشل أحيانًا بصمت.

عند تهيئة TaskLocalRNG() باستخدام seed!، يمكن أن تكون البذور الممررة، إن وجدت، أي عدد صحيح.

Julia 1.11

تتطلب تهيئة TaskLocalRNG() باستخدام بذور عدد صحيح سلبية على الأقل Julia 1.11.

Julia 1.10

لم يعد إنشاء المهام يتقدم في حالة مولد الأرقام العشوائية (RNG) الخاص بالمهمة الأصلية اعتبارًا من Julia 1.10.

source
Random.XoshiroType
Xoshiro(seed::Union{Integer, AbstractString})
Xoshiro()

Xoshiro256++ هو مولد أرقام عشوائية زائفة سريع تم وصفه بواسطة ديفيد بلاكمان وسيباستيان فيغنا في "مولدات الأرقام العشوائية الخطية المخلوطة"، ACM Trans. Math. Softw.، 2021. التنفيذ المرجعي متاح على https://prng.di.unimi.it

بصرف النظر عن السرعة العالية، يتمتع Xoshiro بذاكرة صغيرة، مما يجعله مناسبًا للتطبيقات التي تحتاج إلى الاحتفاظ بالعديد من الحالات العشوائية المختلفة لفترة طويلة.

تحتوي تنفيذ Xoshiro في جوليا على وضع توليد جماعي؛ حيث يقوم بتوليد مولدات أرقام عشوائية افتراضية جديدة من الأصل، ويستخدم SIMD للتوليد بالتوازي (أي أن تدفق الكتلة يتكون من عدة حالات xoshiro متداخلة). يتم التخلص من مولدات الأرقام العشوائية الافتراضية بمجرد معالجة الطلب الجماعي (ويجب ألا تتسبب في أي تخصيصات للذاكرة).

إذا لم يتم توفير بذور، يتم إنشاء واحدة تم توليدها عشوائيًا (باستخدام الفوضى من النظام). راجع دالة seed! لإعادة تزويد كائن Xoshiro موجود بالفعل.

جوليا 1.11

يتطلب تمرير بذور عدد صحيح سالب على الأقل جوليا 1.11.

أمثلة

julia> using Random

julia> rng = Xoshiro(1234);

julia> x1 = rand(rng, 2)
2-element Vector{Float64}:
 0.32597672886359486
 0.5490511363155669

julia> rng = Xoshiro(1234);

julia> x2 = rand(rng, 2)
2-element Vector{Float64}:
 0.32597672886359486
 0.5490511363155669

julia> x1 == x2
true
source
Random.MersenneTwisterType
MersenneTwister(seed)
MersenneTwister()

قم بإنشاء كائن مولد أرقام عشوائية MersenneTwister. يمكن أن تحتوي كائنات مولد الأرقام العشوائية المختلفة على بذور خاصة بها، مما قد يكون مفيدًا لتوليد تدفقات مختلفة من الأرقام العشوائية. قد تكون seed عددًا صحيحًا، أو سلسلة، أو متجه من الأعداد الصحيحة UInt32. إذا لم يتم توفير بذور، يتم إنشاء واحدة عشوائيًا (باستخدام الفوضى من النظام). راجع دالة seed! لإعادة تهيئة كائن MersenneTwister موجود بالفعل.

Julia 1.11

يتطلب تمرير بذور عدد صحيح سالب على الأقل Julia 1.11.

أمثلة

julia> rng = MersenneTwister(123);

julia> x1 = rand(rng, 2)
2-element Vector{Float64}:
 0.37453777969575874
 0.8735343642013971

julia> x2 = rand(MersenneTwister(123), 2)
2-element Vector{Float64}:
 0.37453777969575874
 0.8735343642013971

julia> x1 == x2
true
source
Random.RandomDeviceType
RandomDevice()

قم بإنشاء كائن RNG من نوع RandomDevice. ستولد هذان الكائنان دائمًا تدفقات مختلفة من الأرقام العشوائية. يتم الحصول على الانتروبيا من نظام التشغيل.

source

Hooking into the Random API

هناك طريقتان أساسيتان لتوسيع وظائف Random:

  1. توليد قيم عشوائية من أنواع مخصصة
  2. إنشاء مولدات جديدة

واجهة برمجة التطبيقات لـ 1) وظيفية إلى حد ما، لكنها حديثة نسبيًا لذا قد تحتاج إلى التطور في الإصدارات اللاحقة من وحدة Random. على سبيل المثال، عادةً ما يكون من الكافي تنفيذ طريقة rand واحدة لكي تعمل جميع الطرق المعتادة الأخرى تلقائيًا.

واجهة برمجة التطبيقات لـ 2) لا تزال بدائية، وقد تتطلب المزيد من العمل أكثر مما هو ضروري بشكل صارم من المنفذ، من أجل دعم الأنواع المعتادة من القيم المولدة.

Generating random values of custom types

توليد قيم عشوائية لبعض التوزيعات قد ينطوي على مجموعة من المساومات. القيم المحسوبة مسبقًا، مثل alias table للتوزيعات المنفصلة، أو “squeezing” functions للتوزيعات أحادية المتغير، يمكن أن تسرع من عملية أخذ العينات بشكل كبير. مقدار المعلومات التي يجب حسابها مسبقًا يمكن أن يعتمد على عدد القيم التي نخطط لاستخراجها من توزيع ما. أيضًا، يمكن أن تحتوي بعض مولدات الأرقام العشوائية على خصائص معينة قد ترغب خوارزميات مختلفة في استغلالها.

يحدد نموذج Random إطارًا قابلاً للتخصيص للحصول على قيم عشوائية يمكن أن تعالج هذه القضايا. كل استدعاء لـ rand يولد عينة يمكن تخصيصها مع وضع الاعتبارات المذكورة أعلاه في الاعتبار، من خلال إضافة طرق إلى Sampler، والتي يمكن أن تقوم بدورها بتوجيه على مولد الأرقام العشوائية، والكائن الذي يميز التوزيع، واقتراح لعدد التكرارات. حاليًا، بالنسبة للأخير، يتم استخدام Val{1} (لعينة واحدة) و Val{Inf} (لعدد غير محدد)، مع كون Random.Repetition اسم مستعار لكليهما.

الكائن الذي يتم إرجاعه بواسطة Sampler يُستخدم بعد ذلك لتوليد القيم العشوائية. عند تنفيذ واجهة توليد القيم العشوائية لقيمة X يمكن أخذ عينات منها، يجب على المنفذ تعريف الطريقة

rand(rng, sampler)

لـ sampler المحدد الذي تم إرجاعه بواسطة Sampler(rng, X, repetition).

يمكن أن تكون العينات قيمًا عشوائية تنفذ rand(rng, sampler)، ولكن بالنسبة لمعظم التطبيقات، قد تكون العينات المعرفة مسبقًا التالية كافية:

  1. SamplerType{T}() يمكن استخدامه لتنفيذ العينات التي تسحب من النوع T (على سبيل المثال rand(Int)). هذا هو الافتراضي الذي يتم إرجاعه بواسطة Sampler للأنواع.
  2. SamplerTrivial(self) هو غلاف بسيط لـ self، والذي يمكن الوصول إليه باستخدام []. هذا هو العينة الموصى بها عندما لا تكون هناك معلومات مسبقة الحساب مطلوبة (على سبيل المثال، rand(1:3))، وهو الافتراضي الذي يتم إرجاعه بواسطة Sampler لـ القيم.
  3. SamplerSimple(self, data) يحتوي أيضًا على حقل data الإضافي، والذي يمكن استخدامه لتخزين قيم مسبقة الحساب عشوائية، والتي يجب حسابها في طريقة مخصصة لـ Sampler.

نحن نقدم أمثلة لكل من هذه. نفترض هنا أن اختيار الخوارزمية مستقل عن مولد الأرقام العشوائية، لذا نستخدم AbstractRNG في توقيعاتنا.

Random.SamplerType
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 لتخزين القيم المحسوبة مسبقًا دون الحاجة إلى تعريف أنواع إضافية فقط لهذا الغرض.

source
Random.SamplerTypeType
SamplerType{T}()

عينة للأنواع، تحتوي على معلومات أخرى. الخيار الافتراضي لـ Sampler عند استدعائه مع الأنواع.

source
Random.SamplerTrivialType
SamplerTrivial(x)

أنشئ عينة تغلف القيمة المعطاة x. هذه هي الحالة الافتراضية للقيم. نوع العنصر (eltype) لهذه العينة يساوي eltype(x).

الحالة الموصى بها هي أخذ عينات من القيم دون بيانات محسوبة مسبقًا.

source
Random.SamplerSimpleType
SamplerSimple(x, data)

قم بإنشاء عينة تغلف القيمة المعطاة x و data. نوع العنصر (eltype) لهذه العينة يساوي eltype(x).

حالة الاستخدام الموصى بها هي أخذ عينات من القيم مع بيانات محسوبة مسبقًا.

source

فصل الحساب المسبق عن توليد القيم فعليًا هو جزء من واجهة برمجة التطبيقات، وهو متاح أيضًا للمستخدم. كمثال، افترض أن rand(rng, 1:20) يجب أن يتم استدعاؤه بشكل متكرر في حلقة: الطريقة للاستفادة من هذا الفصل هي كما يلي:

rng = Xoshiro()
sp = Random.Sampler(rng, 1:20) # or Random.Sampler(Xoshiro, 1:20)
for x in X
    n = rand(rng, sp) # similar to n = rand(rng, 1:20)
    # use n
end

هذه هي الآلية التي تُستخدم أيضًا في المكتبة القياسية، على سبيل المثال من خلال التنفيذ الافتراضي لتوليد المصفوفات العشوائية (مثل rand(1:20, 10)).

Generating values from a type

بالنظر إلى نوع 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(5)

julia> rand(Xoshiro(0), Die)
Die(10)

julia> rand(Die, 3)
3-element Vector{Die}:
 Die(9)
 Die(15)
 Die(14)

julia> a = Vector{Die}(undef, 3); rand!(a)
3-element Vector{Die}:
 Die(19)
 Die(7)
 Die(17)

A simple sampler without pre-computed data

هنا نحدد عينة لمجموعة. إذا لم تكن هناك حاجة لبيانات مسبقة الحساب، يمكن تنفيذها باستخدام عينة 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))
1

julia> rand(Die(4), 3)
3-element Vector{Any}:
 2
 3
 3

نظرًا لنوع المجموعة S، يُفترض حاليًا أنه إذا تم تعريف rand(::S)، فسيتم إنتاج كائن من نوع eltype(S). في المثال الأخير، تم إنتاج Vector{Any}؛ والسبب هو أن eltype(Die) == Any. العلاج هو تعريف Base.eltype(::Type{Die}) = Int.

Generating values for an AbstractFloat type

AbstractFloat الأنواع لها حالات خاصة، لأنه بشكل افتراضي لا يتم إنتاج قيم عشوائية في كامل نطاق النوع، بل في [0,1). يجب تنفيذ الطريقة التالية لـ T <: AbstractFloat: Random.rand(::AbstractRNG, ::Random.SamplerTrivial{Random.CloseOpen01{T}})

An optimized sampler with pre-computed data

اعتبر توزيعًا منفصلًا، حيث يتم سحب الأرقام 1:n مع احتمالات معينة مجموعها واحد. عندما تكون هناك حاجة إلى العديد من القيم من هذا التوزيع، فإن أسرع طريقة هي استخدام alias table. نحن لا نقدم الخوارزمية لبناء مثل هذا الجدول هنا، ولكن افترض أنه متاح في 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(distribution, make_alias_table(distribution.probabilities))
end

يجب أن يتم تعريفه لإرجاع عينة مع بيانات مسبقة الحساب، ثم

function rand(rng::AbstractRNG, sp::SamplerSimple{<:DiscreteDistribution})
    draw_number(rng, sp.data)
end

سيتم استخدامه لرسم القيم.

Custom sampler types

نوع 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، ولكنه يعتبر واجهة برمجة تطبيقات داخلية، ويمكن أن يتعطل في أي وقت دون إعلانات إلغاء مناسبة.

Using distinct algorithms for scalar or array generation

في بعض الحالات، سواء كان المرء يرغب في توليد عدد قليل من القيم أو عدد كبير من القيم سيؤثر على اختيار الخوارزمية. يتم التعامل مع ذلك من خلال المعامل الثالث في مُنشئ 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}}.

Creating new generators

لا يتم تعريف واجهة برمجة التطبيقات بوضوح بعد، ولكن كقاعدة عامة:

  1. يجب تعريف أي طريقة rand تنتج أنواع "أساسية" (isbitstype الأعداد الصحيحة وأنواع الفلوات في Base) لهذا المولد العشوائي المحدد، إذا كانت مطلوبة؛
  2. يجب أن تعمل طرق rand الأخرى الموثقة التي تقبل AbstractRNG بشكل مباشر، (بشرط أن يتم تنفيذ الطرق المعتمدة من 1) ، ولكن يمكن بالطبع تخصيصها لهذا RNG إذا كان هناك مجال للتحسين؛
  3. copy يجب أن تعيد نسخة مستقلة تنتج نفس تسلسل الأرقام العشوائية مثل الأصل من تلك النقطة عند استدعائها بنفس الطريقة. عندما لا يكون ذلك ممكنًا (مثل مولدات الأرقام العشوائية المعتمدة على الأجهزة)، يجب عدم تنفيذ copy.

فيما يتعلق بالنقطة 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) في الممارسة العملية (إذا أراد المرء تخصيص التوليد لهذا النوع المحدد من RNG)، ولكن يعتبر نوع SamplerS كتفصيل داخلي، وقد يتغير دون إشعار.

Specializing array generation

في بعض الحالات، بالنسبة لنوع معين من مولد الأرقام العشوائية، يمكن أن يكون إنشاء مصفوفة من القيم العشوائية أكثر كفاءة باستخدام طريقة متخصصة بدلاً من مجرد استخدام تقنية الفصل الموضحة سابقًا. هذه هي الحالة على سبيل المثال لـ MersenneTwister، الذي يكتب القيم العشوائية في مصفوفة بشكل أصلي.

لتنفيذ هذا التخصص لـ MyRNG و لمواصفة s، التي تنتج عناصر من النوع S، يمكن تعريف الطريقة التالية: rand!(rng::MyRNG, a::AbstractArray{S}, ::SamplerS)، حيث SamplerS هو نوع العينة التي يتم إرجاعها بواسطة Sampler(MyRNG, s, Val(Inf)). بدلاً من AbstractArray، من الممكن تنفيذ الوظيفة فقط لنوع فرعي، مثل Array{S}. ستقوم طريقة rand غير المتغيرة للمصفوفة باستدعاء هذا التخصص تلقائيًا داخليًا.

Reproducibility

باستخدام معلمة RNG مُهيأة ببذور معينة، يمكنك إعادة إنتاج نفس تسلسل الأرقام العشوائية الزائفة عند تشغيل برنامجك عدة مرات. ومع ذلك، قد تؤدي إصدار فرعي صغير من جوليا (مثل 1.3 إلى 1.4) إلى تغيير تسلسل الأرقام العشوائية الزائفة الناتجة عن بذور معينة، خاصة إذا تم استخدام MersenneTwister. (حتى إذا لم يتغير التسلسل الناتج عن دالة منخفضة المستوى مثل rand، قد يتغير ناتج الدوال عالية المستوى مثل randsubseq بسبب تحديثات الخوارزمية.) السبب: ضمان عدم تغيير تدفقات الأرقام العشوائية الزائفة يمنع العديد من التحسينات الخوارزمية.

إذا كنت بحاجة إلى ضمان إعادة إنتاج دقيقة للبيانات العشوائية، فمن المستحسن ببساطة حفظ البيانات (على سبيل المثال، كملحق إضافي في منشور علمي). (يمكنك أيضًا، بالطبع، تحديد إصدار معين من جوليا وبيان الحزمة، خاصة إذا كنت بحاجة إلى إعادة إنتاج بت.)

يجب على اختبارات البرمجيات التي تعتمد على بيانات "عشوائية" محددة أن تقوم عمومًا إما بحفظ البيانات، أو تضمينها في كود الاختبار، أو استخدام حزم طرف ثالث مثل StableRNGs.jl. من ناحية أخرى، يمكن للاختبارات التي يجب أن تنجح مع معظم البيانات العشوائية (مثل اختبار A \ (A*x) ≈ x لمصفوفة عشوائية A = randn(n,n)) استخدام مولد أرقام عشوائية مع بذور ثابتة لضمان أن تشغيل الاختبار عدة مرات لا يواجه فشلًا بسبب بيانات غير محتملة جدًا (مثل مصفوفة ذات حالة سيئة للغاية).

التوزيع الإحصائي الذي تُسحب منه العينات العشوائية مضمون أن يكون هو نفسه عبر أي إصدارات فرعية صغيرة من جوليا.