BlessRNG یا د انصاف لپاره RNG چک کول

BlessRNG یا د انصاف لپاره RNG چک کول

د لوبې په پراختیا کې ، تاسو ډیری وختونه اړتیا لرئ یو څه د تصادفي سره وتړئ: یووالي د دې لپاره خپل تصادفي لري ، او د دې سره موازي سیسټم شتون لري. یو وخت، په یوه پروژه کې، ما دا تاثر ترلاسه کړ چې دواړه کولی شي په مختلف ډول کار وکړي (که څه هم دوی باید حتی ویش ولري).

بیا دوی توضیحاتو ته لاړ نه شول - دا بس و چې سیسټم ته لیږد ټولې ستونزې سمې کړې. اوس موږ پریکړه وکړه چې دا په ډیر تفصیل سره وګورو او لږه څیړنه ترسره کړو: څومره "متعصب" یا د وړاندوینې وړ RNGs دي، او کوم یو غوره کړئ. سربیره پردې ، ما د دوی د "ایماندارۍ" په اړه له یو ځل څخه ډیر متضاد نظرونه اوریدلي دي - راځئ هڅه وکړو چې معلومه کړو چې ریښتینې پایلې د اعلان شوي پایلو سره څنګه پرتله کیږي.

لنډ تعلیمي پروګرام یا RNG په حقیقت کې RNG دی

که تاسو دمخه د تصادفي شمیرې جنریټرونو سره آشنا یاست ، نو تاسو سمدلاسه د "ازموینې" برخې ته لاړ شئ.

تصادفي شمیرې (RN) د شمیرو لړۍ ده چې د ځینې تصادفي ( ګډوډ) پروسې په کارولو سره رامینځته کیږي ، د انټروپي سرچینه. یعني، دا یو ترتیب دی چې عناصر یې د هیڅ ریاضياتي قانون سره تړلي ندي - دوی د علت او تاثیر اړیکه نلري.

هغه څه چې تصادفي شمیره رامینځته کوي د تصادفي شمیرې جنریټر (RNG) په نوم یادیږي. داسې ښکاري چې هر څه ابتدايي دي، مګر که موږ له تیوري څخه عمل ته حرکت وکړو، نو په حقیقت کې دا دومره ساده نه ده چې د داسې ترتیب رامینځته کولو لپاره د سافټویر الګوریتم پلي کول.

دلیل په عصري مصرف کونکي برقیاتو کې د ورته ګډوډۍ نشتوالی دی. پرته له دې، تصادفي شمیرې تصادفي پاتې کیږي، او د دوی جنراتور د څرګند تعریف شوي دلیلونو عادي فعالیت بدلوي. د معلوماتي ټکنالوجۍ په برخه کې د یو شمیر ځانګړتیاو لپاره، دا یوه جدي ستونزه ده (د بیلګې په توګه، کریپټوګرافي)، مګر د نورو لپاره په بشپړه توګه د منلو وړ حل شتون لري.

دا اړینه ده چې یو الګوریتم ولیکئ چې بیرته راشي ، که څه هم واقعیا تصادفي شمیرې نه وي ، مګر څومره چې امکان ولري دوی ته نږدې وي - تش په نامه سیډو - تصادفي شمیرې (PRN). په دې قضیه کې الګوریتم د pseudorandom شمیره جنریټر (PRNG) په نوم یادیږي.

د PRNG جوړولو لپاره ډیری اختیارونه شتون لري، مګر لاندې به د هرچا لپاره اړین وي:

  1. د ابتدايي ابتدايي کولو اړتیا.

    PRNG د انټروپي سرچینه نلري، نو دا باید د کارولو دمخه یو ابتدايي حالت ورکړل شي. دا د عدد (یا ویکتور) په توګه مشخص شوی او د تخم (تصادفي تخم) په نوم یادیږي. ډیری وختونه، د پروسیسر ساعت کاونټر یا د سیسټم وخت شمیري مساوي د تخم په توګه کارول کیږي.

  2. په ترتیب سره د تولید وړتیا.

    PRNG په بشپړ ډول ټاکونکی دی، نو د پیل کولو په وخت کې مشخص شوی تخم په ځانګړي ډول د شمیرو ټول راتلونکی ترتیب ټاکي. دا پدې مانا ده چې یو جلا PRNG د ورته تخم سره پیل شوی (په مختلفو وختونو کې، په مختلفو پروګرامونو کې، په مختلفو وسایلو کې) به ورته ترتیب تولید کړي.

