BlessRNG vÔi RNG Ôigluse kontrollimine

BlessRNG vÔi RNG Ôigluse kontrollimine

MĂ€nguarenduses tuleb sageli midagi juhuslikkusega siduda: Unityl on selleks oma Random ja sellega paralleelselt System.Random. Kunagi ammu ĂŒhe projekti puhul jĂ€i mulje, et mĂ”lemad vĂ”iksid toimida erinevalt (kuigi nende jaotus peaks olema ĂŒhtlane).

Siis nad detailidesse ei laskunud - piisas sellest, et ĂŒleminek sĂŒsteemile System.Random parandas kĂ”ik probleemid. NĂŒĂŒd otsustasime seda ĂŒksikasjalikumalt uurida ja teha vĂ€ikese uuringu: kui "kallutatud" vĂ”i prognoositavad on RNG-d ja millist neist valida. Veelgi enam, olen korduvalt kuulnud vastakaid arvamusi nende "aususe" kohta - proovime vĂ€lja mĂ”elda, kuidas tegelikud tulemused vĂ”rreldavad deklareeritud tulemustega.

LĂŒhiharidusprogramm ehk RNG on tegelikult RNG

Kui olete juhuslike numbrite generaatoritega juba tuttav, vÔite kohe minna jaotisse "Testimine".

Juhuslikud arvud (RN) on arvude jada, mis on genereeritud mĂ”ne juhusliku (kaootilise) protsessi abil, mis on entroopia allikas. See tĂ€hendab, et see on jada, mille elemendid ei ole omavahel seotud ĂŒhegi matemaatilise seadusega – neil pole pĂ”hjuse-tagajĂ€rje seost.

Juhusliku arvu loomist nimetatakse juhuslike arvude generaatoriks (RNG). NÀib, et kÔik on elementaarne, kuid kui liigume teooriast praktikasse, siis tegelikult pole sellise jada genereerimiseks tarkvaraalgoritmi rakendamine nii lihtne.

PĂ”hjus peitub selles, et kaasaegses tarbeelektroonikas puudub sama kaos. Ilma selleta lakkavad juhuslikud arvud olemast juhuslikud ja nende generaator muutub ilmselgelt mÀÀratletud argumentide tavaliseks funktsiooniks. Mitmete IT-valdkonna erialade jaoks on see tĂ”sine probleem (nĂ€iteks krĂŒptograafia), kuid teiste jaoks on tĂ€iesti vastuvĂ”etav lahendus.

On vaja kirjutada algoritm, mis tagastaks, ehkki mitte pÀris juhuslikud arvud, vaid neile vÔimalikult lÀhedased - nn pseudojuhuslikud numbrid (PRN). Sel juhul nimetatakse algoritmi pseudojuhuslike arvude generaatoriks (PRNG).

PRNG loomiseks on mitu vÔimalust, kuid jÀrgmine on asjakohane kÔigile:

  1. Eelinitsialiseerimise vajadus.

    PRNG-l pole entroopia allikat, seega tuleb enne kasutamist anda sellele algseisund. See on mÀÀratud arvuna (vĂ”i vektorina) ja seda nimetatakse seemneks (juhuslik seeme). Sageli kasutatakse seemnena protsessori kella loendurit vĂ”i sĂŒsteemiaja numbrilist ekvivalenti.

  2. JĂ€rjestuste reprodutseeritavus.

    PRNG on tĂ€ielikult deterministlik, seega mÀÀrab lĂ€htestamise kĂ€igus mÀÀratud seeme ĂŒheselt kogu tulevase numbrijada. See tĂ€hendab, et sama seemnega (eri aegadel, erinevates programmides, erinevates seadmetes) lĂ€htestatud eraldi PRNG genereerib sama jada.

Samuti peate teadma PRNG-d iseloomustavat tĂ”enĂ€osusjaotust – milliseid numbreid see genereerib ja millise tĂ”enĂ€osusega. Enamasti on see kas normaaljaotus vĂ”i ĂŒhtlane jaotus.
BlessRNG vÔi RNG Ôigluse kontrollimine
Normaaljaotus (vasakul) ja ĂŒhtlane jaotus (paremal)

Oletame, et meil on 24 kĂŒljega aus. Kui viskate selle ĂŒmber, on ĂŒhe saamise tĂ”enĂ€osus 1/24 (sama mis tĂ”enĂ€osusega saada mĂ”ni muu arv). Kui teete palju viskeid ja fikseerite tulemused, mĂ€rkate, et kĂ”ik servad kukuvad vĂ€lja ligikaudu sama sagedusega. PĂ”himĂ”tteliselt vĂ”ib seda stantsi pidada ĂŒhtlase jaotusega RNG-ks.

