Kurzusok
/
Julia
Julia
/
Feladatok
/
Kapitányi napló
Kapitányi napló

Kapitányi napló

Tanulófeladat

Bevezetés

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:

  • Érmefeldobá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.

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:

  • Csupán 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 funkciók olyan csomagokban, amelyeket használat előtt telepíteni kell (és amelyek nem érhetők el az Exercism-ön).

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

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

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:

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

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

Eloszlásokkal való munka

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.

Egyenletes eloszlások

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.

Gauss-eloszlás

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.

A randn() függvény

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

A Random modul

Ez 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

Keverések és permutációk

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

Utasítások

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.

1. Generálj egy véletlenszerű bolygót

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'

2. Generálj egy véletlenszerű csillaghajó-lajstromszámot

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"

3. Generálj egy véletlenszerű csillagidőt

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

4. Generálj egy kerekített csillagidőt

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

5. Válassz ki néhány véletlenszerű csillaghajót egy listából.

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"

5a. Opcionálisan gondold át a többi argumentumtípust.

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.

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

Készen állsz elkezdeni a(z) Kapitányi napló feladatot?

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.