BlessRNG немесе RNG әділдігін тексеру

BlessRNG немесе RNG әділдігін тексеру

Ойын әзірлеуде жиі кездейсоқтыққа сену керек: Бірліктің бұл үшін өз Кездейсоқ мүмкіндігі бар және онымен қатар System.Random бар. Ұзақ уақыт бұрын, бір жобада мен екеуі басқаша жұмыс істеуі мүмкін деген әсер алдым (бірақ олардың біркелкі таралуы керек).

Ол кезде біз егжей-тегжейлі қарастырған жоқпыз — System.Random жүйесіне ауысу барлық мәселелерді шешкені жеткілікті еді. Енді біз егжей-тегжейлі қарастырып, шағын зерттеу жүргізуді ұйғардық: RNGs қаншалықты «біржақты» немесе болжамды және қайсысын таңдау керек. Оның үстіне, мен олардың «әділдігі» туралы қарама-қайшы пікірлерді бірнеше рет естідім — нақты нәтижелер шағымдармен қалай салыстырылатынын анықтауға тырысайық.

RNG-ге қысқаша кіріспе немесе бұл шынымен RNG ме?

Кездейсоқ сандар генераторларымен бұрыннан таныс болсаңыз, «Тестілеу» бөліміне өтуіңізге болады.

Кездейсоқ сандар (RN) - бұл қандай да бір кездейсоқ (хаотикалық) процесс нәтижесінде пайда болған сандар тізбегі, энтропия көзі. Яғни, бұл элементтер бір-бірімен ешбір математикалық заң бойынша байланыспаған тізбек — олардың себеп-салдар байланысы жоқ.

Кездейсоқ сандар генераторын (RNG) жасайтын нәрсе кездейсоқ сандар генераторы (RNG) деп аталады. Бұл қарапайым болып көрінгенімен, теорияны практикада қолдану мұндай жүйелілікті құру үшін бағдарламалық қамтамасыз ету алгоритмін енгізу соншалықты қарапайым емес екенін көрсетеді.

Мұның себебі қазіргі заманғы тұрмыстық электроникада мұндай кездейсоқтықтың болмауында. Онсыз кездейсоқ сандар кездейсоқ болуды тоқтатады және олардың генераторы алдын ала анықталған кірістердің қарапайым функциясына айналады. Бірқатар IT мамандықтары үшін (мысалы, криптография) бұл күрделі мәселе, бірақ басқалары үшін өте қолайлы шешім бар.

Біз шын мәнінде кездейсоқ емес, бірақ оларға барынша жақын сандарды, яғни жалған кездейсоқ сандар (PRNGs) деп аталатын сандарды қайтаратын алгоритмді жазуымыз керек. Бұл жағдайда алгоритм жалған кездейсоқ сандар генераторы (PRNG) деп аталады.

PRNG құрудың бірнеше нұсқасы бар, бірақ төмендегілер барлығына қатысты болады:

  1. Алдын ала инициализация қажет.

    PRNG-де энтропия көзі жоқ, сондықтан пайдалану алдында бастапқы күйді көрсету керек. Бұл бастапқы күй сан (немесе вектор) ретінде көрсетіледі және тұқым (кездейсоқ тұқым) деп аталады. Процессор циклінің есептегіші немесе жүйе уақытының сандық эквиваленті жиі тұқым ретінде пайдаланылады.

  2. Тізбектің қайталануы.

    PRNG толығымен детерминирленген, сондықтан инициализация кезінде көрсетілген тұқым барлық болашақ сандар тізбегін бірегей түрде анықтайды. Бұл бір тұқыммен (әртүрлі уақытта, әртүрлі бағдарламаларда, әртүрлі құрылғыларда) инициализацияланған жалғыз PRNG бірдей реттілік жасайтынын білдіреді.

Сіз сондай-ақ PRNG-ді сипаттайтын ықтималдық үлестірімін білуіңіз керек - ол қандай сандарды және қандай ықтималдықпен жасайды. Көбінесе бұл қалыпты таралу немесе біркелкі таралу.
BlessRNG немесе RNG әділдігін тексеру
Қалыпты таралу (сол жақта) және біркелкі таралу (оң жақта)

