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)