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