BlessRNG oder kontrolléieren der RNG fir Éierlechkeet

BlessRNG oder kontrolléieren der RNG fir Éierlechkeet

An der Spillentwécklung musst Dir dacks eppes zoufälleg bannen: Unity huet säin eegene Random fir dëst, a System.Random existéiert parallel domat. Eemol bei engem vun de Projete war d'Impressioun, datt déi zwee anescht kënne schaffen (obwuel se eng eenheetlech Verdeelung sollten hunn).

Dunn sinn se net an Detailer ginn - et war genuch, datt den Iwwergank zu System.Random all Problemer fix. Elo hu mir beschloss méi no ze kucken an eng kleng Etude ze maachen: wéi "biaséiert" oder prévisibel sinn RNGs, a wéi eng ze wielen. Ausserdeem hunn ech méi wéi eemol konfliktend Meenungen iwwer hir "Éierlechkeet" héieren - loosst eis probéieren erauszefannen, wéi d'real Resultater mat den deklaréierten vergläichen.

Kuerz Erzéiungsprogramm oder RNG ass tatsächlech RNG

Wann Dir scho mat zoufälleg Zuelengeneratoren vertraut sidd, kënnt Dir op d'Testsektioun sprangen.

Zoufälleg Zuelen (SN) sinn eng Rei vun Zuelen generéiert duerch e puer zoufälleg (chaotesche) Prozess, eng Quell vun Entropie. Dat ass, et ass sou eng Sequenz, d'Elementer vun deenen net mat engem mathematesche Gesetz matenee verbonne sinn - si hunn keng kausal Relatioun.

D'Saach, déi den SC erstellt, gëtt den Random Number Generator (RNG) genannt. Et géif schéngen datt alles elementar ass, awer wa mir vun der Theorie op d'Praxis réckelen, dann ass et tatsächlech net sou einfach fir e Software Algorithmus fir sou eng Sequenz ze generéieren.

De Grond läit an der Verontreiung vun deemselwechte Chaos an der moderner Konsumentelektronik. Ouni et, zoufälleg Zuelen ophalen zoufälleg ze sinn, an hire Generator gëtt an eng normal Funktioun vun offensichtlech definéiert Argumenter. Fir eng Rei vu Spezialitéiten am IT Beräich ass dëst e seriöse Problem (zum Beispill, cryptography), mä fir anerer ass et eng komplett akzeptabel Léisung.

Et ass néideg en Algorithmus ze schreiwen, deen zréckkommen, wann net wierklech zoufälleg Zuelen, awer sou no wéi méiglech un hinnen - déi sougenannte pseudo-zoufälleg Zuelen (PRN). Den Algorithmus an dësem Fall gëtt e pseudo-zoufälleg Zuelengenerator (PRNG) genannt.

Et gi verschidde Méiglechkeeten fir e PRNG ze kreéieren, awer déi folgend wäerte fir jiddereen relevant sinn:

  1. De Besoin fir Pre-Initialiséierung.

    De PRNG ass ouni Quell vun Entropie, dofir, ier Dir se benotzt, ass et néideg den initialen Zoustand ze spezifizéieren. Et gëtt als Zuel (oder e Vektor) uginn a gëtt e Som genannt (Saat, zoufälleg Som). Dacks gëtt de Prozessor Zyklus Konter oder den numeresche Äquivalent vun der Systemzäit als Som benotzt.

  2. Sequenz reproducibility.

    De PRNG ass komplett deterministesch, sou datt d'Somen, déi während der Initialiséierung spezifizéiert sinn, eenzegaarteg déi ganz zukünfteg Sequenz vun Zuelen bestëmmt. Dëst bedeit datt eng eenzeg PRNG initialiséiert mat deemselwechte Som (zu verschiddenen Zäiten, a verschiddene Programmer, op verschiddenen Apparater) déiselwecht Sequenz generéiert.

Dir musst och d'Wahrscheinlechkeetsverdeelung wëssen, déi de PRNG charakteriséiert - wéi eng Zuelen et generéiert a mat wéi enger Wahrscheinlechkeet. Meeschtens ass dëst entweder eng normal Verdeelung oder eng eenheetlech Verdeelung.
BlessRNG oder kontrolléieren der RNG fir Éierlechkeet
Normalverdeelung (lénks) an eenheetlech Verdeelung (riets)

