BlessRNG an kontrolkirina RNG ji bo dadperweriyê

BlessRNG an kontrolkirina RNG ji bo dadperweriyê

Di pêşkeftina lîstikê de, hûn bi gelemperî hewce ne ku tiştek bi bêserûberiyê ve girêbidin: Yekbûn ji bo vê yekê Random-ya xwe heye, û paralelî wê jî System.Random heye. Carekê, li ser yek ji projeyan, min pê hesiya ku her du jî dikarin cûda bixebitin (her çend divê ew belavokek hevûdu hebin).

Dûv re ew neçûn nav hûrguliyan - bes bû ku derbasbûna System.Random hemî pirsgirêk rast kir. Naha me biryar da ku em bi hûrgulî lê binihêrin û lêkolînek piçûk bikin: RNG-yên "alîgir" an pêşbînbar çiqas in, û kîjan hilbijêrin. Digel vê yekê, min ji carekê zêdetir ramanên nakokî li ser "rastgotina" wan bihîstiye - em hewl bidin ku fêr bibin ka encamên rastîn bi yên hatine ragihandin re çawa têne berhev kirin.

Bernameya perwerdehiya kurt an RNG bi rastî RNG ye

Ger hûn jixwe bi hilberînerên hejmarên rasthatî nas in, wê hingê hûn dikarin tavilê biçin beşa "Testkirin".

Jimarên tesadufî (RN) rêzek jimaran in ku bi karanîna hin pêvajoyek rasthatî (kaotîk) têne çêkirin, çavkaniya entropiyê. Ango, ev rêzek e ku hêmanên wê bi tu zagonên matematîkî ve bi hev ve ne girêdayî ne - têkiliya wan a sedem-encamê tune.

Tiştê ku jimareya rasthatî diafirîne jê re çêkerê jimarên tesadufî (RNG) tê gotin. Wusa dixuye ku her tişt seretayî ye, lê heke em ji teoriyê berbi pratîkê ve biçin, wê hingê ne ew qas hêsan e ku meriv algorîtmayek nermalavê ji bo afirandina rêzek wusa bicîh bîne.

Sedem di nebûna heman kaosê de di elektronîkên xerîdar ên nûjen de ye. Bêyî wê, hejmarên rasthatî ji randombûnê namîne, û çêkerê wan vediguhere fonksiyonek asayî ya argumanên eşkere diyarkirî. Ji bo hejmarek pisporên di warê IT-ê de, ev pirsgirêkek ciddî ye (mînak, krîptografî), lê ji bo yên din çareseriyek bi tevahî pejirandî heye.

Pêdivî ye ku meriv algorîtmayek binivîsîne ku vegere, her çend ne bi rastî hejmarên rasthatî be jî, lê bi qasî ku mimkun nêzîkê wan be - bi navê hejmarên pseudo-random (PRN). Di vê rewşê de ji algorîtmayê re çêkerek hejmarên pseudorandom (PRNG) tê gotin.

Ji bo afirandina PRNG gelek vebijark hene, lê ya jêrîn dê ji bo her kesî têkildar be:

  1. Pêdiviya destpêkê ya pêşîn.

    PRNG xwedan çavkaniyek entropiyê nîne, ji ber vê yekê divê berî karanîna rewşek destpêkê were dayîn. Ew wekî jimarek (an vektor) tê destnîşan kirin û jê re tov (tovê rasthatî) tê gotin. Bi gelemperî, jimareya demjimêra pêvajoyê an jî hevbera hejmarî ya dema pergalê wekî tov tê bikar anîn.

  2. Reproducibility Sequence.

    PRNG bi tevahî diyarker e, ji ber vê yekê tovê ku di dema destpêkirinê de hatî destnîşan kirin yekane rêza paşerojê ya hejmaran diyar dike. Ev tê vê wateyê ku PRNG-ya cihê ku bi heman tovê hatî destpêkirin (di demên cûda de, di bernameyên cûda de, li ser cîhazên cihêreng) dê heman rêzê çêbike.

Her weha hûn hewce ne ku dabeşkirina îhtimalê ya ku PRNG diyar dike zanibin - ew ê kîjan jimaran çêbike û bi çi îhtîmalî. Pir caran ev an belavkirinek normal an jî dabeşek yekgirtî ye.
BlessRNG an kontrolkirina RNG ji bo dadperweriyê
Dabeşkirina normal (çep) û belavkirina yekgirtî (rast)