Бізде әділ 24 қырлы өлі бар делік. Егер біз оны айналдырсақ, 1-ге қону ықтималдығы 1/24 (кез келген басқа нөмірге қону ықтималдығы сияқты). Егер біз көптеген орамдарды жасап, нәтижелерді жазсақ, біз барлық 24 жақты сүйектердің шамамен бірдей жиілікпен түсетінін байқаймыз. Негізінде бұл өлшені біркелкі бөлінген кездейсоқ сандар генераторы деп санауға болады.

Осындай 10 сүйекті бірден лақтырып, жалпы ұпайды есептесеңіз ше? Ол біркелкі болып қала ма? Жоқ. Көбінесе ұпай 125 ұпайға жақын болады, яғни кейбір орташа мән. Нәтижесінде, сіз айналдырудан бұрын болашақ нәтижені шамамен бағалай аласыз.

Себебі, орташа ұпайға жету үшін комбинациялардың ең көп саны бар. Орташа мәннен неғұрлым алыс болса, соғұрлым комбинациялар аз болады және, демек, орамның ықтималдығы соғұрлым төмен болады. Егер бұл деректер визуалды түрде көрсетілсе, олар қоңырау пішініне түсініксіз түрде ұқсайды. Сондықтан, біраз созылғанда, 10 сүйектен тұратын жүйені қалыпты таралған RNG деп атауға болады.

Тағы бір мысал, бұл жолы ұшақта нысанаға ату. Түсіруші – кездейсоқ сандар генераторы (RNG), ол графикте көрсетілетін сандар жұбын (x, y) жасайды.
BlessRNG немесе RNG әділдігін тексеру
Сіз сол жақтағы нұсқа нақты өмірге жақынырақ екеніне келісесіз - бұл қалыпты таралумен кездейсоқ сандар генераторы. Бірақ егер жұлдыздарды қараңғы аспанға шашырату қажет болса, біркелкі бөлінген кездейсоқ сандар генераторын пайдаланып алынған оң жақ нұсқа қолайлырақ болар еді. Бір сөзбен айтқанда, берілген тапсырма негізінде генераторды таңдаңыз.

Енді кездейсоқ сандар тізбегінің энтропиясына тоқталайық. Мысалы, келесідей басталатын реттілік бар:

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

Бұл сандар бір қарағанда қаншалықты кездейсоқ? Бөлуді тексеруден бастайық.
BlessRNG немесе RNG әділдігін тексеру
Бұл біркелкі болып көрінеді, бірақ егер сіз екі санның тізбегін оқып, оларды жазықтықтағы координаталар ретінде түсінсеңіз, мынаны аласыз:
BlessRNG немесе RNG әділдігін тексеру
Үлгілер анық көрінеді. Ал реттіліктегі деректер белгілі бір жолмен реттелгендіктен (яғни, төмен энтропияға ие), бұл бірдей «қиғаштықты» тудыруы мүмкін. Кем дегенде, мұндай PRNG жазықтықта координаттарды құру үшін өте қолайлы емес.

Басқа реттілік:

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

Мұнда бәрі жақсы сияқты, тіпті тегіс жерде де:
BlessRNG немесе RNG әділдігін тексеру
Көлемді қарастырайық (үш санды оқыңыз):
BlessRNG немесе RNG әділдігін тексеру
Және тағы да үлгілер. Төрт өлшемде бейнелеу енді мүмкін емес. Бірақ үлгілер осы өлшемде және жоғарырақ өлшемде болуы мүмкін.

PRNG-ге ең қатаң талаптар қойылған криптографияда мұндай жағдайға мүлдем жол берілмейді. Сондықтан олардың сапасын бағалау үшін арнайы алгоритмдер әзірленді, біз бұл жерде оларды талқыламаймыз. Тақырып ауқымды және бөлек мақаланы қажет етеді.

Тестілеу

Егер біз бір нәрсені нақты білмесек, онымен қалай күресуге болады? Қай бағдаршам рұқсат беретінін білмесек, жолды кесіп өтуіміз керек пе? Салдары әртүрлі болуы мүмкін.

