diff --git a/Algorithms.Tests/Shufflers/RecursiveShufflerTests.cs b/Algorithms.Tests/Shufflers/RecursiveShufflerTests.cs new file mode 100644 index 00000000..0559e401 --- /dev/null +++ b/Algorithms.Tests/Shufflers/RecursiveShufflerTests.cs @@ -0,0 +1,60 @@ +using Algorithms.Shufflers; +using Algorithms.Tests.Helpers; +using FluentAssertions; +using NUnit.Framework; +using System; + +namespace Algorithms.Tests.Shufflers +{ + public static class RecursiveShufflerTests + { + [Test] + public static void ArrayShuffled_NewArraySameSize( + [Random(10, 1000, 100, Distinct = true)] + int n) + { + // Arrange + var shuffler = new RecursiveShuffler(); + var (correctArray, testArray) = RandomHelper.GetArrays(n); + + // Act + shuffler.Shuffle(testArray); + + // Assert + testArray.Length.Should().Be(correctArray.Length); + } + + [Test] + public static void ArrayShuffled_NewArraySameValues( + [Random(10, 1000, 100, Distinct = true)] + int n) + { + // Arrange + var shuffler = new RecursiveShuffler(); + var (correctArray, testArray) = RandomHelper.GetArrays(n); + + // Act + shuffler.Shuffle(testArray); + + // Assert + testArray.Should().BeEquivalentTo(correctArray); + } + + [Test] + public static void ArrayShuffled_NewArraySameShuffle( + [Random(0, 1000, 2, Distinct = true)] int n, + [Random(1000, 10000, 5, Distinct = true)] int seed) + { + // Arrange + var shuffler = new RecursiveShuffler(); + var (correctArray, testArray) = RandomHelper.GetArrays(n); + + // Act + shuffler.Shuffle(testArray, seed); + shuffler.Shuffle(correctArray, seed); + + // Assert + correctArray.Should().BeEquivalentTo(testArray, options => options.WithStrictOrdering()); + } + } +} diff --git a/Algorithms/Shufflers/RecursiveShuffler.cs b/Algorithms/Shufflers/RecursiveShuffler.cs new file mode 100644 index 00000000..4a367e1b --- /dev/null +++ b/Algorithms/Shufflers/RecursiveShuffler.cs @@ -0,0 +1,46 @@ +using System; + +namespace Algorithms.Shufflers +{ + /// + /// Recursive Shuffler is a recursive version of + /// Fisher-Yates shuffle algorithm. This can only be used + /// for educational purposes due to stack depth limits. + /// + /// Type array input. + public class RecursiveShuffler : IShuffler + { + /// + /// This is the public overload method that calls the private overload method. + /// + /// Array to shuffle. + /// Random generator seed. Used to repeat the shuffle. + public void Shuffle(T[] array, int? seed = null) + { + Shuffle(array, array.Length - 1, seed); + } + + /// + /// First, it will check the length of the array on the base case. + /// Next, if there's still items left, it will shuffle the sub-array. + /// Lastly, it will randomly select index from 0 to number of items of the array + /// then swap the elements array[items] and array[index]. + /// + /// Array to shuffle. + /// Number of items in the array. + /// Random generator seed. Used to repeat the shuffle. + private void Shuffle(T[] array, int items, int? seed) + { + if (items <= 0) + { + return; + } + + Shuffle(array, items - 1, seed); + var random = seed is null ? new Random() : new Random(seed.Value); + int index = random.Next(items + 1); + (array[items], array[index]) = (array[index], array[items]); + (array[items], array[index]) = (array[index], array[items]); + } + } +} diff --git a/README.md b/README.md index bd8490d3..ff928316 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,7 @@ find more than one implementation for the same objective but using different alg * [Fisher-Yates Shuffler](./Algorithms/Shufflers/FisherYatesShuffler.cs) * [LINQ Shuffler](./Algorithms/Shufflers/LinqShuffler.cs) * [Naive Shuffler](./Algorithms/Shufflers/NaiveShuffler.cs) + * [Recursive Shuffler](./Algorithms/Shufflers/RecursiveShuffler.cs) * [Sequences](./Algorithms/Sequences) * [A000002 Kolakoski](./Algorithms/Sequences/KolakoskiSequence.cs) * [A000004 Zero](./Algorithms/Sequences/ZeroSequence.cs)