An Extension Method on Ienumerable Needed for Shuffling

An extension method on IEnumerable needed for shuffling

You can use a Fisher-Yates-Durstenfeld shuffle. There's no need to explicitly pass a size argument to the method itself, you can simply tack on a call to Take if you don't need the entire sequence:

var shuffled = originalSequence.Shuffle().Take(5);

// ...

public static class EnumerableExtensions
{
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
{
return source.Shuffle(new Random());
}

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (rng == null) throw new ArgumentNullException(nameof(rng));

return source.ShuffleIterator(rng);
}

private static IEnumerable<T> ShuffleIterator<T>(
this IEnumerable<T> source, Random rng)
{
var buffer = source.ToList();
for (int i = 0; i < buffer.Count; i++)
{
int j = rng.Next(i, buffer.Count);
yield return buffer[j];

buffer[j] = buffer[i];
}
}
}

Random Order of an IEnumerable

I see you require lazy evaluation for the results, if that is the case, what you can do is this:

var randomNumbers = result1.Select(r => random.Next()).ToArray();
var orderedResult = result1.Zip(randomNumbers, (r, o) => new { Result = r, Order = o })
.OrderBy(o => o.Order)
.Select(o => o.Result);

By calling ToArray() on the random numbers, these will not change. When you finally desire the items in result1, you can zip the items with the random numbers, OrderBy the random number and Select the result.

As long as the items in result1 come in the same order, the result in orderedResult should be the same each time.

IEnumerable shuffling not random for set of set

The problem is that the Shuffle extension method in the linked question instantiates a new Random object each time. Since the default constructor uses Environment.TickCount to seed the random number generator and this all happens very quickly, all the lists get the same random seed.

You need to instantiate a Random instance of your own and pass it to the Shuffle overload:

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)

from this answer.

Your code, then, would be:

Random myRandom = new Random();
SetOfSets.Select(set => set.Shuffle(myRandom));

How can I randomly ordering an IEnumerable?

Enumerators allow "iterated" access only. Thus, there is no possibility to access the elements randomly or in a sorted way. You have to read the enumerated values and (temporarily) store them in another list for which you can apply your (random) sorting algorithm.

[edit] Example:

List<MyObject> list = new List<MyObject>( my_enumerable );
Random rnd = new Random(/* Eventually provide some random seed here. */);
for (int i = list.Count - 1; i > 0; --i)
{
int j = rnd.Next(i + 1);
MyObject tmp = list[i];
list[i] = list[j];
list[j] = tmp;
}
my_enumerable = list;

(I have not tested the example code.)

Optimal LINQ query to get a random sub collection - Shuffle

Another option is to use OrderBy and to sort on a GUID value, which you can do so using:

var result = sequence.OrderBy(elem => Guid.NewGuid());

I did some empirical tests to convince myself that the above actually generates a random distribution (which it appears to do). You can see my results at Techniques for Randomly Reordering an Array.

Randomize a ListT

Shuffle any (I)List with an extension method based on the Fisher-Yates shuffle:

private static Random rng = new Random();  

public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1) {
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}

Usage:

List<Product> products = GetProducts();
products.Shuffle();

The code above uses the much criticised System.Random method to select swap candidates. It's fast but not as random as it should be. If you need a better quality of randomness in your shuffles use the random number generator in System.Security.Cryptography like so:

using System.Security.Cryptography;
...
public static void Shuffle<T>(this IList<T> list)
{
RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();
int n = list.Count;
while (n > 1)
{
byte[] box = new byte[1];
do provider.GetBytes(box);
while (!(box[0] < n * (Byte.MaxValue / n)));
int k = (box[0] % n);
n--;
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}

A simple comparison is available at this blog (WayBack Machine).

Edit: Since writing this answer a couple years back, many people have commented or written to me, to point out the big silly flaw in my comparison. They are of course right. There's nothing wrong with System.Random if it's used in the way it was intended. In my first example above, I instantiate the rng variable inside of the Shuffle method, which is asking for trouble if the method is going to be called repeatedly. Below is a fixed, full example based on a really useful comment received today from @weston here on SO.

Program.cs:

using System;
using System.Collections.Generic;
using System.Threading;

namespace SimpleLottery
{
class Program
{
private static void Main(string[] args)
{
var numbers = new List<int>(Enumerable.Range(1, 75));
numbers.Shuffle();
Console.WriteLine("The winning numbers are: {0}", string.Join(", ", numbers.GetRange(0, 5)));
}
}

public static class ThreadSafeRandom
{
[ThreadStatic] private static Random Local;

public static Random ThisThreadsRandom
{
get { return Local ?? (Local = new Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId))); }
}
}

static class MyExtensions
{
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = ThreadSafeRandom.ThisThreadsRandom.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
}

How to implement lazy shuffling of Lists in C#?

Since the original list can be modified, here is a very simple and efficient solution, based on this answer:

public static IEnumerable<T> Shuffle<T>(this IList<T> list, Random rng)
{
for(int i = list.Count - 1; i >= 0; i--)
{
int swapIndex = rng.Next(i + 1);
yield return list[swapIndex];
list[swapIndex] = list[i];
}
}

Extension Method not Returning Correct Collection

sourceList is actually a local variable.
Might be better to return shuffedList;

var newList = caller.GroupRandomize<T>(5) ;

Collection Randomization using Extension Method

There are a few issues I would have with this method:

  • It should check for a null argument.
  • It should not check for a 0-length list.
  • Avoid side-effects. Create a new list for the shuffled elements, instead of modifying the existing one.
  • Don't hide dependencies. Pass the random number generator in as an argument.
  • Use a more descriptive name than 'RandomList'.
  • The input type can be generalized to IEnumerable.
  • The method can be changed to an enumerator [generalize the output type].

Essentially:

public static IList<T> Shuffled<T>(this IEnumerable<T> source, Random generator)
{
if (source == null) throw new ArgumentNullException("source");
if (generator == null) throw new ArgumentNullException("generator");

//copy
var result = source.ToList();
//shuffle the copy
for (int i = result.Count - 1; i > 0; i--)
{
int RandomIndex = generator.Next(i + 1);
T temp = result[i];
result[i] = result[RandomIndex];
result[RandomIndex] = temp;
}

return result;
}

I didn't generalize the output type. You can do that if you want.



Related Topics



Leave a reply



Submit