BlessRNG utawa mriksa RNG kanggo keadilan

BlessRNG utawa mriksa RNG kanggo keadilan

Ing pembangunan game, sampeyan kerep kudu dasi soko munggah karo randomness: Unity wis Random dhewe kanggo iki, lan ing podo karo ana System.Random. Biyen, ing salah sawijining proyek, aku entuk kesan yen loro-lorone bisa beda-beda (sanajan kudu duwe distribusi sing rata).

Banjur padha ora pindhah menyang rincian - iku cukup yen transisi menyang System.Random didandani kabeh masalah. Saiki kita mutusaké kanggo dipikir ing liyane rinci lan nindakake riset sethitik: carane RNGs "bias" utawa katebak, lan kang siji kanggo milih. Kajaba iku, aku wis luwih saka sepisan krungu pendapat sing bertentangan babagan "kejujuran" - ayo nyoba ngerteni kepiye asil nyata dibandhingake karo sing diumumake.

Program pendidikan singkat utawa RNG sejatine RNG

Yen sampeyan wis kenal karo generator nomer acak, sampeyan bisa langsung pindhah menyang bagean "Tes".

Nomer acak (RN) minangka urutan nomer sing digawe nggunakake sawetara proses acak (kacau), sumber entropi. Tegese, iki minangka urutan sing unsur-unsur ora ana hubungane karo hukum matematika - ora ana hubungan sebab-akibat.

Sing nggawe nomer acak diarani generator nomer acak (RNG). Iku bakal katon sing kabeh iku dhasar, nanging yen kita pindhah saka teori kanggo laku, ing kasunyatan iku ora supaya prasaja kanggo ngleksanakake algoritma lunak kanggo nggawe urutan kuwi.

Alesane yaiku ora ana kekacauan sing padha ing elektronik konsumen modern. Tanpa iku, nomer acak mandek dadi acak, lan generator dadi fungsi biasa bantahan temenan ditetepake. Kanggo sawetara spesialisasi ing bidang IT, iki minangka masalah serius (contone, kriptografi), nanging kanggo wong liya ana solusi sing bisa ditampa kanthi lengkap.

Sampeyan kudu nulis algoritma sing bakal bali, sanajan nomer kasebut ora bener-bener acak, nanging sabisa-bisane - sing diarani nomer pseudo-random (PRN). Algoritma ing kasus iki diarani generator nomer pseudorandom (PRNG).

Ana sawetara opsi kanggo nggawe PRNG, nanging ing ngisor iki bakal cocog kanggo kabeh wong:

  1. Perlu kanggo initialization awal.

    PRNG ora duwe sumber entropi, mula kudu diwenehi status awal sadurunge digunakake. Ditemtokake minangka angka (utawa vektor) lan diarani wiji (wiji acak). Asring, counter jam prosesor utawa setara numerik wektu sistem digunakake minangka wiji.

  2. Reproducibility urutan.

    PRNG pancen deterministik, saengga wiji sing ditemtokake sajrone wiwitan kanthi unik nemtokake kabeh urutan nomer ing mangsa ngarep. Iki tegese PRNG kapisah diwiwiti kanthi winih sing padha (ing wektu sing beda, ing program sing beda, ing piranti sing beda) bakal ngasilake urutan sing padha.

Sampeyan uga kudu ngerti distribusi probabilitas characterizing PRNG - apa nomer bakal generate lan apa kamungkinan. Paling asring iki minangka distribusi normal utawa distribusi seragam.
BlessRNG utawa mriksa RNG kanggo keadilan
Distribusi normal (kiwa) lan distribusi seragam (tengen)

Ayo kita duwe die adil karo 24 sisih. Yen sampeyan mbuwang, kemungkinan entuk siji bakal padha karo 1/24 (padha karo kemungkinan entuk nomer liyane). Yen sampeyan nggawe akeh uncalan lan ngrekam asil, sampeyan bakal sok dong mirsani sing kabeh sudhut tiba metu karo frekuensi kira-kira padha. Ateges, mati iki bisa dianggep minangka RNG kanthi distribusi seragam.

Apa yen sampeyan mbuwang 10 dadu iki bebarengan lan ngetung total poin? Apa keseragaman bakal dijaga? Ora. Paling asring, jumlah kasebut bakal cedhak karo 125 poin, yaiku, kanggo sawetara nilai rata-rata. Lan minangka asil, malah sadurunge nggawe uncalan, sampeyan bisa kira-kira asil mangsa.

Alesane yaiku ana kombinasi paling akeh kanggo entuk skor rata-rata. Sing luwih adoh, kombinasi sing luwih sithik - lan, kanthi mangkono, kemungkinan kerugian sing luwih murah. Yen data iki digambarake, bakal meh padha karo bentuk lonceng. Mulane, kanthi sawetara babagan, sistem 10 dadu bisa diarani RNG kanthi distribusi normal.