Loosst d'soen mir hunn eng fair Wierfel mat 24 Gesiichter. Wann et gehäit gëtt, da wäert d'Wahrscheinlechkeet vun enger Eenheet erausfalen gläich sinn 1/24 (wéi och d'Wahrscheinlechkeet fir aus all aner Zuel ze falen). Wann Dir vill Wäitschoss maacht an d'Resultater opschreift, mierkt Dir datt all d'Gesiichter ongeféier an der selwechter Frequenz falen. Tatsächlech kann dëst Wierfel als RNG mat enger eenheetlecher Verdeelung ugesi ginn.

A wann Dir gläichzäiteg 10 esou Wierfel geheien an de Gesamtbetrag vu Punkten zielen? Bleift et eenheetlech? Nee. Am meeschten, wäert de Montant no bei 125 Punkten ginn, dat ass, fir e puer Moyenne Wäert. An als Resultat, och ier Dir e Schlag mécht, kënnt Dir d'Zukunft Resultat ongeféier schätzen.

De Grond ass datt et déi héchst Zuel vu Kombinatioune gëtt fir den duerchschnëttleche Score ze kréien. Wat méi wäit dovun ass, wat manner Kombinatiounen - an deementspriechend manner d'Wahrscheinlechkeet fir erauszefalen. Wann dës Donnéeën visualiséiert ginn, wäerte se vague wéi d'Form vun enger Klack gleewen. Dofir, mat e puer Streck, kann e System vun 10 Schanken e RNG mat enger normaler Verdeelung genannt ginn.

En anert Beispill, nëmmen am Fliger, ass op en Zil ze schéissen. De Shooter wäert e RNG sinn deen e Paar Zuelen generéiert (x, y), déi op der Grafik ugewise gëtt.
BlessRNG oder kontrolléieren der RNG fir Éierlechkeet
Averstanen datt d'Optioun op der lénkser Säit méi no beim richtege Liewen ass - dëst ass en RNG mat enger normaler Verdeelung. Awer wann Dir d'Stären an engem donkelen Himmel verstreet musst, dann ass déi richteg Optioun, kritt mat engem RNG mat enger eenheetlecher Verdeelung, besser. Am Allgemengen, wielt e Generator jee no der Aufgab op der Hand.

Loosst eis elo iwwer d'Entropie vun der PSN Sequenz schwätzen. Zum Beispill gëtt et eng Sequenz déi esou ufänkt:

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

Wéi zoufälleg sinn dës Zuelen op den éischte Bléck? Loosst eis ufänken mat der Verdeelung ze kontrolléieren.
BlessRNG oder kontrolléieren der RNG fir Éierlechkeet
Et gesäit aus wéi wann et no un Uniform ass, awer wann Dir eng Sequenz vun zwou Zuelen liest an se als Koordinaten op engem Fliger interpretéiert, kritt Dir dëst:
BlessRNG oder kontrolléieren der RNG fir Éierlechkeet
Mustere ginn kloer sichtbar. A well d'Daten an der Sequenz op eng gewësse Manéier bestallt sinn (dat ass, si hunn eng niddereg Entropie), da kann dat zu der ganz "Bias" entstoen. Op e Minimum ass sou e PRNG net ganz gëeegent fir Koordinaten op engem Fliger ze generéieren.

Eng aner Sequenz:

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

Hei schéngt alles gutt ze sinn och um Fliger:
BlessRNG oder kontrolléieren der RNG fir Éierlechkeet
Loosst eis am Volume kucken (mir liesen dräi Zuelen gläichzäiteg):
BlessRNG oder kontrolléieren der RNG fir Éierlechkeet
An erëm Musteren. Et wäert net méi méiglech sinn eng Visualiséierung a véier Dimensiounen ze bauen. Awer Mustere kënne souwuel an dëser Dimensioun wéi a méi grousser existéieren.

An der selwechter Kryptografie, wou déi strengste Viraussetzungen op PRNG opgesat ginn, ass sou eng Situatioun kategoresch inakzeptabel. Dofir, fir hir Qualitéit ze bewäerten, goufen speziell Algorithmen entwéckelt, déi mir elo net beréieren. D'Thema ass extensiv an zitt op engem separaten Artikel.

Testen

Wa mir eppes net sécher wësse, wéi geet et dann ëm? Ass et derwäert d'Strooss ze Kräiz wann Dir net wësst wat de Verkéier Signal et erlaabt? D'Konsequenze kënnen ënnerschiddlech sinn.

Datselwecht gëlt fir déi notoresch Zoufällegkeet an der Unity. Et ass gutt wann d'Dokumentatioun déi néideg Detailer weist, awer d'Geschicht, déi am Ufank vum Artikel erwähnt ass, ass geschitt wéinst dem Mangel un de gewënschte Spezifizitéiten.

An ouni ze wëssen wéi d'Instrument funktionnéiert, kënnt Dir et net richteg applizéieren. Am Allgemengen ass et Zäit fir en Experiment ze kontrolléieren an ze maachen fir endlech op d'mannst op Käschte vun der Verdeelung sécherzestellen.

D'Léisung war einfach an effektiv - Statistiken sammelen, objektiv Donnéeën erhalen an d'Resultater kucken.

Sujet vun der Studie

Et gi verschidde Weeër fir zoufälleg Zuelen an Unity ze generéieren - mir hunn fënnef getest.

  1. System.Random.Next(). Generéiert ganz Zuelen (ganz Zuel) an der bestëmmter Gamme vu Wäerter.
  2. System.Random.NextDouble(). Generéiert duebel Präzisioun Zuelen (duebel) am Beräich vun [0; 1).
  3. UnityEngine.Random.Range(). Generéiert eenzeg Präzisioun (float) Zuelen an der bestëmmter Palette vu Wäerter.
  4. UnityEngine.Random.value. Generéiert eenzel Präzisiounszuelen (schwëmmt) am Beräich vun [0; 1).
  5. Unity.Mathematics.Random.NextFloat(). Deel vun der neier Unity.Mathematics Library. Generéiert eenzeg Präzisioun (float) Zuelen an der bestëmmter Palette vu Wäerter.

Bal iwwerall an der Dokumentatioun gouf eng eenheetlech Verdeelung präziséiert, mat Ausnam vun UnityEngine.Random.value (wou d'Verdeelung net spezifizéiert ass, mä duerch Analogie mat UnityEngine.Random.Range() war et och erwaart datt se eenheetlech wier) an Unity .Mathematics.Random.NextFloat () (wou an baséiert op der xorshift Algorithmus, dat heescht, datt erëm muss Dir fir eng eenheetlech Verdeelung wait).

Par défaut waren déi erwaart Resultater déi an der Dokumentatioun spezifizéiert.

Technik

Mir hunn eng kleng Applikatioun geschriwwen, déi Sequenzen vun zoufälleg Zuelen op jiddereng vun de präsentéierte Weeër generéiert huet an d'Resultater fir weider Veraarbechtung gespäichert huet.

D'Längt vun all Sequenz ass 100 Zuelen.
D'Gamme vun zoufälleg Zuelen ass [0, 100).

Date goufe vu verschiddenen Zilplattformen gesammelt:

  • Windows
    - Unity v2018.3.14f1, Editor Modus, Mono, .NET Standard 2.0
  • macOS
    - Unity v2018.3.14f1, Editor Modus, Mono, .NET Standard 2.0
    — Unity v5.6.4p4, Editor Modus, Mono, .NET Standard 2.0
  • Android
    - Unity v2018.3.14f1, Build Per Device, Mono, .NET Standard 2.0
  • iOS
    - Unity v2018.3.14f1, Build Per Device, il2cpp, .NET Standard 2.0

Ëmsetzung

Mir hunn e puer verschidde Weeër fir zoufälleg Zuelen ze generéieren. Fir jidderee vun hinnen schreiwen mir eng separat Wrapper Klass déi soll ubidden:

  1. Méiglechkeet fir d'Gamme vu Wäerter [min / max] ze setzen. Gëtt iwwer de Konstruktor gesat.
  2. D'Method déi de MF zréckkënnt. Als Typ wäerte mir Float wielen, als méi allgemeng.
  3. Den Numm vun der Generatiounsmethod fir d'Resultater ze markéieren. Fir d'Bequemlechkeet wäerte mir de ganzen Numm vun der Klass zréckginn + den Numm vun der Method déi benotzt gëtt fir de Midrange als Wäert ze generéieren.

Als éischt, loosst eis eng Abstraktioun deklaréieren déi duerch d'IRandomGenerator Interface vertruede gëtt:

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

        float Generate();
    }
}