Dibêjin em bi 24 alîyan re bi edalet mirinek heye. Ger hûn wê bavêjin, îhtîmala bidestxistina yekî dê bibe 1/24 (eynî îhtîmala bidestxistina hejmareke din). Ger hûn gelek avêtinan bikin û encaman tomar bikin, hûn ê pêbihesin ku hemî kevroşk bi qasî heman frekansê dikevin. Di bingeh de, ev mirin dikare wekî RNG-ya bi belavkirinek yekgirtî were hesibandin.

Ger hûn 10 ji van diranan yekcar bavêjin û xalên giştî bijmêrin? Dê yekrengî ji bo wê were parastin? Na. Bi gelemperî, mîqdar dê nêzî 125 xalan be, ango, hin nirxek navîn. Û di encamê de, tewra berî avêtina avêtinê, hûn dikarin bi qasî encama pêşerojê texmîn bikin.

Sedem ev e ku ji bo bidestxistina pîvana navînî hejmara herî mezin a hevberdanê hene. Her ku ji wê dûrtir be, ew qas kêm hevberdan - û, li gorî vê yekê, îhtîmala windabûnê jî kêm dibe. Ger ev dane were xuyang kirin, ew ê bi rengekî nezelal dişibe şeklê zengilê. Ji ber vê yekê, digel hin dirêjkirinê, pergalek 10 dice dikare bi belavkirinek normal RNG were gotin.

Mînakek din, tenê vê carê li ser balafirê - gulebarana li armancekê. Firoker dê bibe RNG ku cotek jimareyan (x, y) ku li ser grafîkê têne xuyang kirin çêdike.
BlessRNG an kontrolkirina RNG ji bo dadperweriyê
Bipejirînin ku vebijarka li milê çepê nêzîkê jiyana rast e - ev RNG bi belavkirinek normal e. Lê heke hûn hewce ne ku stêran li ezmanek tarî belav bikin, wê hingê vebijarka rast, ku bi karanîna RNG-ê bi belavkirinek yekgirtî ve hatî wergirtin, çêtir e. Bi gelemperî, li gorî peywira di dest de jeneratorek hilbijêrin.

Naha em li ser entropiya rêza PNG biaxivin. Mînakî, rêzek heye ku bi vî rengî dest pê dike:

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

Di nihêrîna pêşîn de ev hejmar çiqas tesadufî ne? Ka em bi kontrolkirina belavkirinê dest pê bikin.
BlessRNG an kontrolkirina RNG ji bo dadperweriyê
Ew nêzîkî yekreng xuya dike, lê heke hûn rêzek du hejmaran bixwînin û wan wekî hevrêzên li ser balafirê şîrove bikin, hûn vê yekê digirin:
BlessRNG an kontrolkirina RNG ji bo dadperweriyê
Nimûne bi zelalî xuya dibin. Û ji ber ku daneyên di rêzê de bi rengek diyarkirî têne rêz kirin (ango, entropiya wê kêm e), ev dikare bibe sedema wê pir "bias". Bi kêmanî, PRNGek wusa ji bo çêkirina koordînatên li ser balafirê ne pir maqûl e.

Rêzek din:

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

Li vir her tişt di balafirê de jî baş xuya dike:
BlessRNG an kontrolkirina RNG ji bo dadperweriyê
Ka em li cildê binêrin (her car sê jimaran bixwînin):
BlessRNG an kontrolkirina RNG ji bo dadperweriyê
Û dîsa nimûne. Êdî ne mimkûn e ku dîmenek di çar alîyan de were çêkirin. Lê şablon dikarin li ser vê pîvanê û li ser yên mezintir hebin.

Di krîptografî de, ku hewcedariyên herî hişk li ser PRNG-an têne ferz kirin, rewşek weha bi rengekî bête qebûl kirin. Ji ber vê yekê, algorîtmayên taybetî hatine pêşve xistin da ku kalîteya wan binirxînin, ku em ê nuha dest nedin wan. Mijar berfireh e û hêjayî gotareke cuda ye.

Îmtîhanê

