Sok programnak van szüksége (látszólag) véletlen értékekre, hogy valós eseményeket szimuláljon.
Gyakori és ismerős példák:
('H', 'T') halmazból.Valóban véletlen értékek előállítása számítógéppel meglepően nehéz technikai kihívás, ezért ezeket az eredményeket gyakran „álvéletlennek” nevezik.
Fontos: Ez a fogalom nem terjed ki a kriptográfiailag biztonságos véletlen számokra, amelyek jóval nehezebb kihívást jelentenek.
A jól megtervezett könyvtárak, mint például a Julia szabványos könyvtárának Random modulja azonban gyorsak, rugalmasak, és olyan eredményeket adnak, amelyek bőven elég jók a legtöbb modellezési, szimulációs és játékalkalmazáshoz.
A Julia több helyre osztja a véletlenszám-funkcionalitást:
Base-ben, amelyek mindig elérhetők.Random modulban.A Random a szabványos könyvtár része, és valószínűleg előre telepítve van, de a programod elejére be kell írnod a using Random sort, hogy a tartalma bekerüljön a névtérbe.
rand() függvényHogy ez a függvény mit csinál, az a neki átadott argumentumoktól függ. Sok lehetőség van.
Argumentumok nélkül egy 0 (beleértve) és 1 közötti lebegőpontos számot állít elő.
Ez egy uniform eloszlás, amelyben minden érték egyformán valószínű, amint arról a lenti „Eloszlások használata” szakaszban szó lesz.
Egyetlen egész szám argumentumként egy ilyen hosszúságú vektort állít elő.
julia> rand()
0.10261774967264703
julia> rand(5)
5-element Vector{Float64}:
0.24134501977563894
0.5664193284851202
0.9804412082089355
0.6229551330613335
0.47589221741904664
Ha más tartományra van szükséged, egyszerűen tolj el és skálázd az eredményt a megfelelő módon.
Az alábbi példa broadcasting használ a kivonáshoz, amelyről a vektorműveletek fogalomban lesz szó.
A .- egyszerűen minden vektorelemre alkalmazza ezt a műveletet.
# numbers between -1.0 and +1.0
julia> (rand(5) .- 0.5) * 2
5-element Vector{Float64}:
-0.5303906759076336
0.9635682226775855
-0.048823697086981754
0.465842804648374
0.9880834344780736
Ha egyetlen argumentumként egy típust adsz meg, a rand a typemin és typemax értékeket használja határként.
Ez valószínűleg nem az, amit szeretnél!
Véletlen egész számokhoz megadhatunk egy tartományt, és opcionálisan azt is, hány értéket szeretnénk előállítani.
julia> rand(Int64)
-9159538335234594326 # not very useful
julia> rand(1:10, 5)
5-element Vector{Int64}:
1
1
1
4
7
A fenti rand(1:10, 5) példában figyeld meg, hogy (véletlen egybeesésből) ismétlődő értékek vannak, mert minden húzás független.
Ez a „visszatevéses mintavétel”, amelyről lentebb részletesebben is szó lesz.
Lebegőpontos értékekhez egy tartományon belül általában meg kell adnod a lépésközt. Ellenkező esetben a lépésköz alapértelmezésben 1.0 lesz, ami ritkán hasznos.
julia> rand(2.4:0.01:3.2, 4)
4-element Vector{Float64}:
3.19
2.53
3.14
3.13
Másik lehetőség, hogy egy tömböt vagy tuple-t adsz meg, és a rand egy véletlen elemet ad vissza:
julia> rand([4, 9, 16, 25])
16
# coin flip
julia> rand(['H', 'T'])
'H': ASCII/Unicode U+0048 (category Lu: Letter, uppercase)
# mixed types in tuple
julia> rand( (1, 3.2, "name"), 2 )
2-element Vector{Any}:
1
"name"
Képzeld el, hogy van egy zsákunk, amelyben 3 piros és 4 zöld golyó van, és véletlenszerűen kihúzunk egy golyót a zsákból. A második golyó kihúzására két lehetőség van:
Az 1. eset visszatevéssel történik, a 2. eset visszatevés nélkül, és a kettő különböző eredményt ad.
A visszatevés nélküli mintavétel szimulálására a Juliában néhány lehetőség kínálkozik.
A legegyszerűbb (és az Exercism-ön belül az egyetlen lehetőség), hogy a Random.shuffle() függvénnyel véletlen sorrendbe teszed az elemeket, majd az első n elemet használod.
Ez kis méretű problémák esetén rendben van, de nagy gyűjteményekre nem biztos, hogy jól skálázódik: a shuffle-nek a teljes tömböt elő kell állítania, még akkor is, ha annak csak egy kis részére van szükséged.
Ha „rendesen” szeretnél visszatevéses mintavételt végezni, telepítsd a StatsBase.jl csomagot.
Ez adja a sample() függvényt a lehetőségek teljes tárházával.
Joggal remélhetjük, hogy hasonló funkcionalitás egy későbbi kiadásban bekerül a Random modulba, így a szabványos könyvtár részévé válik (a dokumentumban szereplő kódpéldákat a Julia 1.11-es verziójával teszteltük).
Eddig azokra az esetekre összpontosítottunk, amelyekben minden kimenetel egyformán valószínű.
Például a rand(1:100) ugyanolyan valószínűséggel ad bármely egész számot 1 és 100 között.
A valós helyzetek többsége ennél jóval összetettebb.
Ezért a statisztikusok számos distributions alkottak, hogy matematikailag leírják a „valós” eredményeket.
A fent leírt rand() függvényt akkor használjuk, amikor minden valószínűség egyenlő.
Ezt [uniform][uniform-distribution] eloszlásnak nevezzük.
Ezt „normál” eloszlásnak vagy „haranggörbének” is nevezik, és nagyon gyakori módja a mért értékek pontatlanságának leírására.
Tegyük fel például, hogy a gyár, ahol dolgozol, éppen 10 000 darab csavart vásárolt, amelyeknek azonosnak kellene lenniük.
Szeretnéd beállítani a gyári robotot, hogy kezelni tudja őket, ezért lemérsz egy 100 darabos mintát, és azt találod, hogy az átlagos (vagy mean) tömegük 4,731 g.
Ez alapján rendkívül valószínűtlen, hogy mindegyik pontosan 4,731 g legyen.
Talán azt tapasztalod, hogy az értékek 4,627 és 4,794 g között mozognak, de 4,731 g körül csoportosulnak.
Ez a Gaussian distribution, amelynél a valószínűség az átlagnál tetőzik, és mindkét oldalon szimmetrikusan lecseng (innen a „harang alakú” elnevezés).
Ahhoz, hogy ezt szoftverben szimuláljuk, valamilyen módon meg kell adnunk a görbe szélességét (jellemzően a drága csavarok szorosabban csoportosulnak az átlag körül, mint az olcsók!).
Ezt a szokás a standard deviation segítségével fejezi ki: kis érték éles, keskeny görbét, nagy érték alacsony, széles görbét jelent.
A matematikusok imádják a görög betűket, ezért a μ („mű”) az átlagot, a σ („szigma”) pedig a szórást jelöli.
Így ha azt olvasod, hogy „az értékek 95%-a μ 2σ-os körzetén belül van”, vagy hogy „a Higgs-bozont 5 szigmás konfidenciaszinttel mutatták ki”, az ilyen megjegyzések a szórásra utalnak.
Erről bővebben a Statistics fogalomban lesz szó.
randn() függvényA „random normal” rövidítése; hasonló a rand() lebegőpontos változatához, azzal a különbséggel, hogy az értékek 0 átlagú és 1 szórású Gauss-eloszlást követnek.
Itt is előfordulhat, hogy a randn nyers kimenetét a szórásnak megfelelően skálázni, az átlag szerint pedig eltolni szeretnéd.
Az alábbi példa 30-as átlagra és 5-ös szórásra alakítja át.
julia> raw = randn(5)
5-element Vector{Float64}:
3.0762588867281475
1.5101100620253902
-0.5914858221637778
0.684175554069735
-0.8416433926114673
julia> raw * 5 .+ 30
5-element Vector{Float64}:
45.38129443364074
37.55055031012695
27.04257088918111
33.420877770348675
25.791783036942665
A kimenetre pillantva nehéz megállapítani, hogy a nyers kimenet közelebb csoportosul a nullához, mint egy egyenletes eloszlás esetében. Ha kételkedsz benne, generálj 1000 vagy több értéket, és ábrázold őket, hogy nyilvánvalóbb legyen.
Random modulEz a modul a következő szintű funkcionalitást tartalmazza, amelyet a Base-ből hagytak ki, hogy segítsen minimalizálni a Julia alapértelmezett konfigurációjának méretét.
A Random a Base-ben lévő rand és randn függvényt egészíti ki a módosító változatokkal: rand! és randn!.
Hasznos kiegészítés a randstring, amely egy adott hosszúságú stringet állít elő.
Alapértelmezés szerint kis- és nagybetűket, valamint 0 és 9 közötti számjegyeket használ, de más lehetőségek is megadhatók.
julia> using Random
julia> randstring(20)
"BoJnIxrS33pJiWggXZQV"
Ezen kívül van egy bitrand függvény, amely egy adott hosszúságú véletlen BitArray tömböt állít elő.
julia> bitrand(5)
julia> bitrand(5)
5-element BitVector:
1
1
0
0
1
Egy Vector elemeinek véletlen megkeveréséhez a shuffle áll rendelkezésünkre; a shuffle! pedig a bemeneti vektort módosítja helyben.
julia> v = ['A', '1', '2', 'J', 'Q', 'K'];
julia> shuffle(v)
6-element Vector{Char}:
'K': ASCII/Unicode U+004B (category Lu: Letter, uppercase)
'1': ASCII/Unicode U+0031 (category Nd: Number, decimal digit)
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
'J': ASCII/Unicode U+004A (category Lu: Letter, uppercase)
'2': ASCII/Unicode U+0032 (category Nd: Number, decimal digit)
'Q': ASCII/Unicode U+0051 (category Lu: Letter, uppercase)
# shuffles are random:
julia> shuffle(v)
6-element Vector{Char}:
'2': ASCII/Unicode U+0032 (category Nd: Number, decimal digit)
'K': ASCII/Unicode U+004B (category Lu: Letter, uppercase)
'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
'Q': ASCII/Unicode U+0051 (category Lu: Letter, uppercase)
'J': ASCII/Unicode U+004A (category Lu: Letter, uppercase)
'1': ASCII/Unicode U+0031 (category Nd: Number, decimal digit)
Néha hasznos, ha inkább a megkevert indexekre van szükségünk.
Erre a randperm(n) használható, ahol n a sorozat hossza.
julia> randperm(6)
6-element Vector{Int64}:
6
2
4
1
3
5
Valójában a fenti példa ugyanazt az eredményt adja, mint a shuffle(1:6).
Kapcsolódó függvények közé tartozik a randsubseq, amely rögzített valószínűséggel választ ki elemeket, és a randcycle a ciklikus permutációkhoz.
Ezek némi speciális tudást igényelnek, ezért nézd meg a dokumentációt, ha érdekelnek.
A Random modulba alapból több véletlenszám-generátor (RNG) algoritmus van beépítve, és bárki, aki rendelkezik a megfelelő matematikai ismeretekkel, továbbiakat adhat hozzá.
Az ilyesmi jóval meghaladja e dokumentum kereteit!
Az RNG-kkel való munka gyakoribb oka a seed megadása, aminek az a hatása, hogy a „véletlen” kimenetek sorozata egyik futástól a másikig reprodukálhatóvá válik.
Ez a reprodukálhatóság éles kódban nem szerencsés, de segíthet a tesztelésben és a hibakeresésben.
Az Exercism-ön kívül számos, véletlennel, valószínűségszámítással és statisztikával kapcsolatos, telepíthető csomag létezik.
Bővebb információért lásd a Statistics fogalmat.
StatsBase.jl csomagA StatsBase függvényeinek többsége meglehetősen technikai jellegű, és nem tartozik e dokumentum témájához.
Kivétel a StatsBase.sample, amely a visszatevéses és visszatevés nélküli mintavétel teljes implementációját nyújtja (lásd a fenti, korábbi szakaszt).
Vannak továbbá függvények súlyozott (nem egyenletes) mintavételhez is.
Distributions.jl csomagA uniform és a normal (vagy Gauss-) eloszlást fentebb már leírtuk.
A Random modul tartalmazza a randexp függvényt is, amellyel az exponenciális eloszlásból vehetsz mintát; ez a (nagyon gyakori) Poisson-eloszlással áll kapcsolatban.
Sokkal szélesebb választékért ott van a Distributions.jl csomag azoknak, akiknek megfelelő statisztikai háttértudásuk van.