System.Random.Next () Ëmsetzung

Dës Method erlaabt Iech eng Rei vu Wäerter ze setzen, awer et gëtt ganz Zuelen (ganz Zuel) zréck, a Schwammen sinn néideg. Dir kënnt einfach eng ganz Zuel als Schwämm interpretéieren, oder Dir kënnt d'Gamme vu Wäerter duerch e puer Uerderen ausbauen, se mat all Generatioun vum Midrange kompenséieren. Et wäert eppes wéi e fixe Punkt mat enger bestëmmter Uerdnung vun der Genauegkeet erausstellen. Mir wäerten dës Optioun benotzen, well et méi no un de reelle Floatwäert ass.

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

System.Random.NextDouble () Ëmsetzung

Hei ass de fixe Beräich vu Wäerter [0; 1). Fir et op deen am Konstruktor ze projizéieren, benotze mir einfach Arithmetik: 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;
    }
}

Ëmsetzung vun UnityEngine.Random.Range()

Dës Method vun der UnityEngine.Random statesch Klass erlaabt Iech eng Rei vu Wäerter ze setzen an e Float-Typ zréck. Dir musst keng zousätzlech Transformatiounen maachen.

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 Ëmsetzung

D'Wäerteigenschafte vun der UnityEngine.Random statesch Klass gëtt e Float-Typ Midrange vun engem fixen Wäertberäich [0; 1). Loosst d'Projet et op déi bestëmmte Beräich an déi selwecht Art a Weis wéi wann Dir 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;
    }
}