Mis siis, kui viskad 10 tĂ€ringut korraga ja loeksid kokku punktid? Kas selle puhul sĂ€ilib ĂŒhtsus? Ei. KĂ”ige sagedamini on see summa 125 punkti lĂ€hedal, st mĂ”nele keskmisele vÀÀrtusele. Ja tĂ€nu sellele saab juba enne viske sooritamist umbkaudu hinnata tulevast tulemust.

PĂ”hjus on selles, et keskmise skoori saamiseks on kĂ”ige rohkem kombinatsioone. Mida kaugemal sellest, seda vĂ€hem kombinatsioone - ja vastavalt, seda vĂ€iksem on kaotuse tĂ”enĂ€osus. Kui need andmed visualiseerida, sarnanevad need ebamÀÀraselt kella kujuga. SeetĂ”ttu vĂ”ib teatud venitusega 10 tĂ€ringust koosnevat sĂŒsteemi nimetada normaaljaotusega RNG-ks.

Veel ĂŒks nĂ€ide, ainult seekord lennukis – sihtmĂ€rki laskmine. Tulistaja on RNG, mis genereerib arvude paari (x, y), mis kuvatakse graafikul.
BlessRNG vÔi RNG Ôigluse kontrollimine
NĂ”us, et vasakpoolne valik on tegelikule elule lĂ€hemal - see on normaaljaotusega RNG. Kuid kui teil on vaja tĂ€hti pimedas taevas hajutada, sobib paremini Ă”ige valik, mis on saadud ĂŒhtlase jaotusega RNG-ga. Üldiselt valige generaator sĂ”ltuvalt kĂ€sil olevast ĂŒlesandest.

NĂŒĂŒd rÀÀgime PNG-jada entroopiast. NĂ€iteks on jada, mis algab jĂ€rgmiselt:

89, 93, 33, 32, 82, 21, 4, 42, 11, 8, 60, 95, 53, 30, 42, 19, 34, 35, 62, 23, 44, 38, 74, 36, 52, 18, 58, 79, 65, 45, 99, 90, 82, 20, 41, 13, 88, 76, 82, 24, 5, 54, 72, 19, 80, 2, 74, 36, 71, 9, ...

Kui juhuslikud need numbrid esmapilgul on? Alustuseks kontrollime jaotust.
BlessRNG vÔi RNG Ôigluse kontrollimine
See nĂ€eb vĂ€lja peaaegu ĂŒhtlane, kuid kui loete kahe numbri jada ja tĂ”lgendate neid tasapinna koordinaatidena, saate jĂ€rgmise:
BlessRNG vÔi RNG Ôigluse kontrollimine
Mustrid muutuvad selgelt nÀhtavaks. Ja kuna jadas olevad andmed on teatud viisil jÀrjestatud (st neil on madal entroopia), vÔib see pÔhjustada just selle "kaldsuse". VÀhemalt selline PRNG ei sobi tasapinnal koordinaatide genereerimiseks.

Teine jÀrjestus:

42, 72, 17, 0, 30, 0, 15, 9, 47, 19, 35, 86, 40, 54, 97, 42, 69, 19, 20, 88, 4, 3, 67, 27, 42, 56, 17, 14, 20, 40, 80, 97, 1, 31, 69, 13, 88, 89, 76, 9, 4, 85, 17, 88, 70, 10, 42, 98, 96, 53, ...

Siin tundub kÔik hÀsti olevat isegi lennukis:
BlessRNG vÔi RNG Ôigluse kontrollimine
Vaatame mahu jÀrgi (loe kolme numbrit korraga):
BlessRNG vÔi RNG Ôigluse kontrollimine
Ja jÀlle mustrid. NeljamÔÔtmelist visualiseerimist pole enam vÔimalik konstrueerida. Kuid mustrid vÔivad eksisteerida nii sellel kui ka suurematel dimensioonidel.

KrĂŒptograafias, kus PRNG-dele esitatakse kĂ”ige rangemad nĂ”uded, on selline olukord kategooriliselt vastuvĂ”etamatu. SeetĂ”ttu on nende kvaliteedi hindamiseks vĂ€lja töötatud spetsiaalsed algoritmid, mida me nĂŒĂŒd ei puuduta. Teema on ulatuslik ja vÀÀrib eraldi artiklit.

Katsetamine

