DEV Community

mohamed Tayel
mohamed Tayel

Posted on

1

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! 🚀

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

AWS Security LIVE!

Hosted by security experts, AWS Security LIVE! showcases AWS Partners tackling real-world security challenges. Join live and get your security questions answered.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️