Example #1
0
        /// <summary>
        /// Builds StaticRandomSelector & clears internal buffers. Must be called after you finish Add-ing items.
        /// </summary>
        /// <param name="seed">Seed for random selector. If you leave it -1, the internal random will generate one.</param>
        /// <returns>Returns IRandomSelector, underlying objects are either StaticRandomSelectorLinear or StaticRandomSelectorBinary. Both are non-mutable.</returns>
        public IRandomSelector <T> Build(int seed = -1)
        {
            T[]     items = itemBuffer.ToArray();
            float[] CDA   = weightBuffer.ToArray();

            itemBuffer.Clear();
            weightBuffer.Clear();

            RandomMath.BuildCumulativeDistribution(CDA);

            if (seed == -1)
            {
                seed = random.Next();
            }

            // RandomMath.ArrayBreakpoint decides where to use Linear or Binary search, based on internal buffer size
            // if CDA array is smaller than breakpoint, then pick linear search random selector, else pick binary search selector
            if (CDA.Length < RandomMath.ArrayBreakpoint)
            {
                return(new StaticRandomSelectorLinear <T>(items, CDA, seed));
            }
            else
            {
                // bigger array sizes need binary search for much faster lookup
                return(new StaticRandomSelectorBinary <T>(items, CDA, seed));
            }
        }
        /// <summary>
        /// Re/Builds internal CDL (Cummulative Distribution List)
        /// Must be called after modifying (calling Add or Remove), or it will break.
        /// Switches between linear or binary search, depending on which one will be faster.
        /// Might generate some garbage (list resize) on first few builds.
        /// </summary>
        /// <param name="seed">You can specify seed for internal random gen or leave it alone</param>
        /// <returns>Returns itself</returns>
        public IRandomSelector <T> Build(int seed = -1)
        {
            if (itemsList.Count == 0)
            {
                throw new Exception("Cannot build with no items.");
            }

            // clear list and then transfer weights
            CDL.Clear();
            for (int i = 0; i < weightsList.Count; i++)
            {
                CDL.Add(weightsList[i]);
            }

            RandomMath.BuildCumulativeDistribution(CDL);

            // default behavior
            // if seed wasn't specified (it is seed==-1), keep same seed - avoids garbage collection from making new random
            if (seed != -1)
            {
                // input -2 if you want to randomize seed
                if (seed == -2)
                {
                    seed   = random.Next();
                    random = new Random(seed);
                }
                else
                {
                    random = new Random(seed);
                }
            }

            // RandomMath.ListBreakpoint decides where to use Linear or Binary search, based on internal buffer size
            // if CDL list is smaller than breakpoint, then pick linear search random selector, else pick binary search selector
            if (CDL.Count < RandomMath.ListBreakpoint)
            {
                selectFunction = RandomMath.SelectIndexLinearSearch;
            }
            else
            {
                selectFunction = RandomMath.SelectIndexBinarySearch;
            }

            return(this);
        }
Example #3
0
        /// <summary>
        /// Test and compare linear and binary searches, they should return identical results
        /// </summary>
        /// <returns></returns>
        bool TestEqualityOfLinearVsBinarySearch()
        {
            var random = new System.Random();

            for (int i = 0; i < 1000000; i++)
            {
                float u = i / 999999f;
                float r = (float)random.NextDouble();

                float[] randomWeights = RandomMath.RandomWeightsArray(random, 33);

                RandomMath.BuildCumulativeDistribution(randomWeights);

                if (randomWeights.SelectIndexLinearSearch(1f) != randomWeights.SelectIndexBinarySearch(1f))
                {
                    return(false);
                }

                if (randomWeights.SelectIndexLinearSearch(u) != randomWeights.SelectIndexBinarySearch(u))
                {
                    Debug.Log("Not matching u");
                    Debug.Log(u);
                    Debug.Log(randomWeights.SelectIndexLinearSearch(u));
                    Debug.Log(randomWeights.SelectIndexBinarySearch(u));

                    return(false);
                }

                if (randomWeights.SelectIndexLinearSearch(r) != randomWeights.SelectIndexBinarySearch(r))
                {
                    Debug.Log("Not matching r");
                    Debug.Log(r);
                    Debug.Log(randomWeights.SelectIndexLinearSearch(r));
                    Debug.Log(randomWeights.SelectIndexBinarySearch(r));

                    return(false);
                }
            }

            return(true);
        }
Example #4
0
        // time both searches (linear and binary (log)), and find optimal breakpoint - where to use which for maximal performance
        int FindOptimalBreakpointArray()
        {
            int optimalBreakpoint = 2;

            var random = new System.Random();

            Stopwatch stopwatchLinear = new Stopwatch();
            Stopwatch stopwatchBinary = new Stopwatch();

            float lin = 0f;
            float log = 1f;

            // continue increasing "optimalBreakpoint" until linear becomes slower than log
            // result is around 15-16, varies a bit due to random nature of test
            while (lin <= log)
            {
                int numOfDiffArrays = 100;
                int numOfTestPerArr = 10000;

                // u = uniform grid, r = uniform random
                float u, r;
                ///Linear Search
                stopwatchLinear.Stop();
                stopwatchLinear.Reset();

                float[] items = RandomMath.IdentityArray(optimalBreakpoint);
                float   selectedItem; //here just to simulate selecting from array
                float[] arr = new float[optimalBreakpoint];

                for (int k = 0; k < numOfDiffArrays; k++)
                {
                    RandomMath.RandomWeightsArray(ref arr, random);
                    RandomMath.BuildCumulativeDistribution(arr);

                    stopwatchLinear.Start();
                    for (int i = 0; i < numOfTestPerArr; i++)
                    {
                        u            = i / (numOfTestPerArr - 1f);
                        selectedItem = items[arr.SelectIndexLinearSearch(u)];

                        r            = (float)random.NextDouble();
                        selectedItem = items[arr.SelectIndexLinearSearch(r)];
                    }

                    stopwatchLinear.Stop();
                }

                lin = stopwatchLinear.ElapsedMilliseconds;

                /// Binary Search
                stopwatchBinary.Stop();
                stopwatchBinary.Reset();

                for (int k = 0; k < numOfDiffArrays; k++)
                {
                    RandomMath.RandomWeightsArray(ref arr, random);
                    RandomMath.BuildCumulativeDistribution(arr);

                    stopwatchBinary.Start();
                    for (int i = 0; i < numOfTestPerArr; i++)
                    {
                        u            = i / (numOfTestPerArr - 1f);
                        selectedItem = items[arr.SelectIndexBinarySearch(u)];

                        r            = (float)random.NextDouble();
                        selectedItem = items[arr.SelectIndexBinarySearch(r)];
                    }
                    stopwatchBinary.Stop();
                }

                log = stopwatchBinary.ElapsedMilliseconds;

                optimalBreakpoint++;
            }

            return(optimalBreakpoint);
        }