Бірліктегі атышулы кездейсоқтық туралы да солай. Құжаттама қажетті мәліметтерді бергенде өте жақсы, бірақ мақаланың басында айтылған оқиға дәл қалаған ерекшеліктің болмауынан туындады.

Ал құралдың қалай жұмыс істейтінін білмей, оны дұрыс пайдалана алмайсыз. Сонымен, оны сынап, кем дегенде таратуға қатысты сенімді болу үшін эксперимент жүргізудің уақыты келді.

Шешім қарапайым және тиімді болды: статистиканы жинау, объективті деректерді алу және нәтижелерді қарау.

Зерттеу пәні

Unity бағдарламасында кездейсоқ сандарды жасаудың бірнеше жолы бар — біз бесеуін тексердік.

  1. System.Random.Next(). Берілген мәндер ауқымында бүтін сандарды жасайды.
  2. System.Random.NextDouble(). [0 диапазонында қос дәлдікті сандарды жасайды; 1).
  3. UnityEngine.Random.Range(). Мәндердің берілген ауқымында бір дәлдіктегі сандарды (қалқымалы мәндерді) жасайды.
  4. UnityEngine.Random.value. [0 диапазонында бір дәлдіктегі сандарды (флоат) жасайды; 1).
  5. Unity.Mathematics.Random.NextFloat(). Жаңа Unity.Mathematics кітапханасының бөлігі. Берілген ауқымдағы бір дәлдіктегі қалқымалы сандарды жасайды.

Құжаттаманың барлық жерінде дерлік UnityEngine.Random.value (тарату көрсетілмеген, бірақ UnityEngine.Random.Range() ұқсастығы бойынша, біркелкі бөлу де күтілген) және Unity.Mathematics.Random.NextFloat() қоспағанда, біркелкі бөлу көрсетілген (бұл қайтадан xorith дегенді білдіреді, бұл таратылым біркелкі болуы керек дегенді білдіреді. күтілген).

Әдепкі бойынша күтілетін нәтижелер құжаттамада көрсетілгендер болды.

Әдістеме

Біз ұсынылған әдістердің әрқайсысын қолдана отырып, кездейсоқ сандар тізбегін құрайтын және одан әрі өңдеу үшін нәтижелерді сақтайтын шағын қосымшаны жаздық.

Әрбір тізбектің ұзындығы 100 000 сан.
Кездейсоқ сандар мәндерінің диапазоны [0, 100).

Деректер бірнеше мақсатты платформалардан жиналды:

  • Windows
    — Unity v2018.3.14f1, Редактор режимі, Mono, .NET Standard 2.0
  • macOS
    — Unity v2018.3.14f1, Редактор режимі, Mono, .NET Standard 2.0
    — Unity v5.6.4p4, Редактор режимі, Mono, .NET Standard 2.0
  • Android
    — Unity v2018.3.14f1, құрылғы құрастыру, Mono, .NET Standard 2.0
  • IOS
    — Unity v2018.3.14f1, құрылғы құрастыру, il2cpp, .NET Standard 2.0

Реализация

Бізде кездейсоқ сандарды құрудың бірнеше түрлі әдістері бар. Әрқайсысы үшін біз бөлек орауыш класын жазамыз, ол мыналарды қамтамасыз етуі керек:

  1. Мәндер ауқымын көрсету мүмкіндігі [min/max). Конструктор арқылы орнатылады.
  2. Санды қайтаратын әдіс. Түр ретінде біз қалқымалыны таңдаймыз, себебі ол жалпылама.
  3. Нәтижелерді таңбалауға арналған генерация әдісінің атауы. Ыңғайлы болу үшін біз толық сынып атауын + мән ретінде SC жасау үшін қолданылатын әдістің атауын қайтарамыз.

Алдымен, IRandomGenerator интерфейсімен ұсынылатын абстракцияны жариялайық:

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

        float Generate();
    }
}

System.Random.Next() іске асыру

