Kurzusok
/
Julia
Julia
/
Tanterv
/
Véletlenszerűség
Vé

Véletlenszerűség ebben a kurzusban: Julia

2 feladat

A(z) Véletlenszerűség fogalomról

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:

  • Érmedobás: véletlen érték a ('H', 'T') halmazból.
  • Kockadobás: véletlen egész szám 1 és 6 között.
  • Kártyapakli megkeverése: egy kártyalista véletlen sorrendje.

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:

  • Néhány alapvető, de nagyon sokoldalú függvény a Base-ben, amelyek mindig elérhetők.
  • Szélesebb választék a Random modulban.
  • Specializáltabb funkcionalitás olyan csomagokban, amelyeket használat előtt telepíteni kell (és amelyek az Exercism-ön nem érhetők el).

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.

A rand() függvény

Hogy 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"

Mintavétel visszatevéssel vagy anélkül

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:

  1. Tedd vissza az első golyót a zsákba, és alaposan rázd össze az egészet, mielőtt újabb golyót húzol. A golyók száma most ugyanannyi, mint korábban (7), és a piros és zöld golyók aránya is ugyanaz.
  2. Tedd az első golyót az asztalra, mielőtt kihúzod a másodikat. Most már csak 6 golyó van a zsákban, és a piros:zöld arány az első golyó színétől függ.

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).

Eloszlások használata

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.

Egyenletes eloszlások

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.

Gauss-eloszlás

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ó.

A randn() függvény

A „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.

A Random modul

Ez 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

Keverés és permutációk

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.

Seedek és algoritmusok

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.

Egyéb csomagok

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.

A StatsBase.jl csomag

A 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.

A Distributions.jl csomag

A 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.

Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg

Tanuld meg a(z) Véletlenszerűség fogalmat

A gyakorlás zárolva

Oldj fel még 1 feladatot, hogy gyakorolhasd a(z) Véletlenszerűség fogalmat