public void TestActuallyFaster()
        {
            int iterations = 100;
            int nSize = 10000;
            long lFastArrayTicks, lYatesShufflerTicks, lDurstenfeldShufflerTicks;
            Stopwatch stopwatch = new Stopwatch();

            // Time FastShuffleArray
            stopwatch.Start();
            FastShuffleArray fast_array;
            for (int i = 0; i < iterations; i++)
            {
                fast_array = new FastShuffleArray(nSize);
            }
            stopwatch.Stop();
            lFastArrayTicks = stopwatch.ElapsedTicks;

            // Time Fisher-Yates
            stopwatch.Restart();
            for (int i=0; i < iterations; i++)
            {
                int[] arry = new int[nSize];
                for (int j = 0; j < nSize; j++)
                {
                    arry[j] = j + 1;
                }

                Shuffler.FisherYatesShuffle<int>(arry);
            }
            stopwatch.Stop();
            lYatesShufflerTicks = stopwatch.ElapsedTicks;

            // Time Durstenfeld.
            stopwatch.Restart();
            for (int i = 0; i < iterations; i++)
            {
                int[] arry = Shuffler.DurstenfeldShuffle<int>(nSize, IntGenerator.LinearSequence());
            }
            stopwatch.Stop();
            lDurstenfeldShufflerTicks = stopwatch.ElapsedTicks;

            double dAdvantageOverYates = ((double) lYatesShufflerTicks - lFastArrayTicks) / lFastArrayTicks;
            double dAdvantageOverDurstenfeld = ((double) lDurstenfeldShufflerTicks - lFastArrayTicks) / lFastArrayTicks;

            // This is another oddity a performance unit test adds to the system: it really should emit its data so that
            // actual performance over time can be calibrated.
            Console.WriteLine("Adv. over Fisher-Yates = [{0}]", dAdvantageOverYates.ToString("#0.##%"));
            Console.WriteLine("Adv. over Durstenfeld = [{0}]", dAdvantageOverDurstenfeld.ToString("#0.##%"));

            // Assert performance criteria.
            Assert.IsTrue(dAdvantageOverYates > 0.005,
                "Not faster then Yates by enough.  FastArray is only {0} faster.",
                dAdvantageOverYates.ToString("#0.##%"));

            Assert.IsTrue(dAdvantageOverDurstenfeld > 0.01,
                "Not faster then Durstenfeld by enough.  FastArray is only {0} faster.",
                 dAdvantageOverDurstenfeld.ToString("#0.##%"));
        }
        public unsafe void TestFastShufflerCorrectness()
        {
            int size = 10000;
            FastShuffleArray fast_array = new FastShuffleArray(size);
            fast_array.Sort();
            int* pFastArray = fast_array.Pointer;

            for (int i = 0; i < size; i++)
            {
                Assert.AreEqual(i + 1, pFastArray[i]);
            }
        }
        public unsafe void TestFastShufflerRandomness()
        {
            IDictionary<string, FirstOrderStatistic> distribution = new Dictionary<string, FirstOrderStatistic>();

            // You need to tune this parameter based on your particular environment, your randomness specifications
            // and the number of unit tests you're writing.
            // More samples = fewer false negatives but takes longer to calculate.
            int samples = 100000;
            int items = 5;

            int distributions = Statistics.factorial(items);
            int expected_distribution_count = samples / distributions;

            for (int i = 0; i < samples; i++)
            {
                FastShuffleArray fast_array = new FastShuffleArray(items);
                int* pFastArray = fast_array.Pointer;

                // This section creates a unique signature for a given distribution and then
                // increments the count for the distribution.

                // Casting it to a string is a little ugly from an efficiency perspective
                // but it's simple, correct and clear.

                // The string format is hardcoded to minimise the efficiency penalty.
                string distribution_id = string.Format("{0}|{1}|{2}|{3}|{4}",
                    pFastArray[0], pFastArray[1], pFastArray[2], pFastArray[3], pFastArray[4]);

                FirstOrderStatistic stats;
                distribution.TryGetValue(distribution_id, out stats);
                if (null == stats)
                {
                    stats = new FirstOrderStatistic();
                }
                stats.Count = stats.Count + 1;
                distribution[distribution_id] = stats;
            }

            // Check that the distribution is roughly equal by confirming that the distribution of results
            // conforms to a normal distribution.

            // First, we need to create a set of second order statistics.
            // We do it here as a second pass to save having to do intermediate calculations that are irrelevant.
            foreach (KeyValuePair<string, FirstOrderStatistic> kvp in distribution)
            {
                kvp.Value.Deviation = kvp.Value.Count - expected_distribution_count;
                kvp.Value.SquaredDeviation = Math.Pow(kvp.Value.Deviation, 2);
            }

            // Now, finally, we need one more pass to consolidate all of that information and actually check that the distribution is normal.
            double dMeanSs = 0.0;
            foreach (KeyValuePair<string, FirstOrderStatistic> kvp in distribution)
            {
                dMeanSs += kvp.Value.SquaredDeviation;
            }

            double dMeanVar = dMeanSs / samples;
            double dStdErr = Math.Sqrt(dMeanVar);

            // This is basically a derived tolerance.  If you were specifying something be random in the
            // real world, just how random it needed to be would be part of the specification.
            Assert.IsTrue(dStdErr < 1.5);
        }
Example #4
0
 /// <summary>
 /// Emits a shuffled array of nCount elements to stdout.
 /// </summary>
 /// <param name="nCount">Number of elements to shuffle and emit.</param>
 static unsafe void EmitShuffle(int nCount)
 {
     FastShuffleArray out_array = new FastShuffleArray(nCount);
     for (int i = 0; i < out_array.Size; i++)
         Console.WriteLine(out_array.Pointer[i]);
 }