Sok programnak van szüksége (látszólag) véletlen értékekre valós események szimulálásához.
Gyakori és ismerős példák:
('H', 'T') halmazból.Számítógéppel valóban véletlen értékeket generálni meglepően nehéz technikai kihívás, ezért előfordulhat, hogy ezeket az eredményeket „álvéletlennek” nevezik.
Fontos: Ez a fogalom nem foglalkozik kriptográfiailag biztonságos véletlen számokkal, amelyek sokkal nehezebb kihívást jelentenek.
Azonban a jól megtervezett könyvtárak, mint például a Julia szabványos könyvtárában található Random modul, gyorsak, rugalmasak, és olyan eredményeket adnak, amelyek bőven elég jók a modellezés, szimuláció és játékok legtöbb alkalmazásához.
A Julia több helyre osztja a véletlenszerűséggel kapcsolatos funkciókat:
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 tesz, az attól függ, milyen argumentumokat adsz neki. Sok lehetőség van.
Argumentum nélkül egy 0 (beleértve) és 1 közötti lebegőpontos számot generál.
Ez egy uniform eloszlás, ahol minden érték egyformán valószínű, ahogy az alábbi, Eloszlásokkal való munka szakaszban tárgyaljuk.
Egyetlen egész szám argumentum egy ilyen hosszúságú vektort generál.
julia> rand()
0.10261774967264703
julia> rand(5)
5-element Vector{Float64}:
0.24134501977563894
0.5664193284851202
0.9804412082089355
0.6229551330613335
0.47589221741904664
Más tartományhoz egyszerűen tolj el és skálázd az eredményt megfelelően.
Az alábbi példa broadcastingot használ a kivonáshoz, amelyet a Vektorműveletek fogalom tárgyal.
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 egy típus az egyetlen argumentum, 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 generáljunk.
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ül) ismétlődő értékek vannak, mert minden húzás független.
Ez a „visszatevéssel történő mintavétel”, amelyet alább részletesebben tárgyalunk.
Egy tartományban lévő lebegőpontos értékekhez általában meg kell adnod a lépésközt. Különben 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
Alternatívaként adj meg egy tömböt vagy tuple-t, é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 zacskónk, amelyben 3 piros és 4 zöld golyó van, és véletlenszerűen kihúzunk egy golyót a zacskóból. Ahhoz, hogy egy második golyót is kapjunk, két lehetőség van:
Az 1. forgatókönyv visszatevéssel történik, a 2. anélkül, és ezek különböző eredményeket adnak.
Ahhoz, hogy Juliában visszatevés nélküli mintavételt szimuláljunk, néhány lehetőség van.
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 kisebb problémákhoz rendben van, de nagy gyűjteményekhez 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 csak egy kis részét szeretnéd.
Ha „rendesen” szeretnél visszatevéssel történő mintavételt végezni, telepítsd a StatsBase.jl csomagot.
Az biztosítja a sample() függvényt a lehetőségek teljes skálájával.
Joggal remélhetjük, hogy egy hasonló funkció egy jövőbeli kiadásban bekerül a Random-ba, hogy a szabványos könyvtár része legyen (a dokumentum kódpéldáit a Julia 1.11-gyel teszteltük).
Eddig azokra az esetekre összpontosítottunk, ahol 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 élet sok helyzete ennél jóval kevésbé egyszerű.
Ennek eredményeként a statisztikusok a distributions széles választékát alkották meg, 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 eloszlásnak nevezzük.
Más néven „normális” eloszlás vagy „haranggörbe”, ez a mért értékek pontatlanságának nagyon gyakori leírási módja.
Tegyük fel például, hogy a gyár, ahol dolgozol, épp most vásárolt 10 000 csavart, amelyeknek egyformáknak kellene lenniük.
Szeretnéd beállítani a gyári robotot, hogy kezelni tudja őket, ezért megmérsz egy 100 darabos mintát, és azt találod, hogy az átlagos (vagy mean) tömegük 4,731 g.
Ez rendkívül valószínűtlen, hogy azt jelentse, mindegyik pontosan 4,731 g.
Talán azt találod, 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égek az átlagnál érik el a csúcsukat, és mindkét oldalon szimmetrikusan lecsengenek (innen a „harang alakú”).
Ahhoz, hogy ezt szoftverben szimuláljuk, kell valamilyen mód a görbe szélességének megadására (a drága csavarok általában szorosabban csoportosulnak az átlag körül, mint az olcsók!).
A szokás szerint erre a standard deviation szolgál: kis értékek éles, keskeny görbét adnak, nagyok alacsony, széles görbét.
A matematikusok imádják a görög betűket, ezért a μ („mű”) jelöli az átlagot, a σ („szigma”) pedig a szórást.
Így ha azt olvasod, hogy „az értékek 95%-a μ 2σ-ján belül van”, vagy hogy „a Higgs-bozont 5 szigma konfidenciával detektálták”, az ilyen megjegyzések a szórásra vonatkoznak.
Erről több szó esik majd a Statistics fogalomban.
randn() függvényA „random normal” rövidítéseként ez hasonlít 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.
Ismét előfordulhat, hogy a randn nyers kimenetét skálázni szeretnéd a szórás, és eltolni az átlag szerint.
Az alábbi példa 30-as átlagra és 5-ös szórásra alakítja.
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 nézve 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 tartalmazza a következő szintű funkciókat, amelyeket a Base-ből hagytak ki, hogy segítsenek minimalizálni a Julia alapértelmezett konfigurációjának méretét.
A Random kiegészíti a Base-ben lévő rand és randn függvényeket mutáló változatokkal, a rand! és randn! függvényekkel.
Hasznos kiegészítés a randstring, amely adott hosszúságú stringet generál.
Alapértelmezés szerint nagy- és kisbetűket, valamint 0-tól 9-ig terjedő 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 adott hosszúságú véletlen BitArray-t generál.
julia> bitrand(5)
julia> bitrand(5)
5-element BitVector:
1
1
0
0
1
Egy Vector elemeinek véletlen megkeverésére ott van a shuffle; és a shuffle! is, amely helyben módosítja a bemeneti vektort.
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 helyette a megkevert indexeket kapjuk meg.
Erre használd a randperm(n) függvényt, ahol n a sorozat hossza.
julia> randperm(6)
6-element Vector{Int64}:
6
2
4
1
3
5
Gyakorlatilag a fenti példa ugyanazokat az eredményeket adja, mint a shuffle(1:6).
Mary nagy rajongója a Star Trek: The Next Generation tévésorozatnak. Gyakran játszik asztali szerepjátékokat, amelyekben a barátaival együtt a Starship Enterprise legénységét alakítják. Mary karaktere Picard kapitány, ami azt jelenti, hogy neki kell vezetnie a kapitányi naplót. Imádja a játék kreatív részét, de nem szereti a helyszínen kitalálni a véletlenszerű adatokat.
Segíts Marynek véletlenszerű generátorokat készíteni azokhoz az adatokhoz, amelyek gyakran előfordulnak a kapitányi naplóban.
A Starship Enterprise utazásai során sok bolygóval találkozik. A Star Trek univerzumában a bolygókat tulajdonságaik alapján kategóriákba sorolják. Például a Föld M osztályú bolygó. A lehetséges bolygóosztályok: D, H, J, K, L, M, N, R, T és Y.
Valósítsd meg a random_planet függvényt. Véletlenszerűen adja vissza az egyik bolygóosztályt.
julia> random_planet_class()
'K'
Az Enterprise (lajstromszáma NCC-1701) nem az egyetlen csillaghajó az űrben! Amikor találkozik egy másik csillaghajóval, Marynek fel kell jegyeznie annak lajstromszámát.
A lajstromszámok a „NCC-” előtaggal kezdődnek, majd egy 1000 és 9999 közötti (mindkét végpontot beleértve) szám következik.
Valósítsd meg a random_ship_registry_number függvényt, amely véletlenszerű csillaghajó-lajstromszámot ad vissza.
julia> random_ship_registry_number()
"NCC-1947"
Mi haszna a naplónak, ha nincsenek benne dátumok?
A csillagidő egy lebegőpontos szám. A Starship Enterprise kalandjai a The Next Generation első évadjában a 41000.0 és 42000.0 közötti csillagidőkben játszódnak. A „4” a 24. századot jelöli, az „1” pedig az első évadot.
Valósítsd meg a random_stardate függvényt, amely 41000.0 (beleértve) és 42000.0 közötti lebegőpontos számot ad vissza.
julia> random_stardate()
41458.15721310934
Sok panasz érkezett, hogy a csillagidőkben túl sok a tizedesjegy, ezért Mary úgy dönt, hogy egy tizedesjegyre kerekíti a naplóbejegyzéseket.
Bár ezt meg lehetne tenni a random_stardate() értékeinek kerekítésével, rájössz, hogy van egy sokkal egyszerűbb megoldás.
Valósítsd meg a random_stardate_v2 függvényt, amely 41000.0 és 42000.0 közötti (mindkét végpontot beleértve) lebegőpontos számot ad vissza, egyetlen tizedesjeggyel.
julia> random_stardate_v2()
41732.6
A játék egyik változatában más csillaghajókkal kell kapcsolatba lépni, és Marynek szüksége van egy válogatásra.
Van egy teljes lista a lehetséges csillaghajókról, de minden játékban csak néhányra van szükség, és azokat véletlenszerűen kell kiválasztani.
Valósítsd meg a pick_starships(starships, number_needed) függvényt, amely egy egyedi csillaghajó-lajstromszámokat tartalmazó vektort kap, és egy number_needed hosszúságú vektort ad vissza.
Az eredményértékeket a starships vektorából kell venni, ismétlődések nélkül.
julia> pick_starships(["NCC-5011", "NCC-1228", "NCC-7039", "NCC-3978", "NCC-1476"], 2)
2-element Vector{String}:
"NCC-1228"
"NCC-5011"
A starships argumentumot vektorra szűkítettük.
Hogyan valósítanád meg a pick_starships() függvényt, hogy más iterálható adatszerkezetekkel is működjön, például tuple-ökkel vagy halmazokkal?
Erre a részre nincsenek tesztek.
Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Julia nyelvet 35 fogalom128 feladat segítségével, valódi emberi mentorálással, mindez ingyen.