تاسو اړتیا لرئ د احتمالي توزیع په اړه هم پوه شئ چې د PRNG ځانګړتیا لري - کوم شمیرې به رامینځته کړي او د کوم احتمال سره. ډیری وختونه دا یا یو نورمال ویش یا یونیفورم ویش دی.
BlessRNG یا د انصاف لپاره RNG چک کول
نورمال توزیع (کیڼ اړخ) او یونیفورم ویش (ښي)

راځئ چې ووایو موږ د 24 اړخونو سره عادلانه مړینه لرو. که تاسو یې وغورځوئ، د یو ترلاسه کولو احتمال به د 1/24 سره مساوي وي (د کومې بلې شمیرې ترلاسه کولو احتمال ورته ورته وي). که تاسو ډیری ګولۍ جوړې کړئ او پایلې یې ثبت کړئ، نو تاسو به وګورئ چې ټولې څنډې نږدې ورته فریکونسۍ سره راوتلي. په لازمي ډول، دا مړینه د یونیفورم ویش سره RNG ګڼل کیدی شي.

څه به وي که تاسو په یو وخت کې له دې څخه 10 مرغۍ وغورځوئ او ټول ټکي حساب کړئ؟ ایا د دې لپاره یووالي ساتل کیږي؟ نه. ډیری وختونه، مقدار به د 125 پوائنټونو ته نږدې وي، دا یو څه اوسط ارزښت ته. او د پایلې په توګه، حتی د غورځولو دمخه، تاسو کولی شئ د راتلونکي پایلې اټکل وکړئ.

دلیل یې دا دی چې د اوسط نمرې ترلاسه کولو لپاره ترټولو لوی شمیر ترکیبونه شتون لري. هرڅومره چې له دې څخه لرې وي ، لږ ترکیبونه - او په وینا یې ، د زیان احتمال کم دی. که دا ډاټا لیدل کیږي، دا به په مبهم ډول د زنګ شکل سره ورته وي. له همدې امله، د یو څه اوږدوالي سره، د 10 ډیس سیسټم د عادي ویش سره RNG بلل کیدی شي.

بل مثال، یوازې دا وخت په الوتکه کې - په هدف ډزې کول. شوټر به یو RNG وي چې یو جوړه شمیره رامینځته کوي (x, y) چې په ګراف کې ښودل شوي.
BlessRNG یا د انصاف لپاره RNG چک کول
موافقه وکړئ چې کیڼ اړخ ته اختیار ریښتیني ژوند ته نږدې دی - دا د عادي توزیع سره RNG دی. مګر که تاسو اړتیا لرئ په تیاره اسمان کې ستوري توزیع کړئ ، نو سم انتخاب ، د یونیفورم توزیع سره د RNG په کارولو سره ترلاسه شوی غوره مناسب دی. په عموم کې، په لاس کې د دندې پر بنسټ یو جنراتور غوره کړئ.

اوس راځئ چې د PNG ترتیب د انټروپي په اړه وغږیږو. د مثال په توګه، یو ترتیب شتون لري چې داسې پیل کیږي:

۸۹، ۹۳، ۳۳، ۳۲، ۸۲، ۲۱، ۴، ۴۲، ۱۱، ۸، ۶۰، ۹۵، ۵۳، ۳۰، ۴۲، ۱۹، ۳۴، ۳۵، ۶۲، ۲۳، ۴۴، ۳۸، ۷۴، ۳۶، ۵۲ ۱۸، ۵۸، ۷۹، ۶۵، ۴۵، ۹۹، ۹۰، ۸۲، ۲۰، ۴۱، ۱۳، ۸۸، ۷۶، ۸۲، ۲۴، ۵، ۵۴، ۷۲، ۱۹، ۸۰، ۲، ۷۴، ۳۶، ۷۱، ۹ ...

په لومړي نظر کې دا شمیرې څومره تصادفي دي؟ راځئ چې د ویش په چک کولو سره پیل وکړو.
BlessRNG یا د انصاف لپاره RNG چک کول
دا یونیفورم ته نږدې ښکاري، مګر که تاسو د دوو شمیرو ترتیب ولولئ او په الوتکه کې د همغږۍ په توګه تشریح کړئ، تاسو دا ترلاسه کوئ:
BlessRNG یا د انصاف لپاره RNG چک کول
نمونې په ښکاره ډول ښکاره کیږي. او څرنګه چې په ترتیب کې ډاټا په یو مشخص ډول ترتیب شوي (یعنې دا ټیټ انټروپي لري)، دا کولی شي هغه "تعصب" ته وده ورکړي. لږترلږه، دا ډول PRNG په الوتکه کې د همغږۍ تولید لپاره خورا مناسب نه دی.

بله لړۍ:

۸۹، ۹۳، ۳۳، ۳۲، ۸۲، ۲۱، ۴، ۴۲، ۱۱، ۸، ۶۰، ۹۵، ۵۳، ۳۰، ۴۲، ۱۹، ۳۴، ۳۵، ۶۲، ۲۳، ۴۴، ۳۸، ۷۴، ۳۶، ۵۲ ۱۸، ۵۸، ۷۹، ۶۵، ۴۵، ۹۹، ۹۰، ۸۲، ۲۰، ۴۱، ۱۳، ۸۸، ۷۶، ۸۲، ۲۴، ۵، ۵۴، ۷۲، ۱۹، ۸۰، ۲، ۷۴، ۳۶، ۷۱، ۹ ...

دلته هرڅه سم ښکاري حتی په الوتکه کې:
BlessRNG یا د انصاف لپاره RNG چک کول
راځئ چې په حجم کې وګورو (په یو وخت کې درې شمیرې ولولئ):
BlessRNG یا د انصاف لپاره RNG چک کول
او بیا نمونې. دا نور امکان نلري چې په څلورو ابعادو کې یو لید جوړ کړئ. مګر نمونې په دې ابعاد او لویو کې شتون لري.

په کریپټوګرافي کې، چیرې چې په PRNGs باندې خورا سخت شرایط وضع شوي، دا ډول وضعیت په کلکه د منلو وړ نه دی. له همدې امله ، د دوی کیفیت ارزولو لپاره ځانګړي الګوریتمونه رامینځته شوي ، کوم چې موږ به یې اوس په تماس کې نه شو. موضوع پراخه ده او د جلا مقالې مستحق دی.

ازمايښت

که موږ د ډاډ لپاره یو څه نه پوهیږو، نو څنګه ورسره کار وکړو؟ ایا دا د سړک څخه تیریدو ارزښت لري که تاسو نه پوهیږئ کوم ټرافیک څراغ ورته اجازه ورکوي؟ پایلې ممکن توپیر ولري.

ورته په یووالي کې د بدنام تصادفي لپاره ځي. دا ښه ده که اسناد اړین توضیحات څرګند کړي، مګر د مقالې په پیل کې یادونه شوې کیسه دقیقا د مطلوب مشخصاتو نشتوالي له امله واقع شوې.

او که تاسو نه پوهیږئ چې دا وسیله څنګه کار کوي، تاسو به د دې توان ونلرئ چې دا په سمه توګه وکاروئ. په عموم کې، وخت راغلی چې تجربه وګورئ او ترسره کړئ ترڅو په پای کې لږترلږه د ویش په اړه ډاډ ترلاسه کړئ.

د حل لاره ساده او اغیزمنه وه - احصایې راټول کړئ، هدف ډاټا ترلاسه کړئ او پایلې وګورئ.

د مطالعې موضوع

په یووالي کې د تصادفي شمیرو رامینځته کولو ډیری لارې شتون لري - موږ پنځه ازموینه وکړه.

  1. System.Random.Next(). د ارزښتونو په یوه ټاکل شوي سلسله کې انټیجرونه تولیدوي.
  2. System.Random.NextDouble(). له [0] څخه په سلسله کې دوه ځله دقیق شمیرې تولیدوي. 1).
  3. UnityEngine.Random.Range(). د ارزښتونو په یوه ورکړل شوي سلسله کې واحد دقیق شمیر (فلوټ) رامینځته کوي.
  4. د یووالي انجین.Random.value. له [0] څخه په سلسله کې واحد دقیق شمیر (فلوټ) تولیدوي. 1).
  5. اتحاد.ریاضی.رینډم.نیکسټ فلوټ(). د ریاضیاتو د کتابتون یوه برخه. د ارزښتونو په یوه ورکړل شوي سلسله کې واحد دقیق شمیر (فلوټ) رامینځته کوي.

په اسنادو کې نږدې هرچیرې یونیفورم توزیع مشخص شوی و، پرته له UnityEngine.Random.value (چیرې چې ویش مشخص شوی نه و، مګر د UnityEngine.Random.Range() یونیفورم سره ورته والی هم تمه کیده) او Unity.Mathematics.Random .NextFloat() (په کوم ځای کې چې اساس د xorshift الګوریتم دی، پدې معنی چې بیا تاسو اړتیا لرئ د یونیفورم ویش انتظار وکړئ).

په ډیفالټ کې، تمه شوي پایلې د اسنادو په توګه مشخص شوي.

میتودولوژي

موږ یو کوچنی غوښتنلیک لیکلی چې د هرې وړاندې شوي میتودونو په کارولو سره د تصادفي شمیرو لړۍ رامینځته کړې او پایلې یې د نورو پروسس کولو لپاره خوندي کړې.

د هرې سلسلې اوږدوالی 100 شمیرې دي.
د تصادفي شمیرو سلسله ده [0، 100).