Conto liyane, mung wektu iki ing pesawat - njupuk ing target. Penembake bakal dadi RNG sing ngasilake pasangan nomer (x, y) sing ditampilake ing grafik.
BlessRNG utawa mriksa RNG kanggo keadilan
Setuju yen pilihan ing sisih kiwa luwih cedhak karo urip nyata - iki minangka RNG kanthi distribusi normal. Nanging yen sampeyan kudu nyebar lintang ing langit peteng, banjur pilihan tengen, dijupuk nggunakake RNG karo distribusi seragam, iku luwih cocog. Umumé, milih generator gumantung ing tugas ing tangan.

Saiki ayo ngomong babagan entropi saka urutan PNG. Contone, ana urutan sing diwiwiti kaya iki:

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

Carane acak nomer iki ing kawitan marketing? Ayo miwiti kanthi mriksa distribusi.
BlessRNG utawa mriksa RNG kanggo keadilan
Iku katon cedhak karo seragam, nanging yen maca urutan saka rong nomer lan kokwaca minangka koordinat ing pesawat, sampeyan njaluk iki:
BlessRNG utawa mriksa RNG kanggo keadilan
Pola katon jelas. Lan wiwit data ing urutan diurutake kanthi cara tartamtu (yaiku, nduweni entropi sing kurang), iki bisa nyebabake "bias" kasebut. Paling ora, PRNG kasebut ora cocog kanggo ngasilake koordinat ing pesawat.

Urutan liyane:

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

Kabeh katon apik ing kene sanajan ing pesawat:
BlessRNG utawa mriksa RNG kanggo keadilan
Ayo katon ing volume (maca telung nomer sekaligus):
BlessRNG utawa mriksa RNG kanggo keadilan
Lan maneh pola. Wis ora bisa maneh kanggo mbangun visualisasi ing papat dimensi. Nanging pola bisa ana ing dimensi iki lan ing ukuran sing luwih gedhe.

Ing kriptografi, ing ngendi syarat sing paling ketat dileksanakake ing PRNG, kahanan kasebut ora bisa ditampa kanthi kategorine. Mulane, algoritma khusus wis dikembangake kanggo netepake kualitase, sing ora bakal didemek saiki. Topik punika ekstensif lan pantes artikel kapisah.

Tes

Yen kita ora ngerti apa-apa, banjur kepiye cara nggarap? Apa worth nyebrang dalan yen sampeyan ora ngerti lampu lalu lintas sing ngidini? Konsekuensi bisa uga beda.

Padha dadi kanggo randomness kondhang ing Unity. Iku apik yen dokumentasi mbukak rincian sing perlu, nanging crita kasebut ing awal artikel kedaden amarga saka lack saka spesifik sing dikarepake.

Lan yen sampeyan ora ngerti cara kerjane alat kasebut, sampeyan ora bakal bisa nggunakake kanthi bener. Umumé, wektu wis teka kanggo mriksa lan nganakake eksperimen kanggo mesthekake paling ora babagan distribusi.

Solusi kasebut gampang lan efektif - ngumpulake statistik, entuk data objektif lan deleng asile.

Subyek sinau

Ana sawetara cara kanggo generate nomer acak ing Unity - kita dites limang.

  1. System.Random.Next(). Ngasilake integer ing sawetara nilai tartamtu.
  2. System.Random.NextDouble(). Ngasilake nomer tliti pindho ing sawetara saka [0; 1).
  3. UnityEngine.Random.Range(). Ngasilake nomer presisi tunggal (ngambang) ing sawetara nilai tartamtu.
  4. UnityEngine.Random.value. Ngasilake nomer tliti siji (ngambang) ing sawetara saka [0; 1).
  5. Unity.Mathematics.Random.NextFloat(). Bagéyan saka perpustakaan Unity.Mathematics anyar. Ngasilake nomer presisi tunggal (ngambang) ing sawetara nilai tartamtu.

Meh nang endi wae ing dokumentasi kasebut distribusi seragam, kajaba UnityEngine.Random.value (ing ngendi distribusi ora ditemtokake, nanging kanthi analogi karo UnityEngine.Random.Range () seragam uga samesthine) lan Unity.Mathematics.Random .NextFloat () (ngendi ing basis punika algoritma xorshift, kang tegese maneh sampeyan kudu ngenteni distribusi seragam).

Kanthi gawan, asil samesthine dijupuk minangka sing ditemtokake ing dokumentasi.

Metodologi

We wrote aplikasi cilik sing kui urutan nomer acak nggunakake saben cara presented lan disimpen asil kanggo Processing luwih.

Dawane saben urutan yaiku 100 angka.
Rentang angka acak yaiku [0, 100).

Data diklumpukake saka sawetara platform target:

  • Windows
    — Unity v2018.3.14f1, Mode editor, Mono, .NET Standard 2.0
  • MacOS
    — Unity v2018.3.14f1, Mode editor, Mono, .NET Standard 2.0
    — Unity v5.6.4p4, mode Editor, Mono, .NET Standard 2.0
  • Android
    - Unity v2018.3.14f1, mbangun saben piranti, Mono, .NET Standard 2.0
  • iOS
    - Unity v2018.3.14f1, mbangun saben piranti, il2cpp, .NET Standard 2.0