Unity.Mathematics.Random.NextFloat () Ëmsetzung

D'NextFloat () Method vun der Unity.Mathematics.Random Klass gëtt eng Schwemmpunktzuel zréck an erlaabt Iech eng Rei vu Wäerter ze setzen. Déi eenzeg Nuance ass datt all Instanz vun Unity.Mathematics.Random mat e puer Som initialiséiert musse ginn - sou wäerte mir vermeiden datt se widderhuelend Sequenzen generéieren.

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

Ëmsetzung vun MainController

Verschidde Implementatioune vum IRandomGenerator si prett. Als nächst musst Dir Sequenzen generéieren an déi resultéierend Dataset fir d'Veraarbechtung späicheren. Fir dëst ze maachen, loosst eis eng Szen an Unity erstellen an e klenge MainController Skript deen all déi néideg Aarbecht mécht a verantwortlech ass fir mat der UI laanscht de Wee ze interagéieren.

Loosst eis d'Gréisst vum Dataset an d'Gamme vu Midrange Wäerter setzen, a kréien och eng Method déi eng Rei vu konfiguréierten a prett-ze-schaffen Generatoren zréckginn.

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

        ...
    }
}

Loosst eis elo en Dataset erstellen. An dësem Fall gëtt d'Dategeneratioun kombinéiert mat der Opnam vun de Resultater an engem Textstream (am Csv-Format). All IRandomGenerator huet seng eege Kolonn fir d'Wäerter ze späicheren, an déi éischt Zeil enthält den Numm vum Generator.

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

        ...
    }
}

Alles wat bleift ass d'GenerateCsvDataSet Method ze ruffen an d'Resultat op eng Datei ze späicheren, oder direkt d'Donnéeën iwwer d'Netz vum Endapparat op den Empfangsserver transferéieren.

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

        ...
    }
}

De Projet Quelle sinn op GitLab.

Resultater

D'Wonner ass net geschitt. Wat se erwaart hunn, kruten se - an alle Fäll eng gläichméisseg Verdeelung ouni Hiweis vu Verschwörungen. Ech gesinn net de Punkt fir separat Charts fir Plattformen z'applizéieren - si weisen all ongeféier déiselwecht Resultater.

D'Realitéit ass dës:
BlessRNG oder kontrolléieren der RNG fir Éierlechkeet

Visualiséierung vu Sequenzen um Fliger vun alle fënnef Generatiounsmethoden:
BlessRNG oder kontrolléieren der RNG fir Éierlechkeet

A Visualiséierung an 3D. Ech loossen nëmmen d'Resultat vum System.Random.Next (), fir net eng Rëtsch vum selwechten Inhalt ze produzéieren.
BlessRNG oder kontrolléieren der RNG fir Éierlechkeet

D'Geschicht erzielt an der Aféierung iwwer d'Normalverdeelung vun UnityEngine.Zoufälleg gouf net widderholl: entweder et war ursprénglech falsch, oder eppes huet sech am Motor zënterhier geännert. Awer elo si mir sécher.

Source: will.com

Setzt e Commentaire