Ger em tiştek bi guman nizanin, wê hingê meriv çawa pê re bixebite? Ma hêja ye ku hûn rêyê derbas bikin ger hûn nizanin kîjan ronahiya trafîkê destûrê dide? Encam dikarin cuda bin.

Heman tişt ji bo rasthatiniya navdar a di Yekîtiyê de jî derbas dibe. Baş e heke belge hûrguliyên pêwîst eşkere bike, lê çîroka ku di destpêka gotarê de hatî behs kirin tam ji ber nebûna taybetmendiyên xwestî qewimî.

Û heke hûn nizanin amûr çawa dixebite, hûn ê nikaribin wê rast bikar bînin. Bi gelemperî, dem hatiye ku em ceribandinek kontrol bikin û bikin da ku di dawiyê de bi kêmanî di derbarê belavkirinê de piştrast bibin.

Çareserî sade û bi bandor bû - berhevkirina statîstîkan, bidestxistina daneyên objektîf û li encaman binihêre.

Mijara lêkolînê

Gelek awayên afirandina hejmarên bêserûber di Unity de hene - me pênc ceribandin.

  1. System.Random.Next(). Di nav rêzek diyarkirî ya nirxan de hêjmaran çêdike.
  2. System.Random.NextDouble(). Di navberê de ji [0; 1).
  3. UnityEngine.Random.Range(). Di nav rêzek nirxê diyarkirî de jimareyên yekane yên rastîn çêdike.
  4. UnityEngine.Random.value. Di nav rêza ji [0; 1).
  5. Unity.Mathematics.Random.NextFloat(). Beşek ji pirtûkxaneya Unity.Mathematics ya nû. Di nav rêzek nirxê diyarkirî de jimareyên yekane yên rastîn çêdike.

Hema hema li her derê di belgeyê de belavokek yekgirtî hate diyar kirin, ji bilî UnityEngine.Random.value (ku belavbûn nehat diyar kirin, lê bi analogî bi UnityEngine.Random.Range() yekreng jî dihat hêvîkirin) û Unity.Mathematics.Random .NextFloat() (ku di Bingehê de algorîtmaya xorshift e, ku tê vê wateyê ku hûn dîsa hewce ne ku li benda belavkirinek yekgirtî bisekinin).

Bi xwerû, encamên hêvîkirî wekî yên ku di belgeyê de hatine destnîşan kirin hatine girtin.

Methodology

Me serîlêdanek piçûk nivîsand ku rêzikên hejmarên rasthatî bi karanîna her rêgezên pêşkêşkirî diafirîne û encam ji bo pêvajoyek din tomar dike.

Dirêjahiya her rêzekê 100 jimar e.
Rêjeya hejmarên rasthatî [0, 100) ye.

Daneyên ji çend platformên armanc hatine berhev kirin:

  • Windows
    - Unity v2018.3.14f1, Moda Edîtor, Mono, .NET Standard 2.0
  • MacOS
    - Unity v2018.3.14f1, Moda Edîtor, Mono, .NET Standard 2.0
    - Unity v5.6.4p4, Moda Edîtor, Mono, .NET Standard 2.0
  • Android
    - Yekbûn v2018.3.14f1, avakirina per amûrekê, Mono, .NET Standard 2.0
  • iOS
    - Yekbûn v2018.3.14f1, avakirina per amûrekê, il2cpp, .NET Standard 2.0

Реализация

Gelek awayên me yên cûda hene ku em hejmarên bêserûber çêbikin. Ji bo her yek ji wan, em ê çînek pêçanê ya cihê binivîsin, ku divê peyda bike:

  1. Derfeta danîna rêza nirxan [min / max). Dê bi rêya çêker were danîn.
  2. Rêbaza vegerandina MF. Ka em float wekî celeb hilbijêrin, ji ber ku ew gelemperîtir e.
  3. Navê rêbaza nifşê ji bo nîşankirina encaman. Ji bo rehetiyê, em ê wekî nirx navê tevahî polê + navê rêbaza ku ji bo hilberîna MF-ê hatî bikar anîn vegerînin.

Pêşîn, bila em abstraksek ku dê ji hêla navrûya IRandomGenerator ve were temsîl kirin ragihînin:

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

        float Generate();
    }
}

Pêkanîna System.Random.Next()