Kui me midagi kindlalt ei tea, siis kuidas sellega töötada? Kas tasub teed ĂŒletada, kui ei tea, milline foor seda lubab? TagajĂ€rjed vĂ”ivad olla erinevad.

Sama kehtib Unity kurikuulsa juhuslikkuse kohta. Hea, kui dokumentatsioonist selgub vajalikud detailid, kuid artikli alguses mainitud lugu juhtus just soovitud spetsiifika puudumise tÔttu.

Ja kui te ei tea, kuidas tööriist töötab, ei saa te seda Ă”igesti kasutada. Üldiselt on kĂ€tte jĂ”udnud aeg kontrollida ja lĂ€bi viia eksperiment, et lĂ”puks veenduda vĂ€hemalt levitamises.

Lahendus oli lihtne ja tĂ”hus – koguge statistikat, hankige objektiivseid andmeid ja vaadake tulemusi.

Õppeaine

Juhuslike arvude genereerimiseks Unitys on mitu vĂ”imalust – testisime viit.

  1. SĂŒsteem.Juhuslik.JĂ€rgmine(). Genereerib tĂ€isarvud antud vÀÀrtusvahemikus.
  2. System.Random.NextDouble(). Genereerib topelttÀpsusega numbrid vahemikus [0; 1).
  3. UnityEngine.Random.Range(). Genereerib ĂŒksikud tĂ€ppisarvud (ujuvad) antud vÀÀrtusvahemikus.
  4. UnityEngine.Random.value. Genereerib ĂŒksikud tĂ€ppisarvud (ujuvad) vahemikus [0; 1).
  5. Ühtsus.Matemaatika.Juhuslik.NextFloat(). Osa uuest Unity.Mathematics raamatukogust. Genereerib ĂŒksikud tĂ€ppisarvud (ujuvad) antud vÀÀrtusvahemikus.

Peaaegu kĂ”ikjal dokumentatsioonis mÀÀrati ĂŒhtlane jaotus, vĂ€lja arvatud UnityEngine.Random.value (kus jaotust ei tĂ€psustatud, kuid analoogselt UnityEngine.Random.Range() ĂŒhtlust eeldati ka) ja Unity.Mathematics.Random .NextFloat() (kus aluseks on xorshift algoritm, mis tĂ€hendab, et jĂ€lle tuleb oodata ĂŒhtlast jaotust).

Vaikimisi vÔeti oodatavaid tulemusi dokumentatsioonis mÀÀratletutena.

Metoodika

Kirjutasime vÀikese rakenduse, mis genereeris iga esitatud meetodi abil juhuslike arvude jadad ja salvestas tulemused edasiseks töötlemiseks.

Iga jada pikkus on 100 000 numbrit.
Juhuslike arvude vahemik on [0, 100).

Andmeid koguti mitmelt sihtplatvormilt:

  • Windows
    — Unity v2018.3.14f1, redigeerimisreĆŸiim, mono, .NET standard 2.0
  • macOS
    — Unity v2018.3.14f1, redigeerimisreĆŸiim, mono, .NET standard 2.0
    — Unity v5.6.4p4, redaktorireĆŸiim, mono, .NET standard 2.0
  • Android
    — Unity v2018.3.14f1, ehitamine seadme kohta, Mono, .NET standard 2.0
  • iOS
    — Unity v2018.3.14f1, jĂ€rg seadme kohta, il2cpp, .NET standard 2.0

Đ Đ”Đ°Đ»ĐžĐ·Đ°Ń†ĐžŃ

Meil on juhuslike arvude genereerimiseks mitu erinevat viisi. IgaĂŒhe jaoks kirjutame eraldi ĂŒmbrisklassi, mis peaks pakkuma:

  1. VÔimalus mÀÀrata vÀÀrtuste vahemik [min/max). Seadistab konstruktori kaudu.
  2. MF-i tagastamise meetod. Valime tĂŒĂŒbiks ujuki, kuna see on ĂŒldisem.
  3. Tulemuste mÀrgistamise genereerimismeetodi nimetus. Mugavuse huvides tagastame vÀÀrtusena klassi tÀisnime + MF genereerimiseks kasutatud meetodi nime.

Esiteks deklareerime abstraktsiooni, mida esindab IRandomGeneratori liides:

namespace RandomDistribution
{
    public interface IRandomGenerator
    {
        string Name { get; }

        float Generate();
    }
}

System.Random.Next() juurutamine