ډاټا د څو هدف پلیټ فارمونو څخه راټول شوي:

  • Windows
    - یووالي v2018.3.14f1، د مدیر حالت، مونو، .NET معیاري 2.0
  • macOS
    - یووالي v2018.3.14f1، د مدیر حالت، مونو، .NET معیاري 2.0
    - یووالي v5.6.4p4، د مدیر حالت، مونو، .NET معیاري 2.0
  • Android
    - یووالي v2018.3.14f1، د هر وسیله جوړ کړئ، مونو، .NET معیاري 2.0
  • iOS
    - یووالي v2018.3.14f1، هر وسیله جوړ کړئ، il2cpp، .NET معیاري 2.0

پلي کول

موږ د تصادفي شمیرو رامینځته کولو لپاره ډیری مختلفې لارې لرو. د دوی د هر یو لپاره، موږ به د جلا پوښۍ ټولګي ولیکئ، کوم چې باید چمتو کړي:

  1. د ارزښتونو حد ټاکلو امکان [منټ/زیاته]. د جوړونکي له لارې به تنظیم شي.
  2. د MF د بیرته راستنیدو طریقه. راځئ چې د ډول په توګه فلوټ غوره کړو، ځکه چې دا ډیر عام دی.
  3. د پایلو د نښه کولو لپاره د نسل میتود نوم. د اسانتیا لپاره، موږ به د ارزښت په توګه د ټولګي بشپړ نوم + د میتود نوم چې د MF تولید لپاره کارول کیږي.

لومړی، راځئ چې یو خلاصون اعلان کړو چې د IRandomGenerator انٹرفیس لخوا به استازیتوب کیږي:

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

        float Generate();
    }
}

د سیسټم پلي کول.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;
    }
}

د سیسټم پلي کول.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 جامد ټولګي دا طریقه تاسو ته اجازه درکوي یو لړ ارزښتونه وټاکئ او د فلوټ ډول بیرته راوباسئ. تاسو اړتیا نلرئ کوم اضافي بدلونونه ترسره کړئ.

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() میتود د فلوټ ډول ډول فلوټینګ نقطه راګرځوي او تاسو ته اجازه درکوي د ارزښتونو لړۍ مشخص کړئ. یوازینی اهمیت دا دی چې د یووالي. ریاضیاتو هر مثال باید د یو څه تخم سره پیل شي - پدې توګه به موږ د تکرار ترتیبونو رامینځته کولو څخه مخنیوی وکړو.

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

د اصلي کنټرولر پلي کول

د IRandomGenerator ډیری تطبیقونه چمتو دي. بیا ، تاسو اړتیا لرئ ترتیبونه رامینځته کړئ او د پروسس کولو لپاره پایله لرونکي ډیټاسیټ خوندي کړئ. د دې کولو لپاره، موږ به په یووالي کې یو صحنه او یو کوچنی اصلي کنټرولر سکریپټ جوړ کړو، کوم چې به ټول اړین کارونه ترسره کړي او په ورته وخت کې د UI سره د تعامل مسولیت په غاړه ولري.

راځئ چې د ډیټاسیټ اندازه او د MF ارزښتونو سلسله تنظیم کړو، او یو میتود هم ترلاسه کړو چې د جنراتورونو لړۍ بیرته راګرځوي چې ترتیب شوي او کار کولو ته چمتو دي.

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

        ...
    }
}

د پروژې سرچینې په کې دي ګیتاباب.

پایلې

کومه معجزه نه وه شوې. هغه څه چې دوی یې تمه درلوده هغه څه دي چې دوی ترلاسه کړي - په ټولو قضیو کې، د توطیې له اشارې پرته حتی ویش. زه د پلیټ فارمونو لپاره د جلا ګرافونو په ایښودلو کې نقطه نه ګورم - دوی ټول نږدې ورته پایلې ښیې.

حقیقت دا دی:
BlessRNG یا د انصاف لپاره RNG چک کول

د ټولو پنځو نسلونو میتودونو څخه په الوتکه کې د ترتیبونو لید:
BlessRNG یا د انصاف لپاره RNG چک کول

او په 3D کې لید. زه به یوازې د System.Random.Next() پایله پریږدم ترڅو د ورته مینځپانګو یوه ډله تولید نه کړم.
BlessRNG یا د انصاف لپاره RNG چک کول

کیسه د UnityEngine د نورمال توزیع په اړه په پیژندنه کې ویل شوي. Random ځان نه تکراروي: یا دا په پیل کې غلطه وه، یا یو څه وروسته په انجن کې بدلون موندلی. مګر اوس موږ ډاډه یو.

سرچینه: www.habr.com

Add a comment