Ev rêbaz dihêle hûn rêzek nirxan saz bikin, lê ew jimaran vedigerîne, lê float hewce ne. Hûn dikarin bi tenê jimareyek wekî float şirove bikin, an jî hûn dikarin rêza nirxan bi çend rêzikên mezinahiyê berfireh bikin, bi her nifşek navînî re berdêl bidin. Encam dê bibe tiştek mîna xalek sabît bi rêzek rastbûnê. Em ê vê vebijarkê bikar bînin ji ber ku ew nêzî nirxa float ya rastîn e.

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;
    }
}

Pêkanîna System.Random.NextDouble()

Li vir rêza sabît a nirxan [0; 1). Ji bo projekirina wê li ser ya ku di çêkerê de hatî destnîşan kirin de, em jimareya hêsan bikar tînin: 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;
    }
}

Pêkanîna UnityEngine.Random.Range()

Ev rêbaza çîna statîk UnityEngine.Random dihêle hûn rêzek nirxan saz bikin û celebek float vedigerîne. Hûn ne hewce ne ku hûn veguherînên zêde bikin.

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);
    }
}

Pêkanîna UnityEngine.Random.value

Taybetmendiya nirxê ya pola statîk UnityEngine.Random ji rêzek nirxan a sabît celebek float vedigerîne [0; 1). Werin em wê bi heman awayê ku dema bicîhkirina System.Random.NextDouble().

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;
    }
}

Pêkanîna Unity.Mathematics.Random.NextFloat()

Rêbaza NextFloat() ya çîna Unity.Mathematics.Random xaleke herikbar a cureya float vedigerîne û dihêle hûn rêzek nirxan diyar bikin. Tiştek tenê ev e ku her mînakek Unity.Mathematics.Random dê bi hin tov ve were destpêkirin - bi vî rengî em ê ji çêkirina rêzikên dubare dûr bikevin.

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);
    }
}

Pêkanîna MainController

Gelek pêkanînên IRandomGenerator amade ne. Dûv re, hûn hewce ne ku rêzikan biafirînin û daneyên encam ji bo pêvajoyê hilînin. Ji bo vê yekê, em ê di Unity de dîmenek û skrîptek MainController a piçûk biafirînin, ku dê hemî karên pêwîst bike û di heman demê de berpirsiyarê danûstendina bi UI-yê re be.

Werin em mezinahiya databasê û rêza nirxên MF-ê destnîşan bikin, û di heman demê de rêbazek ku komek jeneratorên mîhengkirî û ji xebatê re amade ne vedigerîne bistînin.

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)
            };
        }

        ...
    }
}

Naha em danegehek çêbikin. Di vê rewşê de, hilberîna daneyê dê bi tomarkirina encaman di navgînek nivîsê de (bi formata csv) re were hev kirin. Ji bo hilanîna nirxên her IRandomGenerator, stûna wê ya cihêreng tê veqetandin, û rêza yekem Navê jeneratorê dihewîne.

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();
            }
        }

        ...
    }
}

Tiştê ku dimîne ev e ku hûn bangî rêbaza GenerateCsvDataSet bikin û encamê li pelek hilînin, an tavilê daneyê li ser torê ji cîhaza dawî veguhezînin servera wergirtinê.

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();
            }
        }

        ...
    }
}

Çavkaniyên projeyê li GitLab.

Encam

Tu mûcîze çênebû. Tiştê ku wan hêvî dikir ew e ya ku wan bi dest xist - di hemî rewşan de, belavkirinek yekta bêyî nîşanek komployan. Ez xala danîna grafikên cihêreng ji bo platforman nabînim - ew hemî hema hema heman encaman destnîşan dikin.

Rastî ev e:
BlessRNG an kontrolkirina RNG ji bo dadperweriyê

Dîtina rêzikên li ser balafirê ji her pênc rêbazên nifşê:
BlessRNG an kontrolkirina RNG ji bo dadperweriyê

Û dîtbarî di 3D de. Ez ê tenê encama System.Random.Next() bihêlim da ku komek naverokek wekhev çênekim.
BlessRNG an kontrolkirina RNG ji bo dadperweriyê

Çîroka ku di pêşgotinê de li ser belavkirina normal ya UnityEngine hate gotin.Random xwe dubare nekir: an ew di destpêkê de xelet bû, an jî ji hingê ve tiştek di motorê de guherî. Lê niha em piştrast in.

Source: www.habr.com

Add a comment