Реализация

Kita duwe sawetara cara kanggo ngasilake nomer acak. Kanggo saben wong, kita bakal nulis kelas pambungkus sing kapisah, sing kudu nyedhiyakake:

  1. Kamungkinan kanggo nyetel sawetara nilai [min / max). Bakal disetel liwat konstruktor.
  2. Metode ngasilake MF. Ayo milih float minangka jinis, amarga luwih umum.
  3. Jeneng metode generasi kanggo menehi tandha asil. Kanggo penak, kita bakal bali minangka nilai jeneng lengkap kelas + jeneng cara digunakake kanggo generate MF.

Pisanan, ayo ngumumake abstraksi sing bakal diwakili dening antarmuka IRandomGenerator:

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

        float Generate();
    }
}

Implementasi System.Random.Next()

Cara iki ngidini sampeyan nyetel sawetara nilai, nanging ngasilake integer, nanging floats dibutuhake. Sampeyan mung bisa nginterpretasikake integer minangka float, utawa sampeyan bisa nggedhekake sawetara nilai kanthi sawetara urutan gedhene, menehi kompensasi karo saben generasi midrange. Asil bakal kaya titik tetep kanthi urutan akurasi. Kita bakal nggunakake pilihan iki amarga luwih cedhak karo nilai float nyata.

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

Implementasi System.Random.NextDouble()

Ing kene sawetara nilai tetep [0; 1). Kanggo proyek kasebut menyang sing kasebut ing konstruktor, kita nggunakake aritmetika prasaja: X * (maks − 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;
    }
}

Implementasi UnityEngine.Random.Range()

Cara kelas statis UnityEngine.Random iki ngidini sampeyan nyetel sawetara nilai lan ngasilake jinis float. Sampeyan ora kudu nindakake transformasi tambahan.

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

Implementasi UnityEngine.Random.value

Properti nilai saka UnityEngine.Random kelas statis ngasilake jinis float saka sawetara nilai tetep [0; 1). Ayo project menyang sawetara diwenehi ing cara sing padha nalika ngleksanakake 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;
    }
}

Implementasi Unity.Mathematics.Random.NextFloat()

Metode NextFloat () kelas Unity.Mathematics.Random ngasilake titik ngambang saka jinis ngambang lan ngidini sampeyan nemtokake sawetara nilai. Siji-sijine nuansa yaiku saben conto Unity.Mathematics.Random kudu diinisialisasi karo sawetara wiji - kanthi cara iki kita ora bakal ngasilake urutan sing bola-bali.

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

Implementasi MainController

Sawetara implementasi IRandomGenerator wis siyap. Sabanjure, sampeyan kudu ngasilake urutan lan nyimpen dataset sing diasilake kanggo diproses. Kanggo nindakake iki, kita bakal nggawe pemandangan lan script MainController cilik ing Unity, kang bakal nindakake kabeh karya perlu lan ing wektu sing padha tanggung jawab kanggo interaksi karo UI.

Ayo nyetel ukuran set data lan sawetara nilai MF, lan uga entuk metode sing ngasilake macem-macem generator sing dikonfigurasi lan siap digunakake.

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

        ...
    }
}

Saiki ayo nggawe dataset. Ing kasus iki, generasi data bakal digabungake karo ngrekam asil menyang stream teks (ing format csv). Kanggo nyimpen nilai saben IRandomGenerator, kolom kapisah dhewe diparengake, lan baris pisanan ngemot Jeneng 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();
            }
        }

        ...
    }
}

Kabeh sing isih ana yaiku nelpon metode GenerateCsvDataSet lan simpen asil menyang file, utawa langsung nransfer data liwat jaringan saka piranti pungkasan menyang server panampa.

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

        ...
    }
}

Sumber proyek ana ing GitLab.

Результаты

Ora ana keajaiban. Sing dikarepake yaiku apa sing dipikolehi - ing kabeh kasus, distribusi sing rata tanpa ana konspirasi. Aku ora weruh titik kanggo nyelehake grafik sing kapisah kanggo platform - kabeh nuduhake asil sing padha.

Kasunyatane yaiku:
BlessRNG utawa mriksa RNG kanggo keadilan

Visualisasi urutan ing pesawat saka kabeh limang cara generasi:
BlessRNG utawa mriksa RNG kanggo keadilan

Lan visualisasi ing 3D. Aku bakal ninggalake mung asil System.Random.Next () supaya ora kanggo gawé Bunch saka isi podho rupo.
BlessRNG utawa mriksa RNG kanggo keadilan

Crita marang ing introduksi bab distribusi normal UnityEngine.Random ora mbaleni dhewe: salah siji iku pisanan erroneous, utawa soko wis wiwit diganti ing mesin. Nanging saiki kita yakin.

Source: www.habr.com

Add a comment