See meetod vÔimaldab teil mÀÀrata vÀÀrtuste vahemiku, kuid see tagastab tÀisarvud, kuid ujukid on vajalikud. TÀisarvu saate lihtsalt tÔlgendada ujuvana vÔi vÀÀrtuste vahemikku mitme suurusjÀrgu vÔrra laiendada, kompenseerides neid keskvahemiku iga pÔlvkonnaga. Tulemuseks on midagi kindla tÀpsusega fikseeritud punkti sarnast. Kasutame seda valikut, kuna see on tegelikule ujuvvÀÀrtusele lÀhemal.

using System;

namespace RandomDistribution
{
    public class SystemIntegerRandomGenerator : IRandomGenerator
    {
        private const int DefaultFactor = 100000;
        
        private readonly Random _generator = new Random();
        private readonly int _min;
        private readonly int _max;
        private readonly int _factor;


        public string Name => "System.Random.Next()";


        public SystemIntegerRandomGenerator(float min, float max, int factor = DefaultFactor)
        {
            _min = (int)min * factor;
            _max = (int)max * factor;
            _factor = factor;
        }


        public float Generate() => (float)_generator.Next(_min, _max) / _factor;
    }
}

SĂŒsteemi System.Random.NextDouble() rakendamine

Siin on fikseeritud vÀÀrtuste vahemik [0; 1). Selle projekteerimiseks konstruktoris mÀÀratletule kasutame lihtsat aritmeetikat: X * (max − min) + min.

using System;

namespace RandomDistribution
{
    public class SystemDoubleRandomGenerator : IRandomGenerator
    {
        private readonly Random _generator = new Random();
        private readonly double _factor;
        private readonly float _min;


        public string Name => "System.Random.NextDouble()";


        public SystemDoubleRandomGenerator(float min, float max)
        {
            _factor = max - min;
            _min = min;
        }


        public float Generate() => (float)(_generator.NextDouble() * _factor) + _min;
    }
}

UnityEngine.Random.Range() rakendamine

See UnityEngine.Random staatilise klassi meetod vĂ”imaldab teil mÀÀrata vÀÀrtuste vahemikku ja tagastab ujukitĂŒĂŒbi. Te ei pea tegema tĂ€iendavaid teisendusi.

using UnityEngine;

namespace RandomDistribution
{
    public class UnityRandomRangeGenerator : IRandomGenerator
    {
        private readonly float _min;
        private readonly float _max;


        public string Name => "UnityEngine.Random.Range()";


        public UnityRandomRangeGenerator(float min, float max)
        {
            _min = min;
            _max = max;
        }


        public float Generate() => Random.Range(_min, _max);
    }
}

UnityEngine.Random.value rakendamine

Staatilise klassi UnityEngine.Random vÀÀrtusomadus tagastab ujuktĂŒĂŒbi fikseeritud vÀÀrtusvahemikust [0; 1). Projekteerime selle antud vahemikku samamoodi nagu System.Random.NextDouble() rakendamisel.

using UnityEngine;

namespace RandomDistribution
{
    public class UnityRandomValueGenerator : IRandomGenerator
    {
        private readonly float _factor;
        private readonly float _min;


        public string Name => "UnityEngine.Random.value";


        public UnityRandomValueGenerator(float min, float max)
        {
            _factor = max - min;
            _min = min;
        }


        public float Generate() => (float)(Random.value * _factor) + _min;
    }
}

Unity.Mathematics.Random.NextFloat() rakendamine

Klassi Unity.Mathematics.Random meetod NextFloat() tagastab ujukoma tĂŒĂŒpi ujukoma ja vĂ”imaldab mÀÀrata vÀÀrtusvahemiku. Ainus nĂŒanss on see, et iga Unity.Mathematics.Random eksemplar tuleb lĂ€htestada mĂ”ne seemnega – nii vĂ€ldime korduvate jadade genereerimist.

using Unity.Mathematics;

namespace RandomDistribution
{
    public class UnityMathematicsRandomValueGenerator : IRandomGenerator
    {
        private Random _generator;
        private readonly float _min;
        private readonly float _max;


        public string Name => "Unity.Mathematics.Random.NextFloat()";


        public UnityMathematicsRandomValueGenerator(float min, float max)
        {
            _min = min;
            _max = max;
            _generator = new Random();
            _generator.InitState(unchecked((uint)System.DateTime.Now.Ticks));
        }


        public float Generate() => _generator.NextFloat(_min, _max);
    }
}

MainControlleri juurutamine

Valmis on mitu IRandomGeneratori rakendust. JÀrgmiseks peate genereerima jÀrjestused ja salvestama saadud andmekogumi töötlemiseks. Selleks loome Unitys stseeni ja vÀikese MainControlleri skripti, mis teeb Àra kogu vajaliku töö ja vastutab samal ajal kasutajaliidese interaktsiooni eest.