Бұл әдіс мәндер ауқымын көрсетуге мүмкіндік береді, бірақ ол бүтін сандарды қайтарады, ал қалқымалы мәндер қажет. Сіз жай ғана бүтін санды қалқымалы мән ретінде түсіндіре аласыз немесе жиілік жасалған сайын олардың орнын толтыра отырып, мәндер ауқымын бірнеше шама ретімен кеңейте аласыз. Бұл белгілі бір дәлдік реті бар тіркелген нүкте сияқты нәрсеге әкеледі. Біз бұл опцияны қолданамыз, себебі ол шынайы өзгермелі мәнге жақынырақ.

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() іске асыру

Мұнда мәндердің бекітілген диапазоны [0; 1). Оны конструкторда көрсетілгенге проекциялау үшін қарапайым арифметиканы қолданамыз: 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() іске асыру

UnityEngine.Random статикалық класының бұл әдісі мәндер ауқымын көрсетуге мүмкіндік береді және өзгермелі түрдегі кездейсоқ санды қайтарады. Қосымша түрлендірулер қажет емес.

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 іске асыру

UnityEngine.Random статикалық класының мән қасиеті бекітілген мәндер ауқымынан өзгермелі түрдегі кездейсоқ санды қайтарады [0; 1). Біз оны берілген диапазонға 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() іске асыру

Unity.Mathematics.Random класының NextFloat() әдісі қалқымалы кездейсоқ санды қайтарады және мәндер ауқымын көрсетуге мүмкіндік береді. Жалғыз ескерту: Unity.Mathematics.Random бағдарламасының әрбір данасы тұқыммен инициализациялануы керек — бұл қайталанатын тізбектердің пайда болуына жол бермейді.

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

MainController іске асыру

Бірнеше IRandomGenerator іске асыру дайын. Әрі қарай, реттіліктерді жасап, алынған деректер жиынтығын өңдеу үшін сақтау керек. Мұны істеу үшін Unity бағдарламасында көрініс пен барлық қажетті жұмысты орындайтын және UI өзара әрекеттесуін өңдейтін шағын MainController сценарийін жасаймыз.

Деректер жиынының өлшемін және жиілік мәндерінің ауқымын анықтаймыз, сондай-ақ конфигурацияланған және пайдалануға дайын генераторлар массивін қайтаратын әдісті жасаймыз.

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

        ...
    }
}

Енді деректер жинағын жасаймыз. Бұл жағдайда деректерді генерациялау нәтижелерді мәтін ағынына жазумен біріктіріледі (CSV пішімінде). Әрбір IRandomGenerator өз бағанына ие болады және бірінші жолда генератордың аты болады.

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

        ...
    }
}

Тек GenerateCsvDataSet әдісін шақырып, нәтижені файлға сақтау немесе деректерді желі арқылы соңғы құрылғыдан қабылдаушы құрылғыға дереу жіберу ғана қалады. сервер.

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

        ...
    }
}

Жоба көздері мына жерде орналасқан GitLab.

нәтижелері

Ешқандай керемет болған жоқ. Біз күткен нәрсеге қол жеткіздік - барлық жағдайда біркелкі бөлу, ешқандай қастандық белгісі жоқ. Мен бөлек платформалық диаграммаларды қосудың мәнін көрмеймін - олардың барлығы шамамен бірдей нәтижелерді көрсетеді.

Шындық мынау:
BlessRNG немесе RNG әділдігін тексеру

Барлық бес генерация әдісінен жазықтықта тізбектерді визуализациялау:
BlessRNG немесе RNG әділдігін тексеру

Және 3D визуализациясы. Бірдей мазмұнды жасамау үшін System.Random.Next() нәтижесін ғана қалдырамын.
BlessRNG немесе RNG әділдігін тексеру

UnityEngine.Random-тың қалыпты таралуы туралы кіріспеде айтылған оқиға қайталанбады: не ол басынан бастап ақаулы болды, немесе содан бері қозғалтқышта бір нәрсе өзгерді. Бірақ қазір сенімдіміз.

Ақпарат көзі: www.habr.com

DDoS қорғауы бар сайттар үшін сенімді хостинг, VPS VDS серверлерін сатып алыңыз 🔥 DDoS қорғанысы, VPS VDS серверлері бар сенімді веб-сайт хостингін сатып алыңыз | ProHoster