DEV Community

mohamed Tayel
mohamed Tayel

Posted on

Article 2: Building a Reusable Shuffler for Randomizing Data

Introduction

Randomizing data is a common requirement in many applications, whether for games, simulations, or team assignments. In this article, we’ll build a reusable Shuffler class using the Fisher-Yates Shuffle algorithm. This class will efficiently randomize a sequence of items and ensure isolation between iterations.

By the end of this article, you’ll:

  1. Understand the Fisher-Yates Shuffle algorithm.
  2. Learn how to encapsulate randomization logic in a reusable class.
  3. See how to use the Shuffler in a real-world scenario.

Key Concepts

  1. Efficient Randomization:

    • The Fisher-Yates Shuffle ensures uniform randomization of items in O(n) time complexity.
  2. Encapsulation:

    • The randomization logic will be encapsulated in the Shuffler class, making it reusable and modular.
  3. Isolation:

    • We’ll use the IEnumerator<T> interface to ensure independent iterations, avoiding conflicts when multiple shuffles occur simultaneously.

Step-by-Step Implementation

Step 1: Create the Shuffler Class

The Shuffler class will:

  • Accept an input sequence.
  • Randomize it in-place using an array for efficient swaps.
  • Implement the IEnumerator<T> interface to enable controlled iteration.
using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace Utilities { public class Shuffler<T> : IEnumerator<T> { private readonly T[] _data; private readonly Random _random; private int _position = -1; public Shuffler(IEnumerable<T> inputData) { // Convert input data to an array for in-place swaps _data = inputData.ToArray(); _random = new Random(); } public T Current => _data[_position]; object IEnumerator.Current => Current; public bool MoveNext() { if (_position >= _data.Length - 1) return false; _position++; // Randomly pick an index from the remaining elements int swapIndex = _random.Next(_position, _data.Length); // Swap the current element with the randomly chosen one (_data[_position], _data[swapIndex]) = (_data[swapIndex], _data[_position]); return true; } public void Reset() => _position = -1; public void Dispose() { /* No resources to release */ } } } 
Enter fullscreen mode Exit fullscreen mode

Step 2: Add an Extension Method for Easy Usage

To make the Shuffler easier to use, we’ll create an extension method that allows you to shuffle any IEnumerable<T>.

namespace Utilities { public static class EnumerableExtensions { public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source) { using var shuffler = new Shuffler<T>(source); while (shuffler.MoveNext()) { yield return shuffler.Current; } } } } 
Enter fullscreen mode Exit fullscreen mode

Step 3: Use the Shuffler in a Program

Here’s how to use the Shuffler to randomize a list of items.

using System; using System.Collections.Generic; using Utilities; class Program { static void Main(string[] args) { // Step 1: Input data var data = new List<string> { "Alice", "Bob", "Charlie", "Diana", "Eve" }; Console.WriteLine("Original Data:"); Console.WriteLine(string.Join(", ", data)); // Step 2: Shuffle the data var shuffledData = data.Shuffle(); Console.WriteLine("\nShuffled Data:"); Console.WriteLine(string.Join(", ", shuffledData)); } } 
Enter fullscreen mode Exit fullscreen mode

Step 4: Run the Program

Input:

Data: Alice, Bob, Charlie, Diana, Eve 
Enter fullscreen mode Exit fullscreen mode

Output:

Original Data: Alice, Bob, Charlie, Diana, Eve Shuffled Data: Diana, Charlie, Eve, Bob, Alice 
Enter fullscreen mode Exit fullscreen mode

How It Works

  1. Array for Efficient Swaps:

    • The input sequence is converted to an array to allow in-place swaps during randomization.
  2. Fisher-Yates Shuffle:

    • In each iteration, a random element from the remaining unshuffled portion is swapped with the current element.
  3. Extension Method:

    • The Shuffle extension method simplifies usage, allowing any IEnumerable<T> to be shuffled with minimal code.

Takeaways

  1. Efficiency:

    • The Fisher-Yates Shuffle is highly efficient, with O(n) time complexity.
  2. Reusability:

    • The Shuffler class can be reused across projects for any type of data.
  3. Simplicity:

    • The extension method makes shuffling intuitive and easy to integrate.

Next Steps

In the next article, we’ll combine the Shuffler and GridFormatter to create a Team Assignment Application. You’ll see how these utilities can work together to solve real-world problems.

Stay tuned for Article 3: Building a Team Assignment Application with Grid Formatting and Shuffling! 🚀

Top comments (0)