MÀÀrame andmestiku suuruse ja MF vÀÀrtuste vahemiku ning saame ka meetodi, mis tagastab konfigureeritud ja töövalmis generaatorite massiivi.

namespace RandomDistribution
{
    public class MainController : MonoBehaviour
    {
        private const int DefaultDatasetSize = 100000;

        public float MinValue = 0f;
        public float MaxValue = 100f;

        ...

        private IRandomGenerator[] CreateRandomGenerators()
        {
            return new IRandomGenerator[]
            {
                new SystemIntegerRandomGenerator(MinValue, MaxValue),
                new SystemDoubleRandomGenerator(MinValue, MaxValue),
                new UnityRandomRangeGenerator(MinValue, MaxValue),
                new UnityRandomValueGenerator(MinValue, MaxValue),
                new UnityMathematicsRandomValueGenerator(MinValue, MaxValue)
            };
        }

        ...
    }
}

NĂŒĂŒd loome andmestiku. Sel juhul kombineeritakse andmete genereerimine tulemuste salvestamisega tekstivoogu (csv-vormingus). Iga IRandomGeneratori vÀÀrtuste salvestamiseks eraldatakse oma eraldi veerg ja esimene rida sisaldab generaatori nime.

namespace RandomDistribution
{
    public class MainController : MonoBehaviour
    {
        ...
		
        private void GenerateCsvDataSet(TextWriter writer, int dataSetSize, params IRandomGenerator[] generators)
        {
            const char separator = ',';
            int lastIdx = generators.Length - 1;

            // write header
            for (int j = 0; j <= lastIdx; j++)
            {
                writer.Write(generators[j].Name);
                if (j != lastIdx)
                    writer.Write(separator);
            }
            writer.WriteLine();

            // write data
            for (int i = 0; i <= dataSetSize; i++)
            {
                for (int j = 0; j <= lastIdx; j++)
                {
                    writer.Write(generators[j].Generate());
                    if (j != lastIdx)
                        writer.Write(separator);
                }

                if (i != dataSetSize)
                    writer.WriteLine();
            }
        }

        ...
    }
}

JÀÀb ĂŒle vaid kutsuda meetod GenerateCsvDataSet ja salvestada tulemus faili vĂ”i edastada andmed kohe vĂ”rgu kaudu lĂ”ppseadmest vastuvĂ”tjasse. server.

namespace RandomDistribution
{
    public class MainController : MonoBehaviour
    {
        ...
		
        public void GenerateCsvDataSet(string path, int dataSetSize, params IRandomGenerator[] generators)
        {
            using (var writer = File.CreateText(path))
            {
                GenerateCsvDataSet(writer, dataSetSize, generators);
            }
        }


        public string GenerateCsvDataSet(int dataSetSize, params IRandomGenerator[] generators)
        {
            using (StringWriter writer = new StringWriter(CultureInfo.InvariantCulture))
            {
                GenerateCsvDataSet(writer, dataSetSize, generators);
                return writer.ToString();
            }
        }

        ...
    }
}

Projekti allikad on aadressil GitLab.

JĂ€reldused

Ime ei juhtunud. Nad ootasid seda, mida nad said – kĂ”igil juhtudel ĂŒhtlase jaotuse ilma vandenĂ”u vihjeta. Ma ei nĂ€e mĂ”tet platvormide jaoks eraldi graafikuid panna – need kĂ”ik nĂ€itavad ligikaudu samu tulemusi.

Tegelikkus on jÀrgmine:
BlessRNG vÔi RNG Ôigluse kontrollimine

Jadade visualiseerimine tasapinnal kÔigi viie genereerimismeetodi jÀrgi:
BlessRNG vÔi RNG Ôigluse kontrollimine

Ja visualiseerimine 3D-s. JĂ€tan ainult System.Random.Next() tulemuse, et mitte tekitada hunnikut identset sisu.
BlessRNG vÔi RNG Ôigluse kontrollimine

Sissejuhatuses jutustatud lugu UnityEngine’i normaaljaotusest. Random ei kordunud: kas see oli alguses vigane vĂ”i on vahepeal midagi mootoris muutunud. Aga nĂŒĂŒd oleme kindlad.

Allikas: www.habr.com

Ostke DDoS-kaitsega saitide jaoks usaldusvÀÀrne hostimine, VPS VDS-serverid đŸ”„ Osta usaldusvÀÀrne veebimajutus DDoS-kaitsega, VPS VDS serverid | ProHoster